fscache, cachefiles: Add alternate API to use kiocb for read/write to cache
Add an alternate API by which the cache can be accessed through a kiocb, doing async DIO, rather than using the current API that tells the cache where all the pages are. The new API is intended to be used in conjunction with the netfs helper library. A filesystem must pick one or the other and not mix them. Filesystems wanting to use the new API must #define FSCACHE_USE_NEW_IO_API before #including the header. This prevents them from continuing to use the old API at the same time as there are incompatibilities in how the PG_fscache page bit is used. Changes: v6: - Provide a routine to shape a write so that the start and length can be aligned for DIO[3]. v4: - Use the vfs_iocb_iter_read/write() helpers[1] - Move initial definition of fscache_begin_read_operation() here. - Remove a commented-out line[2] - Combine ki->term_func calls in cachefiles_read_complete()[2]. - Remove explicit NULL initialiser[2]. - Remove extern on func decl[2]. - Put in param names on func decl[2]. - Remove redundant else[2]. - Fill out the kdoc comment for fscache_begin_read_operation(). - Rename fs/fscache/page2.c to io.c to match later patches. Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-and-tested-by: Jeff Layton <jlayton@kernel.org> Tested-by: Dave Wysochanski <dwysocha@redhat.com> Tested-By: Marc Dionne <marc.dionne@auristor.com> cc: Christoph Hellwig <hch@lst.de> cc: linux-cachefs@redhat.com cc: linux-afs@lists.infradead.org cc: linux-nfs@vger.kernel.org cc: linux-cifs@vger.kernel.org cc: ceph-devel@vger.kernel.org cc: v9fs-developer@lists.sourceforge.net cc: linux-fsdevel@vger.kernel.org Link: https://lore.kernel.org/r/20210216102614.GA27555@lst.de/ [1] Link: https://lore.kernel.org/r/20210216084230.GA23669@lst.de/ [2] Link: https://lore.kernel.org/r/161781047695.463527.7463536103593997492.stgit@warthog.procyon.org.uk/ [3] Link: https://lore.kernel.org/r/161118142558.1232039.17993829899588971439.stgit@warthog.procyon.org.uk/ # rfc Link: https://lore.kernel.org/r/161161037850.2537118.8819808229350326503.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/161340402057.1303470.8038373593844486698.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/161539545919.286939.14573472672781434757.stgit@warthog.procyon.org.uk/ # v4 Link: https://lore.kernel.org/r/161653801477.2770958.10543270629064934227.stgit@warthog.procyon.org.uk/ # v5 Link: https://lore.kernel.org/r/161789084517.6155.12799689829859169640.stgit@warthog.procyon.org.uk/ # v6
This commit is contained in:
parent
0246f3e573
commit
26aaeffcaf
|
@ -7,6 +7,7 @@ cachefiles-y := \
|
||||||
bind.o \
|
bind.o \
|
||||||
daemon.o \
|
daemon.o \
|
||||||
interface.o \
|
interface.o \
|
||||||
|
io.o \
|
||||||
key.o \
|
key.o \
|
||||||
main.o \
|
main.o \
|
||||||
namei.o \
|
namei.o \
|
||||||
|
|
|
@ -319,7 +319,7 @@ static void cachefiles_drop_object(struct fscache_object *_object)
|
||||||
/*
|
/*
|
||||||
* dispose of a reference to an object
|
* dispose of a reference to an object
|
||||||
*/
|
*/
|
||||||
static void cachefiles_put_object(struct fscache_object *_object,
|
void cachefiles_put_object(struct fscache_object *_object,
|
||||||
enum fscache_obj_ref_trace why)
|
enum fscache_obj_ref_trace why)
|
||||||
{
|
{
|
||||||
struct cachefiles_object *object;
|
struct cachefiles_object *object;
|
||||||
|
@ -568,4 +568,5 @@ const struct fscache_cache_ops cachefiles_cache_ops = {
|
||||||
.uncache_page = cachefiles_uncache_page,
|
.uncache_page = cachefiles_uncache_page,
|
||||||
.dissociate_pages = cachefiles_dissociate_pages,
|
.dissociate_pages = cachefiles_dissociate_pages,
|
||||||
.check_consistency = cachefiles_check_consistency,
|
.check_consistency = cachefiles_check_consistency,
|
||||||
|
.begin_read_operation = cachefiles_begin_read_operation,
|
||||||
};
|
};
|
||||||
|
|
|
@ -150,6 +150,9 @@ extern int cachefiles_has_space(struct cachefiles_cache *cache,
|
||||||
*/
|
*/
|
||||||
extern const struct fscache_cache_ops cachefiles_cache_ops;
|
extern const struct fscache_cache_ops cachefiles_cache_ops;
|
||||||
|
|
||||||
|
void cachefiles_put_object(struct fscache_object *_object,
|
||||||
|
enum fscache_obj_ref_trace why);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* key.c
|
* key.c
|
||||||
*/
|
*/
|
||||||
|
@ -217,6 +220,12 @@ extern int cachefiles_allocate_pages(struct fscache_retrieval *,
|
||||||
extern int cachefiles_write_page(struct fscache_storage *, struct page *);
|
extern int cachefiles_write_page(struct fscache_storage *, struct page *);
|
||||||
extern void cachefiles_uncache_page(struct fscache_object *, struct page *);
|
extern void cachefiles_uncache_page(struct fscache_object *, struct page *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rdwr2.c
|
||||||
|
*/
|
||||||
|
extern int cachefiles_begin_read_operation(struct netfs_read_request *,
|
||||||
|
struct fscache_retrieval *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* security.c
|
* security.c
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,420 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/* kiocb-using read/write
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
|
||||||
|
* Written by David Howells (dhowells@redhat.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
#include <linux/sched/mm.h>
|
||||||
|
#include <linux/netfs.h>
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
struct cachefiles_kiocb {
|
||||||
|
struct kiocb iocb;
|
||||||
|
refcount_t ki_refcnt;
|
||||||
|
loff_t start;
|
||||||
|
union {
|
||||||
|
size_t skipped;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
netfs_io_terminated_t term_func;
|
||||||
|
void *term_func_priv;
|
||||||
|
bool was_async;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void cachefiles_put_kiocb(struct cachefiles_kiocb *ki)
|
||||||
|
{
|
||||||
|
if (refcount_dec_and_test(&ki->ki_refcnt)) {
|
||||||
|
fput(ki->iocb.ki_filp);
|
||||||
|
kfree(ki);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle completion of a read from the cache.
|
||||||
|
*/
|
||||||
|
static void cachefiles_read_complete(struct kiocb *iocb, long ret, long ret2)
|
||||||
|
{
|
||||||
|
struct cachefiles_kiocb *ki = container_of(iocb, struct cachefiles_kiocb, iocb);
|
||||||
|
|
||||||
|
_enter("%ld,%ld", ret, ret2);
|
||||||
|
|
||||||
|
if (ki->term_func) {
|
||||||
|
if (ret >= 0)
|
||||||
|
ret += ki->skipped;
|
||||||
|
ki->term_func(ki->term_func_priv, ret, ki->was_async);
|
||||||
|
}
|
||||||
|
|
||||||
|
cachefiles_put_kiocb(ki);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initiate a read from the cache.
|
||||||
|
*/
|
||||||
|
static int cachefiles_read(struct netfs_cache_resources *cres,
|
||||||
|
loff_t start_pos,
|
||||||
|
struct iov_iter *iter,
|
||||||
|
bool seek_data,
|
||||||
|
netfs_io_terminated_t term_func,
|
||||||
|
void *term_func_priv)
|
||||||
|
{
|
||||||
|
struct cachefiles_kiocb *ki;
|
||||||
|
struct file *file = cres->cache_priv2;
|
||||||
|
unsigned int old_nofs;
|
||||||
|
ssize_t ret = -ENOBUFS;
|
||||||
|
size_t len = iov_iter_count(iter), skipped = 0;
|
||||||
|
|
||||||
|
_enter("%pD,%li,%llx,%zx/%llx",
|
||||||
|
file, file_inode(file)->i_ino, start_pos, len,
|
||||||
|
i_size_read(file->f_inode));
|
||||||
|
|
||||||
|
/* If the caller asked us to seek for data before doing the read, then
|
||||||
|
* we should do that now. If we find a gap, we fill it with zeros.
|
||||||
|
*/
|
||||||
|
if (seek_data) {
|
||||||
|
loff_t off = start_pos, off2;
|
||||||
|
|
||||||
|
off2 = vfs_llseek(file, off, SEEK_DATA);
|
||||||
|
if (off2 < 0 && off2 >= (loff_t)-MAX_ERRNO && off2 != -ENXIO) {
|
||||||
|
skipped = 0;
|
||||||
|
ret = off2;
|
||||||
|
goto presubmission_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (off2 == -ENXIO || off2 >= start_pos + len) {
|
||||||
|
/* The region is beyond the EOF or there's no more data
|
||||||
|
* in the region, so clear the rest of the buffer and
|
||||||
|
* return success.
|
||||||
|
*/
|
||||||
|
iov_iter_zero(len, iter);
|
||||||
|
skipped = len;
|
||||||
|
ret = 0;
|
||||||
|
goto presubmission_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipped = off2 - off;
|
||||||
|
iov_iter_zero(skipped, iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = -ENOBUFS;
|
||||||
|
ki = kzalloc(sizeof(struct cachefiles_kiocb), GFP_KERNEL);
|
||||||
|
if (!ki)
|
||||||
|
goto presubmission_error;
|
||||||
|
|
||||||
|
refcount_set(&ki->ki_refcnt, 2);
|
||||||
|
ki->iocb.ki_filp = file;
|
||||||
|
ki->iocb.ki_pos = start_pos + skipped;
|
||||||
|
ki->iocb.ki_flags = IOCB_DIRECT;
|
||||||
|
ki->iocb.ki_hint = ki_hint_validate(file_write_hint(file));
|
||||||
|
ki->iocb.ki_ioprio = get_current_ioprio();
|
||||||
|
ki->skipped = skipped;
|
||||||
|
ki->term_func = term_func;
|
||||||
|
ki->term_func_priv = term_func_priv;
|
||||||
|
ki->was_async = true;
|
||||||
|
|
||||||
|
if (ki->term_func)
|
||||||
|
ki->iocb.ki_complete = cachefiles_read_complete;
|
||||||
|
|
||||||
|
get_file(ki->iocb.ki_filp);
|
||||||
|
|
||||||
|
old_nofs = memalloc_nofs_save();
|
||||||
|
ret = vfs_iocb_iter_read(file, &ki->iocb, iter);
|
||||||
|
memalloc_nofs_restore(old_nofs);
|
||||||
|
switch (ret) {
|
||||||
|
case -EIOCBQUEUED:
|
||||||
|
goto in_progress;
|
||||||
|
|
||||||
|
case -ERESTARTSYS:
|
||||||
|
case -ERESTARTNOINTR:
|
||||||
|
case -ERESTARTNOHAND:
|
||||||
|
case -ERESTART_RESTARTBLOCK:
|
||||||
|
/* There's no easy way to restart the syscall since other AIO's
|
||||||
|
* may be already running. Just fail this IO with EINTR.
|
||||||
|
*/
|
||||||
|
ret = -EINTR;
|
||||||
|
fallthrough;
|
||||||
|
default:
|
||||||
|
ki->was_async = false;
|
||||||
|
cachefiles_read_complete(&ki->iocb, ret, 0);
|
||||||
|
if (ret > 0)
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
in_progress:
|
||||||
|
cachefiles_put_kiocb(ki);
|
||||||
|
_leave(" = %zd", ret);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
presubmission_error:
|
||||||
|
if (term_func)
|
||||||
|
term_func(term_func_priv, ret < 0 ? ret : skipped, false);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle completion of a write to the cache.
|
||||||
|
*/
|
||||||
|
static void cachefiles_write_complete(struct kiocb *iocb, long ret, long ret2)
|
||||||
|
{
|
||||||
|
struct cachefiles_kiocb *ki = container_of(iocb, struct cachefiles_kiocb, iocb);
|
||||||
|
struct inode *inode = file_inode(ki->iocb.ki_filp);
|
||||||
|
|
||||||
|
_enter("%ld,%ld", ret, ret2);
|
||||||
|
|
||||||
|
/* Tell lockdep we inherited freeze protection from submission thread */
|
||||||
|
__sb_writers_acquired(inode->i_sb, SB_FREEZE_WRITE);
|
||||||
|
__sb_end_write(inode->i_sb, SB_FREEZE_WRITE);
|
||||||
|
|
||||||
|
if (ki->term_func)
|
||||||
|
ki->term_func(ki->term_func_priv, ret, ki->was_async);
|
||||||
|
|
||||||
|
cachefiles_put_kiocb(ki);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initiate a write to the cache.
|
||||||
|
*/
|
||||||
|
static int cachefiles_write(struct netfs_cache_resources *cres,
|
||||||
|
loff_t start_pos,
|
||||||
|
struct iov_iter *iter,
|
||||||
|
netfs_io_terminated_t term_func,
|
||||||
|
void *term_func_priv)
|
||||||
|
{
|
||||||
|
struct cachefiles_kiocb *ki;
|
||||||
|
struct inode *inode;
|
||||||
|
struct file *file = cres->cache_priv2;
|
||||||
|
unsigned int old_nofs;
|
||||||
|
ssize_t ret = -ENOBUFS;
|
||||||
|
size_t len = iov_iter_count(iter);
|
||||||
|
|
||||||
|
_enter("%pD,%li,%llx,%zx/%llx",
|
||||||
|
file, file_inode(file)->i_ino, start_pos, len,
|
||||||
|
i_size_read(file->f_inode));
|
||||||
|
|
||||||
|
ki = kzalloc(sizeof(struct cachefiles_kiocb), GFP_KERNEL);
|
||||||
|
if (!ki)
|
||||||
|
goto presubmission_error;
|
||||||
|
|
||||||
|
refcount_set(&ki->ki_refcnt, 2);
|
||||||
|
ki->iocb.ki_filp = file;
|
||||||
|
ki->iocb.ki_pos = start_pos;
|
||||||
|
ki->iocb.ki_flags = IOCB_DIRECT | IOCB_WRITE;
|
||||||
|
ki->iocb.ki_hint = ki_hint_validate(file_write_hint(file));
|
||||||
|
ki->iocb.ki_ioprio = get_current_ioprio();
|
||||||
|
ki->start = start_pos;
|
||||||
|
ki->len = len;
|
||||||
|
ki->term_func = term_func;
|
||||||
|
ki->term_func_priv = term_func_priv;
|
||||||
|
ki->was_async = true;
|
||||||
|
|
||||||
|
if (ki->term_func)
|
||||||
|
ki->iocb.ki_complete = cachefiles_write_complete;
|
||||||
|
|
||||||
|
/* Open-code file_start_write here to grab freeze protection, which
|
||||||
|
* will be released by another thread in aio_complete_rw(). Fool
|
||||||
|
* lockdep by telling it the lock got released so that it doesn't
|
||||||
|
* complain about the held lock when we return to userspace.
|
||||||
|
*/
|
||||||
|
inode = file_inode(file);
|
||||||
|
__sb_start_write(inode->i_sb, SB_FREEZE_WRITE);
|
||||||
|
__sb_writers_release(inode->i_sb, SB_FREEZE_WRITE);
|
||||||
|
|
||||||
|
get_file(ki->iocb.ki_filp);
|
||||||
|
|
||||||
|
old_nofs = memalloc_nofs_save();
|
||||||
|
ret = vfs_iocb_iter_write(file, &ki->iocb, iter);
|
||||||
|
memalloc_nofs_restore(old_nofs);
|
||||||
|
switch (ret) {
|
||||||
|
case -EIOCBQUEUED:
|
||||||
|
goto in_progress;
|
||||||
|
|
||||||
|
case -ERESTARTSYS:
|
||||||
|
case -ERESTARTNOINTR:
|
||||||
|
case -ERESTARTNOHAND:
|
||||||
|
case -ERESTART_RESTARTBLOCK:
|
||||||
|
/* There's no easy way to restart the syscall since other AIO's
|
||||||
|
* may be already running. Just fail this IO with EINTR.
|
||||||
|
*/
|
||||||
|
ret = -EINTR;
|
||||||
|
fallthrough;
|
||||||
|
default:
|
||||||
|
ki->was_async = false;
|
||||||
|
cachefiles_write_complete(&ki->iocb, ret, 0);
|
||||||
|
if (ret > 0)
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
in_progress:
|
||||||
|
cachefiles_put_kiocb(ki);
|
||||||
|
_leave(" = %zd", ret);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
presubmission_error:
|
||||||
|
if (term_func)
|
||||||
|
term_func(term_func_priv, -ENOMEM, false);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare a read operation, shortening it to a cached/uncached
|
||||||
|
* boundary as appropriate.
|
||||||
|
*/
|
||||||
|
static enum netfs_read_source cachefiles_prepare_read(struct netfs_read_subrequest *subreq,
|
||||||
|
loff_t i_size)
|
||||||
|
{
|
||||||
|
struct fscache_retrieval *op = subreq->rreq->cache_resources.cache_priv;
|
||||||
|
struct cachefiles_object *object;
|
||||||
|
struct cachefiles_cache *cache;
|
||||||
|
const struct cred *saved_cred;
|
||||||
|
struct file *file = subreq->rreq->cache_resources.cache_priv2;
|
||||||
|
loff_t off, to;
|
||||||
|
|
||||||
|
_enter("%zx @%llx/%llx", subreq->len, subreq->start, i_size);
|
||||||
|
|
||||||
|
object = container_of(op->op.object,
|
||||||
|
struct cachefiles_object, fscache);
|
||||||
|
cache = container_of(object->fscache.cache,
|
||||||
|
struct cachefiles_cache, cache);
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
goto cache_fail_nosec;
|
||||||
|
|
||||||
|
if (subreq->start >= i_size)
|
||||||
|
return NETFS_FILL_WITH_ZEROES;
|
||||||
|
|
||||||
|
cachefiles_begin_secure(cache, &saved_cred);
|
||||||
|
|
||||||
|
off = vfs_llseek(file, subreq->start, SEEK_DATA);
|
||||||
|
if (off < 0 && off >= (loff_t)-MAX_ERRNO) {
|
||||||
|
if (off == (loff_t)-ENXIO)
|
||||||
|
goto download_and_store;
|
||||||
|
goto cache_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (off >= subreq->start + subreq->len)
|
||||||
|
goto download_and_store;
|
||||||
|
|
||||||
|
if (off > subreq->start) {
|
||||||
|
off = round_up(off, cache->bsize);
|
||||||
|
subreq->len = off - subreq->start;
|
||||||
|
goto download_and_store;
|
||||||
|
}
|
||||||
|
|
||||||
|
to = vfs_llseek(file, subreq->start, SEEK_HOLE);
|
||||||
|
if (to < 0 && to >= (loff_t)-MAX_ERRNO)
|
||||||
|
goto cache_fail;
|
||||||
|
|
||||||
|
if (to < subreq->start + subreq->len) {
|
||||||
|
if (subreq->start + subreq->len >= i_size)
|
||||||
|
to = round_up(to, cache->bsize);
|
||||||
|
else
|
||||||
|
to = round_down(to, cache->bsize);
|
||||||
|
subreq->len = to - subreq->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
cachefiles_end_secure(cache, saved_cred);
|
||||||
|
return NETFS_READ_FROM_CACHE;
|
||||||
|
|
||||||
|
download_and_store:
|
||||||
|
if (cachefiles_has_space(cache, 0, (subreq->len + PAGE_SIZE - 1) / PAGE_SIZE) == 0)
|
||||||
|
__set_bit(NETFS_SREQ_WRITE_TO_CACHE, &subreq->flags);
|
||||||
|
cache_fail:
|
||||||
|
cachefiles_end_secure(cache, saved_cred);
|
||||||
|
cache_fail_nosec:
|
||||||
|
return NETFS_DOWNLOAD_FROM_SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare for a write to occur.
|
||||||
|
*/
|
||||||
|
static int cachefiles_prepare_write(struct netfs_cache_resources *cres,
|
||||||
|
loff_t *_start, size_t *_len, loff_t i_size)
|
||||||
|
{
|
||||||
|
loff_t start = *_start;
|
||||||
|
size_t len = *_len, down;
|
||||||
|
|
||||||
|
/* Round to DIO size */
|
||||||
|
down = start - round_down(start, PAGE_SIZE);
|
||||||
|
*_start = start - down;
|
||||||
|
*_len = round_up(down + len, PAGE_SIZE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up an operation.
|
||||||
|
*/
|
||||||
|
static void cachefiles_end_operation(struct netfs_cache_resources *cres)
|
||||||
|
{
|
||||||
|
struct fscache_retrieval *op = cres->cache_priv;
|
||||||
|
struct file *file = cres->cache_priv2;
|
||||||
|
|
||||||
|
_enter("");
|
||||||
|
|
||||||
|
if (file)
|
||||||
|
fput(file);
|
||||||
|
if (op) {
|
||||||
|
fscache_op_complete(&op->op, false);
|
||||||
|
fscache_put_retrieval(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
_leave("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct netfs_cache_ops cachefiles_netfs_cache_ops = {
|
||||||
|
.end_operation = cachefiles_end_operation,
|
||||||
|
.read = cachefiles_read,
|
||||||
|
.write = cachefiles_write,
|
||||||
|
.prepare_read = cachefiles_prepare_read,
|
||||||
|
.prepare_write = cachefiles_prepare_write,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the cache file when beginning a cache operation.
|
||||||
|
*/
|
||||||
|
int cachefiles_begin_read_operation(struct netfs_read_request *rreq,
|
||||||
|
struct fscache_retrieval *op)
|
||||||
|
{
|
||||||
|
struct cachefiles_object *object;
|
||||||
|
struct cachefiles_cache *cache;
|
||||||
|
struct path path;
|
||||||
|
struct file *file;
|
||||||
|
|
||||||
|
_enter("");
|
||||||
|
|
||||||
|
object = container_of(op->op.object,
|
||||||
|
struct cachefiles_object, fscache);
|
||||||
|
cache = container_of(object->fscache.cache,
|
||||||
|
struct cachefiles_cache, cache);
|
||||||
|
|
||||||
|
path.mnt = cache->mnt;
|
||||||
|
path.dentry = object->backer;
|
||||||
|
file = open_with_fake_path(&path, O_RDWR | O_LARGEFILE | O_DIRECT,
|
||||||
|
d_inode(object->backer), cache->cache_cred);
|
||||||
|
if (IS_ERR(file))
|
||||||
|
return PTR_ERR(file);
|
||||||
|
if (!S_ISREG(file_inode(file)->i_mode))
|
||||||
|
goto error_file;
|
||||||
|
if (unlikely(!file->f_op->read_iter) ||
|
||||||
|
unlikely(!file->f_op->write_iter)) {
|
||||||
|
pr_notice("Cache does not support read_iter and write_iter\n");
|
||||||
|
goto error_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
fscache_get_retrieval(op);
|
||||||
|
rreq->cache_resources.cache_priv = op;
|
||||||
|
rreq->cache_resources.cache_priv2 = file;
|
||||||
|
rreq->cache_resources.ops = &cachefiles_netfs_cache_ops;
|
||||||
|
rreq->cookie_debug_id = object->fscache.debug_id;
|
||||||
|
_leave("");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error_file:
|
||||||
|
fput(file);
|
||||||
|
return -EIO;
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
config FSCACHE
|
config FSCACHE
|
||||||
tristate "General filesystem local caching manager"
|
tristate "General filesystem local caching manager"
|
||||||
|
select NETFS_SUPPORT
|
||||||
help
|
help
|
||||||
This option enables a generic filesystem caching manager that can be
|
This option enables a generic filesystem caching manager that can be
|
||||||
used by various network and other filesystems to cache data locally.
|
used by various network and other filesystems to cache data locally.
|
||||||
|
|
|
@ -7,6 +7,7 @@ fscache-y := \
|
||||||
cache.o \
|
cache.o \
|
||||||
cookie.o \
|
cookie.o \
|
||||||
fsdef.o \
|
fsdef.o \
|
||||||
|
io.o \
|
||||||
main.o \
|
main.o \
|
||||||
netfs.o \
|
netfs.o \
|
||||||
object.o \
|
object.o \
|
||||||
|
|
|
@ -142,6 +142,10 @@ extern int fscache_wait_for_operation_activation(struct fscache_object *,
|
||||||
atomic_t *,
|
atomic_t *,
|
||||||
atomic_t *);
|
atomic_t *);
|
||||||
extern void fscache_invalidate_writes(struct fscache_cookie *);
|
extern void fscache_invalidate_writes(struct fscache_cookie *);
|
||||||
|
struct fscache_retrieval *fscache_alloc_retrieval(struct fscache_cookie *cookie,
|
||||||
|
struct address_space *mapping,
|
||||||
|
fscache_rw_complete_t end_io_func,
|
||||||
|
void *context);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* proc.c
|
* proc.c
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/* Cache data I/O routines
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
|
||||||
|
* Written by David Howells (dhowells@redhat.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FSCACHE_DEBUG_LEVEL PAGE
|
||||||
|
#include <linux/module.h>
|
||||||
|
#define FSCACHE_USE_NEW_IO_API
|
||||||
|
#include <linux/fscache-cache.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/netfs.h>
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start a cache read operation.
|
||||||
|
* - we return:
|
||||||
|
* -ENOMEM - out of memory, some pages may be being read
|
||||||
|
* -ERESTARTSYS - interrupted, some pages may be being read
|
||||||
|
* -ENOBUFS - no backing object or space available in which to cache any
|
||||||
|
* pages not being read
|
||||||
|
* -ENODATA - no data available in the backing object for some or all of
|
||||||
|
* the pages
|
||||||
|
* 0 - dispatched a read on all pages
|
||||||
|
*/
|
||||||
|
int __fscache_begin_read_operation(struct netfs_read_request *rreq,
|
||||||
|
struct fscache_cookie *cookie)
|
||||||
|
{
|
||||||
|
struct fscache_retrieval *op;
|
||||||
|
struct fscache_object *object;
|
||||||
|
bool wake_cookie = false;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
_enter("rr=%08x", rreq->debug_id);
|
||||||
|
|
||||||
|
fscache_stat(&fscache_n_retrievals);
|
||||||
|
|
||||||
|
if (hlist_empty(&cookie->backing_objects))
|
||||||
|
goto nobufs;
|
||||||
|
|
||||||
|
if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) {
|
||||||
|
_leave(" = -ENOBUFS [invalidating]");
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERTCMP(cookie->def->type, !=, FSCACHE_COOKIE_TYPE_INDEX);
|
||||||
|
|
||||||
|
if (fscache_wait_for_deferred_lookup(cookie) < 0)
|
||||||
|
return -ERESTARTSYS;
|
||||||
|
|
||||||
|
op = fscache_alloc_retrieval(cookie, NULL, NULL, NULL);
|
||||||
|
if (!op)
|
||||||
|
return -ENOMEM;
|
||||||
|
trace_fscache_page_op(cookie, NULL, &op->op, fscache_page_op_retr_multi);
|
||||||
|
|
||||||
|
spin_lock(&cookie->lock);
|
||||||
|
|
||||||
|
if (!fscache_cookie_enabled(cookie) ||
|
||||||
|
hlist_empty(&cookie->backing_objects))
|
||||||
|
goto nobufs_unlock;
|
||||||
|
object = hlist_entry(cookie->backing_objects.first,
|
||||||
|
struct fscache_object, cookie_link);
|
||||||
|
|
||||||
|
__fscache_use_cookie(cookie);
|
||||||
|
atomic_inc(&object->n_reads);
|
||||||
|
__set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags);
|
||||||
|
|
||||||
|
if (fscache_submit_op(object, &op->op) < 0)
|
||||||
|
goto nobufs_unlock_dec;
|
||||||
|
spin_unlock(&cookie->lock);
|
||||||
|
|
||||||
|
fscache_stat(&fscache_n_retrieval_ops);
|
||||||
|
|
||||||
|
/* we wait for the operation to become active, and then process it
|
||||||
|
* *here*, in this thread, and not in the thread pool */
|
||||||
|
ret = fscache_wait_for_operation_activation(
|
||||||
|
object, &op->op,
|
||||||
|
__fscache_stat(&fscache_n_retrieval_op_waits),
|
||||||
|
__fscache_stat(&fscache_n_retrievals_object_dead));
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* ask the cache to honour the operation */
|
||||||
|
ret = object->cache->ops->begin_read_operation(rreq, op);
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (ret == -ENOMEM)
|
||||||
|
fscache_stat(&fscache_n_retrievals_nomem);
|
||||||
|
else if (ret == -ERESTARTSYS)
|
||||||
|
fscache_stat(&fscache_n_retrievals_intr);
|
||||||
|
else if (ret == -ENODATA)
|
||||||
|
fscache_stat(&fscache_n_retrievals_nodata);
|
||||||
|
else if (ret < 0)
|
||||||
|
fscache_stat(&fscache_n_retrievals_nobufs);
|
||||||
|
else
|
||||||
|
fscache_stat(&fscache_n_retrievals_ok);
|
||||||
|
|
||||||
|
fscache_put_retrieval(op);
|
||||||
|
_leave(" = %d", ret);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
nobufs_unlock_dec:
|
||||||
|
atomic_dec(&object->n_reads);
|
||||||
|
wake_cookie = __fscache_unuse_cookie(cookie);
|
||||||
|
nobufs_unlock:
|
||||||
|
spin_unlock(&cookie->lock);
|
||||||
|
fscache_put_retrieval(op);
|
||||||
|
if (wake_cookie)
|
||||||
|
__fscache_wake_unused_cookie(cookie);
|
||||||
|
nobufs:
|
||||||
|
fscache_stat(&fscache_n_retrievals_nobufs);
|
||||||
|
_leave(" = -ENOBUFS");
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__fscache_begin_read_operation);
|
|
@ -299,7 +299,7 @@ static void fscache_release_retrieval_op(struct fscache_operation *_op)
|
||||||
/*
|
/*
|
||||||
* allocate a retrieval op
|
* allocate a retrieval op
|
||||||
*/
|
*/
|
||||||
static struct fscache_retrieval *fscache_alloc_retrieval(
|
struct fscache_retrieval *fscache_alloc_retrieval(
|
||||||
struct fscache_cookie *cookie,
|
struct fscache_cookie *cookie,
|
||||||
struct address_space *mapping,
|
struct address_space *mapping,
|
||||||
fscache_rw_complete_t end_io_func,
|
fscache_rw_complete_t end_io_func,
|
||||||
|
|
|
@ -278,5 +278,6 @@ int fscache_stats_show(struct seq_file *m, void *v)
|
||||||
atomic_read(&fscache_n_cache_stale_objects),
|
atomic_read(&fscache_n_cache_stale_objects),
|
||||||
atomic_read(&fscache_n_cache_retired_objects),
|
atomic_read(&fscache_n_cache_retired_objects),
|
||||||
atomic_read(&fscache_n_cache_culled_objects));
|
atomic_read(&fscache_n_cache_culled_objects));
|
||||||
|
netfs_stats_show(m);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,6 +304,10 @@ struct fscache_cache_ops {
|
||||||
|
|
||||||
/* dissociate a cache from all the pages it was backing */
|
/* dissociate a cache from all the pages it was backing */
|
||||||
void (*dissociate_pages)(struct fscache_cache *cache);
|
void (*dissociate_pages)(struct fscache_cache *cache);
|
||||||
|
|
||||||
|
/* Begin a read operation for the netfs lib */
|
||||||
|
int (*begin_read_operation)(struct netfs_read_request *rreq,
|
||||||
|
struct fscache_retrieval *op);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct fscache_cookie fscache_fsdef_index;
|
extern struct fscache_cookie fscache_fsdef_index;
|
||||||
|
|
|
@ -37,6 +37,7 @@ struct pagevec;
|
||||||
struct fscache_cache_tag;
|
struct fscache_cache_tag;
|
||||||
struct fscache_cookie;
|
struct fscache_cookie;
|
||||||
struct fscache_netfs;
|
struct fscache_netfs;
|
||||||
|
struct netfs_read_request;
|
||||||
|
|
||||||
typedef void (*fscache_rw_complete_t)(struct page *page,
|
typedef void (*fscache_rw_complete_t)(struct page *page,
|
||||||
void *context,
|
void *context,
|
||||||
|
@ -191,6 +192,10 @@ extern void __fscache_update_cookie(struct fscache_cookie *, const void *);
|
||||||
extern int __fscache_attr_changed(struct fscache_cookie *);
|
extern int __fscache_attr_changed(struct fscache_cookie *);
|
||||||
extern void __fscache_invalidate(struct fscache_cookie *);
|
extern void __fscache_invalidate(struct fscache_cookie *);
|
||||||
extern void __fscache_wait_on_invalidate(struct fscache_cookie *);
|
extern void __fscache_wait_on_invalidate(struct fscache_cookie *);
|
||||||
|
|
||||||
|
#ifdef FSCACHE_USE_NEW_IO_API
|
||||||
|
extern int __fscache_begin_read_operation(struct netfs_read_request *, struct fscache_cookie *);
|
||||||
|
#else
|
||||||
extern int __fscache_read_or_alloc_page(struct fscache_cookie *,
|
extern int __fscache_read_or_alloc_page(struct fscache_cookie *,
|
||||||
struct page *,
|
struct page *,
|
||||||
fscache_rw_complete_t,
|
fscache_rw_complete_t,
|
||||||
|
@ -214,6 +219,8 @@ extern void __fscache_uncache_all_inode_pages(struct fscache_cookie *,
|
||||||
struct inode *);
|
struct inode *);
|
||||||
extern void __fscache_readpages_cancel(struct fscache_cookie *cookie,
|
extern void __fscache_readpages_cancel(struct fscache_cookie *cookie,
|
||||||
struct list_head *pages);
|
struct list_head *pages);
|
||||||
|
#endif /* FSCACHE_USE_NEW_IO_API */
|
||||||
|
|
||||||
extern void __fscache_disable_cookie(struct fscache_cookie *, const void *, bool);
|
extern void __fscache_disable_cookie(struct fscache_cookie *, const void *, bool);
|
||||||
extern void __fscache_enable_cookie(struct fscache_cookie *, const void *, loff_t,
|
extern void __fscache_enable_cookie(struct fscache_cookie *, const void *, loff_t,
|
||||||
bool (*)(void *), void *);
|
bool (*)(void *), void *);
|
||||||
|
@ -498,6 +505,36 @@ int fscache_reserve_space(struct fscache_cookie *cookie, loff_t size)
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FSCACHE_USE_NEW_IO_API
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fscache_begin_read_operation - Begin a read operation for the netfs lib
|
||||||
|
* @rreq: The read request being undertaken
|
||||||
|
* @cookie: The cookie representing the cache object
|
||||||
|
*
|
||||||
|
* Begin a read operation on behalf of the netfs helper library. @rreq
|
||||||
|
* indicates the read request to which the operation state should be attached;
|
||||||
|
* @cookie indicates the cache object that will be accessed.
|
||||||
|
*
|
||||||
|
* This is intended to be called from the ->begin_cache_operation() netfs lib
|
||||||
|
* operation as implemented by the network filesystem.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* * 0 - Success
|
||||||
|
* * -ENOBUFS - No caching available
|
||||||
|
* * Other error code from the cache, such as -ENOMEM.
|
||||||
|
*/
|
||||||
|
static inline
|
||||||
|
int fscache_begin_read_operation(struct netfs_read_request *rreq,
|
||||||
|
struct fscache_cookie *cookie)
|
||||||
|
{
|
||||||
|
if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie))
|
||||||
|
return __fscache_begin_read_operation(rreq, cookie);
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* FSCACHE_USE_NEW_IO_API */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fscache_read_or_alloc_page - Read a page from the cache or allocate a block
|
* fscache_read_or_alloc_page - Read a page from the cache or allocate a block
|
||||||
* in which to store it
|
* in which to store it
|
||||||
|
@ -777,6 +814,8 @@ void fscache_uncache_all_inode_pages(struct fscache_cookie *cookie,
|
||||||
__fscache_uncache_all_inode_pages(cookie, inode);
|
__fscache_uncache_all_inode_pages(cookie, inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* FSCACHE_USE_NEW_IO_API */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fscache_disable_cookie - Disable a cookie
|
* fscache_disable_cookie - Disable a cookie
|
||||||
* @cookie: The cookie representing the cache object
|
* @cookie: The cookie representing the cache object
|
||||||
|
|
Loading…
Reference in New Issue