mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-09-20 19:34:46 +00:00
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:
parent
ae1008a928
commit
03a09ae085
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
33
fs/fuse/fuse_shortcircuit.h
Normal file
33
fs/fuse/fuse_shortcircuit.h
Normal 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 */
|
|
@ -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
105
fs/fuse/shortcircuit.c
Normal 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;
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue