summaryrefslogtreecommitdiff
path: root/fs/fuse/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fuse/file.c')
-rw-r--r--fs/fuse/file.c83
1 files changed, 79 insertions, 4 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index f7d025d1684c..9fc6c2597dcc 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -7,6 +7,7 @@
*/
#include "fuse_i.h"
+#include "fuse_passthrough.h"
#include <linux/pagemap.h>
#include <linux/slab.h>
@@ -22,8 +23,10 @@
static const struct file_operations fuse_direct_io_file_operations;
static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
- int opcode, struct fuse_open_out *outargp)
+ int opcode, struct fuse_open_out *outargp,
+ struct file **passthrough_filpp)
{
+ int ret_val;
struct fuse_open_in inarg;
FUSE_ARGS(args);
@@ -39,8 +42,14 @@ static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
args.out.numargs = 1;
args.out.args[0].size = sizeof(*outargp);
args.out.args[0].value = outargp;
+ args.out.passthrough_filp = NULL;
- return fuse_simple_request(fc, &args);
+ ret_val = fuse_simple_request(fc, &args);
+
+ if (args.out.passthrough_filp != NULL)
+ *passthrough_filpp = args.out.passthrough_filp;
+
+ return ret_val;
}
struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
@@ -51,6 +60,10 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
if (unlikely(!ff))
return NULL;
+ ff->passthrough_filp = NULL;
+ ff->passthrough_enabled = 0;
+ if (fc->passthrough)
+ ff->passthrough_enabled = 1;
ff->fc = fc;
ff->reserved_req = fuse_request_alloc(0);
if (unlikely(!ff->reserved_req)) {
@@ -119,6 +132,7 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
bool isdir)
{
struct fuse_file *ff;
+ struct file *passthrough_filp = NULL;
int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
ff = fuse_file_alloc(fc);
@@ -131,10 +145,12 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
struct fuse_open_out outarg;
int err;
- err = fuse_send_open(fc, nodeid, file, opcode, &outarg);
+ err = fuse_send_open(fc, nodeid, file, opcode, &outarg,
+ &(passthrough_filp));
if (!err) {
ff->fh = outarg.fh;
ff->open_flags = outarg.open_flags;
+ ff->passthrough_filp = passthrough_filp;
} else if (err != -ENOSYS || isdir) {
fuse_file_free(ff);
@@ -260,6 +276,8 @@ void fuse_release_common(struct file *file, int opcode)
if (unlikely(!ff))
return;
+ fuse_passthrough_release(ff);
+
req = ff->reserved_req;
fuse_prepare_release(ff, file->f_flags, opcode);
@@ -891,6 +909,43 @@ static int fuse_readpages_fill(void *_data, struct page *page)
return -EIO;
}
+#ifdef CONFIG_CMA
+ if (is_cma_pageblock(page)) {
+ struct page *oldpage = page, *newpage;
+ int err;
+
+ /* make sure that old page is not free in-between the calls */
+ page_cache_get(oldpage);
+
+ newpage = alloc_page(GFP_HIGHUSER);
+ if (!newpage) {
+ page_cache_release(oldpage);
+ return -ENOMEM;
+ }
+
+ err = replace_page_cache_page(oldpage, newpage, GFP_KERNEL);
+ if (err) {
+ __free_page(newpage);
+ page_cache_release(oldpage);
+ return err;
+ }
+
+ /*
+ * Decrement the count on new page to make page cache the only
+ * owner of it
+ */
+ lock_page(newpage);
+ put_page(newpage);
+
+ lru_cache_add_file(newpage);
+
+ /* finally release the old page and swap pointers */
+ unlock_page(oldpage);
+ page_cache_release(oldpage);
+ page = newpage;
+ }
+#endif
+
page_cache_get(page);
req->pages[req->num_pages] = page;
req->page_descs[req->num_pages].length = PAGE_SIZE;
@@ -936,8 +991,10 @@ out:
static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
+ ssize_t ret_val;
struct inode *inode = iocb->ki_filp->f_mapping->host;
struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_file *ff = iocb->ki_filp->private_data;
/*
* In auto invalidate mode, always update attributes on read.
@@ -952,7 +1009,12 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
return err;
}
- return generic_file_read_iter(iocb, to);
+ if (ff && ff->passthrough_enabled && ff->passthrough_filp)
+ ret_val = fuse_passthrough_read_iter(iocb, to);
+ else
+ ret_val = generic_file_read_iter(iocb, to);
+
+ return ret_val;
}
static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
@@ -1184,6 +1246,7 @@ static ssize_t fuse_perform_write(struct file *file,
static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
+ struct fuse_file *ff = file->private_data;
struct address_space *mapping = file->f_mapping;
ssize_t written = 0;
ssize_t written_buffered = 0;
@@ -1217,8 +1280,14 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (err)
goto out;
+ if (ff && ff->passthrough_enabled && ff->passthrough_filp) {
+ written = fuse_passthrough_write_iter(iocb, from);
+ goto out;
+ }
+
if (iocb->ki_flags & IOCB_DIRECT) {
loff_t pos = iocb->ki_pos;
+
written = generic_file_direct_write(iocb, from, pos);
if (written < 0 || !iov_iter_count(from))
goto out;
@@ -2090,6 +2159,9 @@ static const struct vm_operations_struct fuse_file_vm_ops = {
static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
{
+ struct fuse_file *ff = file->private_data;
+
+ ff->passthrough_enabled = 0;
if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
fuse_link_write_file(file);
@@ -2100,6 +2172,9 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
static int fuse_direct_mmap(struct file *file, struct vm_area_struct *vma)
{
+ struct fuse_file *ff = file->private_data;
+
+ ff->passthrough_enabled = 0;
/* Can't provide the coherency needed for MAP_SHARED */
if (vma->vm_flags & VM_MAYSHARE)
return -ENODEV;