fuse: Add support for shortcircuited read/write for files

Add support for shortcircuited read/write for files
when enabled through a userspace init option of
FUSE_SHORTCIRCUIT.

When FUSE_SHORTCIRCUIT is enabled all the reads and writes
to the fuse mount point go directly to the native filesystem
rather than through the fuse daemon. All requsts that aren't
read/write still go thought the userspace code.

This allows for significantly better performance on read and writes
and the difference between fuse and the native lower filesystem is
negligible.

Change-Id: I8258f356963505a2f8499f38879e9e36aba43ad4
Signed-off-by: Nikhilesh Reddy <reddyn@codeaurora.org>
This commit is contained in:
Nikhilesh Reddy 2015-05-15 16:03:01 -07:00 committed by Sahitya Tummala
parent ae1008a928
commit 03a09ae085
9 changed files with 191 additions and 5 deletions

View file

@ -5,4 +5,4 @@
obj-$(CONFIG_FUSE_FS) += fuse.o
obj-$(CONFIG_CUSE) += cuse.o
fuse-objs := dev.o dir.o file.o inode.o control.o
fuse-objs := dev.o dir.o file.o inode.o control.o shortcircuit.o

View file

@ -7,6 +7,7 @@
*/
#include "fuse_i.h"
#include "fuse_shortcircuit.h"
#include <linux/init.h>
#include <linux/module.h>
@ -1876,6 +1877,8 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc,
err = copy_out_args(cs, &req->out, nbytes);
fuse_copy_finish(cs);
fuse_setup_shortcircuit(fc, req);
spin_lock(&fc->lock);
req->locked = 0;
if (!err) {

View file

@ -472,6 +472,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
if (err)
goto out_free_ff;
if (req->private_lower_rw_file != NULL)
ff->rw_lower_file = req->private_lower_rw_file;
err = -EIO;
if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid))
goto out_free_ff;

View file

@ -7,6 +7,7 @@
*/
#include "fuse_i.h"
#include "fuse_shortcircuit.h"
#include <linux/pagemap.h>
#include <linux/slab.h>
@ -21,7 +22,8 @@
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 **lower_file)
{
struct fuse_open_in inarg;
struct fuse_req *req;
@ -45,6 +47,10 @@ static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
req->out.args[0].value = outargp;
fuse_request_send(fc, req);
err = req->out.h.error;
if (!err && req->private_lower_rw_file != NULL)
*lower_file = req->private_lower_rw_file;
fuse_put_request(fc, req);
return err;
@ -58,6 +64,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
if (unlikely(!ff))
return NULL;
ff->rw_lower_file = NULL;
ff->fc = fc;
ff->reserved_req = fuse_request_alloc(0);
if (unlikely(!ff->reserved_req)) {
@ -153,7 +160,8 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
if (!ff)
return -ENOMEM;
err = fuse_send_open(fc, nodeid, file, opcode, &outarg);
err = fuse_send_open(fc, nodeid, file, opcode, &outarg,
&(ff->rw_lower_file));
if (err) {
fuse_file_free(ff);
return err;
@ -261,6 +269,8 @@ void fuse_release_common(struct file *file, int opcode)
if (unlikely(!ff))
return;
fuse_shortcircuit_release(ff);
req = ff->reserved_req;
fuse_prepare_release(ff, file->f_flags, opcode);
@ -959,8 +969,10 @@ out:
static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
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.
@ -975,7 +987,12 @@ static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
return err;
}
return generic_file_aio_read(iocb, iov, nr_segs, pos);
if (ff && ff->rw_lower_file)
ret_val = fuse_shortcircuit_aio_read(iocb, iov, nr_segs, pos);
else
ret_val = generic_file_aio_read(iocb, iov, nr_segs, pos);
return ret_val;
}
static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
@ -1213,6 +1230,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
{
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
struct fuse_file *ff = file->private_data;
size_t count = 0;
size_t ocount = 0;
ssize_t written = 0;
@ -1222,6 +1240,15 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
struct iov_iter i;
loff_t endbyte = 0;
if (ff && ff->rw_lower_file) {
/* Update size (EOF optimization) and mode (SUID clearing) */
err = fuse_update_attributes(mapping->host, NULL, file, NULL);
if (err)
return err;
return fuse_shortcircuit_aio_write(iocb, iov, nr_segs, pos);
}
if (get_fuse_conn(inode)->writeback_cache) {
/* Update size (EOF optimization) and mode (SUID clearing) */
err = fuse_update_attributes(mapping->host, NULL, file, NULL);

View file

@ -157,6 +157,9 @@ struct fuse_file {
/** Has flock been performed on this file? */
bool flock:1;
/* the read write file */
struct file *rw_lower_file;
};
/** One input argument of a request */
@ -361,6 +364,9 @@ struct fuse_req {
/** Request is stolen from fuse_file->reserved_req */
struct file *stolen_file;
/** fuse shortcircuit file */
struct file *private_lower_rw_file;
};
/**
@ -483,6 +489,9 @@ struct fuse_conn {
/** write-back cache policy (default is write-through) */
unsigned writeback_cache:1;
/** Shortcircuited IO. */
unsigned shortcircuit_io:1;
/*
* The following bitfields are only for optimization purposes
* and hence races in setting them will not cause malfunction

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _FS_FUSE_SHORCIRCUIT_H
#define _FS_FUSE_SHORCIRCUIT_H
#include "fuse_i.h"
#include <linux/fuse.h>
#include <linux/file.h>
void fuse_setup_shortcircuit(struct fuse_conn *fc, struct fuse_req *req);
ssize_t fuse_shortcircuit_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
ssize_t fuse_shortcircuit_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
void fuse_shortcircuit_release(struct fuse_file *ff);
#endif /* _FS_FUSE_SHORCIRCUIT_H */

View file

@ -904,6 +904,10 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
fc->async_dio = 1;
if (arg->flags & FUSE_WRITEBACK_CACHE)
fc->writeback_cache = 1;
if (arg->flags & FUSE_SHORTCIRCUIT) {
fc->writeback_cache = 0;
fc->shortcircuit_io = 1;
}
if (arg->time_gran && arg->time_gran <= 1000000000)
fc->sb->s_time_gran = arg->time_gran;
else

105
fs/fuse/shortcircuit.c Normal file
View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "fuse_shortcircuit.h"
#include <linux/aio.h>
void fuse_setup_shortcircuit(struct fuse_conn *fc, struct fuse_req *req)
{
int daemon_fd;
struct file *rw_lower_file = NULL;
struct fuse_open_out *open_out;
int open_out_index;
req->private_lower_rw_file = NULL;
if (!(fc->shortcircuit_io))
return;
if ((req->in.h.opcode != FUSE_OPEN) &&
(req->in.h.opcode != FUSE_CREATE))
return;
open_out_index = req->in.numargs - 1;
BUG_ON(open_out_index != 0 && open_out_index != 1);
BUG_ON(req->out.args[open_out_index].size != sizeof(*open_out));
open_out = req->out.args[open_out_index].value;
daemon_fd = (int)open_out->lower_fd;
if (daemon_fd < 0)
return;
rw_lower_file = fget_raw(daemon_fd);
if (!rw_lower_file)
return;
req->private_lower_rw_file = rw_lower_file;
}
static ssize_t fuse_shortcircuit_aio_read_write(struct kiocb *iocb,
const struct iovec *iov,
unsigned long nr_segs,
loff_t pos, int do_write)
{
ssize_t ret_val;
struct fuse_file *ff = iocb->ki_filp->private_data;
struct file *fuse_file, *lower_file;
fuse_file = iocb->ki_filp;
lower_file = ff->rw_lower_file;
/* lock lower file to prevent it from being released */
get_file(lower_file);
iocb->ki_filp = lower_file;
if (do_write) {
if (!lower_file->f_op->aio_write)
return -EIO;
ret_val = lower_file->f_op->aio_write(iocb, iov, nr_segs, pos);
} else {
if (!lower_file->f_op->aio_read)
return -EIO;
ret_val = lower_file->f_op->aio_read(iocb, iov, nr_segs, pos);
}
iocb->ki_filp = fuse_file;
fput(lower_file);
/* unlock lower file */
return ret_val;
}
ssize_t fuse_shortcircuit_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
return fuse_shortcircuit_aio_read_write(iocb, iov, nr_segs, pos, 0);
}
ssize_t fuse_shortcircuit_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
return fuse_shortcircuit_aio_read_write(iocb, iov, nr_segs, pos, 1);
}
void fuse_shortcircuit_release(struct fuse_file *ff)
{
if (!(ff->rw_lower_file))
return;
/* Release the lower file. */
fput(ff->rw_lower_file);
ff->rw_lower_file = NULL;
}

View file

@ -244,6 +244,8 @@ struct fuse_file_lock {
#define FUSE_ASYNC_DIO (1 << 15)
#define FUSE_WRITEBACK_CACHE (1 << 16)
#define FUSE_SHORTCIRCUIT (1 << 31)
/**
* CUSE INIT request/reply flags
*
@ -466,7 +468,7 @@ struct fuse_create_in {
struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
uint32_t padding;
int32_t lower_fd;/* lower layer file descriptor */
};
struct fuse_release_in {