mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
fs: fuse: Implement fuse short circuit
* This significantly improves i/o performance under /sdcard * From OnePlus 8T Oxygen OS 11.0.8.11.KB05AA and OnePlus 8 Oxygen OS 11.0.5.5.IN21AA and OnePlus 8 Pro Oxygen OS 11.0.5.5.IN11AA RealJohnGalt: make proper Kconfig, add back dependencies from OnePlus source onto our CAF tree. Signed-off-by: Adithya R <gh0strider.2k18.reborn@gmail.com> Signed-off-by: Forenche <prahul2003@gmail.com>
This commit is contained in:
parent
d88efacc59
commit
0b49172223
@ -17,6 +17,12 @@ config FUSE_FS
|
||||
If you want to develop a userspace FS, or if you want to use
|
||||
a filesystem based on FUSE, answer Y or M.
|
||||
|
||||
config FUSE_SHORTCIRCUIT
|
||||
bool "OnePlus FUSE Optimization"
|
||||
depends on FUSE_FS
|
||||
help
|
||||
An optimization for better /sdcard/ performance on Android by OnePlus.
|
||||
|
||||
config CUSE
|
||||
tristate "Character device in Userspace support"
|
||||
depends on FUSE_FS
|
||||
|
@ -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 xattr.o acl.o
|
||||
fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o shortcircuit.o
|
||||
|
100
fs/fuse/dev.c
100
fs/fuse/dev.c
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_shortcircuit.h"
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
@ -38,6 +39,63 @@ static struct fuse_dev *fuse_get_dev(struct file *file)
|
||||
return ACCESS_ONCE(file->private_data);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FUSE_SHORTCIRCUIT
|
||||
#include <linux/cred.h>
|
||||
extern unsigned int ht_fuse_boost;
|
||||
|
||||
unsigned int ht_fuse_boost = 2;
|
||||
module_param_named(fuse_boost, ht_fuse_boost, uint, 0664);
|
||||
|
||||
static int fuse_debug;
|
||||
module_param_named(fuse_debug, fuse_debug, int, 0664);
|
||||
|
||||
static inline bool fuse_can_boost(void)
|
||||
{
|
||||
int uid = current_uid().val;
|
||||
|
||||
if (!ht_fuse_boost)
|
||||
return false;
|
||||
|
||||
// fuse_boost enabled and is foreground request
|
||||
if (ht_fuse_boost >= 1 && is_fg(uid))
|
||||
return true;
|
||||
|
||||
// fuse_boost enabled and is system request (include foreground request)
|
||||
if (ht_fuse_boost == 2 && uid < 10000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void fuse_boost_init(struct fuse_req *req)
|
||||
{
|
||||
clear_bit(FR_BOOST, &req->flags);
|
||||
|
||||
if (fuse_can_boost())
|
||||
__set_bit(FR_BOOST, &req->flags);
|
||||
|
||||
if (fuse_debug) {
|
||||
int uid = current_uid().val;
|
||||
|
||||
pr_info("current %s %d, fg: %d, uid: %d\n",
|
||||
current->comm, current->pid, current_is_fg(), uid);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void fuse_boost_active_check(struct fuse_req *req)
|
||||
{
|
||||
// boost active check
|
||||
// 1. sysctl: sched_fuse_boost (on)
|
||||
// 2. target: mediaprovider's specific tasks
|
||||
// TODO: add system busy check to not impact ux experience
|
||||
if (ht_fuse_boost)
|
||||
current->fuse_boost = test_bit(FR_BOOST, &req->flags) ? 1 : 0;
|
||||
}
|
||||
#else
|
||||
static inline void fuse_boost_init(struct fuse_req *req) {}
|
||||
static inline void fuse_boost_active_check(struct fuse_req *req) {}
|
||||
#endif
|
||||
|
||||
static void fuse_request_init(struct fuse_req *req, struct page **pages,
|
||||
struct fuse_page_desc *page_descs,
|
||||
unsigned npages)
|
||||
@ -53,6 +111,8 @@ static void fuse_request_init(struct fuse_req *req, struct page **pages,
|
||||
req->page_descs = page_descs;
|
||||
req->max_pages = npages;
|
||||
__set_bit(FR_PENDING, &req->flags);
|
||||
|
||||
fuse_boost_init(req);
|
||||
}
|
||||
|
||||
static struct fuse_req *__fuse_request_alloc(unsigned npages, gfp_t flags)
|
||||
@ -100,6 +160,10 @@ void fuse_request_free(struct fuse_req *req)
|
||||
kfree(req->pages);
|
||||
kfree(req->page_descs);
|
||||
}
|
||||
if (req->iname) {
|
||||
__putname(req->iname);
|
||||
req->iname = NULL;
|
||||
}
|
||||
kmem_cache_free(fuse_req_cachep, req);
|
||||
}
|
||||
|
||||
@ -568,10 +632,16 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
|
||||
args->in.numargs * sizeof(struct fuse_in_arg));
|
||||
req->out.argvar = args->out.argvar;
|
||||
req->out.numargs = args->out.numargs;
|
||||
req->iname = args->iname;
|
||||
args->iname = NULL;
|
||||
memcpy(req->out.args, args->out.args,
|
||||
args->out.numargs * sizeof(struct fuse_arg));
|
||||
fuse_request_send(fc, req);
|
||||
ret = req->out.h.error;
|
||||
if (!ret) {
|
||||
if (req->private_lower_rw_file != NULL)
|
||||
args->private_lower_rw_file = req->private_lower_rw_file;
|
||||
}
|
||||
if (!ret && args->out.argvar) {
|
||||
BUG_ON(args->out.numargs != 1);
|
||||
ret = req->out.args[0].size;
|
||||
@ -1279,6 +1349,9 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
|
||||
|
||||
req = list_entry(fiq->pending.next, struct fuse_req, list);
|
||||
clear_bit(FR_PENDING, &req->flags);
|
||||
|
||||
fuse_boost_active_check(req);
|
||||
|
||||
list_del_init(&req->list);
|
||||
spin_unlock(&fiq->waitq.lock);
|
||||
|
||||
@ -1328,6 +1401,24 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
|
||||
__fuse_get_request(req);
|
||||
set_bit(FR_SENT, &req->flags);
|
||||
spin_unlock(&fpq->lock);
|
||||
|
||||
if (sct_mode == 1) {
|
||||
if (current->fpack) {
|
||||
if (current->fpack->iname)
|
||||
__putname(current->fpack->iname);
|
||||
memset(current->fpack, 0, sizeof(struct fuse_package));
|
||||
}
|
||||
if (req->in.h.opcode == FUSE_OPEN || req->in.h.opcode == FUSE_CREATE) {
|
||||
if (!current->fpack)
|
||||
current->fpack = kzalloc(sizeof(struct fuse_package), GFP_KERNEL);
|
||||
if (likely(current->fpack)) {
|
||||
current->fpack->fuse_open_req = true;
|
||||
current->fpack->iname = req->iname;
|
||||
req->iname = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* matches barrier in request_wait_answer() */
|
||||
smp_mb__after_atomic();
|
||||
if (test_bit(FR_INTERRUPTED, &req->flags))
|
||||
@ -1857,6 +1948,11 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
|
||||
struct fuse_req *req;
|
||||
struct fuse_out_header oh;
|
||||
|
||||
if (current->fpack && current->fpack->iname) {
|
||||
__putname(current->fpack->iname);
|
||||
current->fpack->iname = NULL;
|
||||
}
|
||||
|
||||
if (nbytes < sizeof(struct fuse_out_header))
|
||||
return -EINVAL;
|
||||
|
||||
@ -1890,6 +1986,8 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
|
||||
if (!req)
|
||||
goto err_unlock_pq;
|
||||
|
||||
fuse_boost_active_check(req);
|
||||
|
||||
/* Is it an interrupt reply? */
|
||||
if (req->intr_unique == oh.unique) {
|
||||
__fuse_get_request(req);
|
||||
@ -1929,6 +2027,8 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
|
||||
}
|
||||
fuse_copy_finish(cs);
|
||||
|
||||
fuse_setup_shortcircuit(fc, req);
|
||||
|
||||
spin_lock(&fpq->lock);
|
||||
clear_bit(FR_LOCKED, &req->flags);
|
||||
if (!fpq->connected)
|
||||
|
@ -464,6 +464,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||
struct fuse_open_out outopen;
|
||||
struct fuse_entry_out outentry;
|
||||
struct fuse_file *ff;
|
||||
char *iname;
|
||||
|
||||
/* Userspace expects S_IFREG in create mode */
|
||||
BUG_ON((mode & S_IFMT) != S_IFREG);
|
||||
@ -482,6 +483,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||
mode &= ~current_umask();
|
||||
|
||||
flags &= ~O_NOCTTY;
|
||||
if (fc->writeback_cache)
|
||||
flags &= ~O_APPEND;
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
memset(&outentry, 0, sizeof(outentry));
|
||||
inarg.flags = flags;
|
||||
@ -499,7 +502,22 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||
args.out.args[0].value = &outentry;
|
||||
args.out.args[1].size = sizeof(outopen);
|
||||
args.out.args[1].value = &outopen;
|
||||
args.private_lower_rw_file = NULL;
|
||||
iname = inode_name(dir);
|
||||
if (iname) {
|
||||
/* compose full path */
|
||||
if ((strlen(iname) + entry->d_name.len + 2) <= PATH_MAX) {
|
||||
strlcat(iname, "/", PATH_MAX);
|
||||
strlcat(iname, entry->d_name.name, PATH_MAX);
|
||||
} else {
|
||||
__putname(iname);
|
||||
iname = NULL;
|
||||
}
|
||||
}
|
||||
args.iname = iname;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (args.iname)
|
||||
__putname(args.iname);
|
||||
if (err)
|
||||
goto out_free_ff;
|
||||
|
||||
@ -511,6 +529,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||
ff->fh = outopen.fh;
|
||||
ff->nodeid = outentry.nodeid;
|
||||
ff->open_flags = outopen.open_flags;
|
||||
if (args.private_lower_rw_file != NULL)
|
||||
ff->rw_lower_file = args.private_lower_rw_file;
|
||||
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
|
||||
&outentry.attr, entry_attr_timeout(&outentry), 0);
|
||||
if (!inode) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_shortcircuit.h"
|
||||
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/slab.h>
|
||||
@ -22,15 +23,21 @@
|
||||
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)
|
||||
{
|
||||
ssize_t ret;
|
||||
struct fuse_open_in inarg;
|
||||
FUSE_ARGS(args);
|
||||
char *iname = NULL;
|
||||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
|
||||
if (!fc->atomic_o_trunc)
|
||||
inarg.flags &= ~O_TRUNC;
|
||||
if (fc->writeback_cache)
|
||||
inarg.flags &= ~O_APPEND;
|
||||
|
||||
args.in.h.opcode = opcode;
|
||||
args.in.h.nodeid = nodeid;
|
||||
args.in.numargs = 1;
|
||||
@ -40,7 +47,16 @@ static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
|
||||
args.out.args[0].size = sizeof(*outargp);
|
||||
args.out.args[0].value = outargp;
|
||||
|
||||
return fuse_simple_request(fc, &args);
|
||||
if (opcode == FUSE_OPEN)
|
||||
iname = inode_name(file_inode(file));
|
||||
args.iname = iname;
|
||||
|
||||
ret = fuse_simple_request(fc, &args);
|
||||
if (args.iname)
|
||||
__putname(args.iname);
|
||||
if (args.private_lower_rw_file != NULL)
|
||||
*lower_file = args.private_lower_rw_file;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
|
||||
@ -51,6 +67,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)) {
|
||||
@ -131,7 +148,8 @@ 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,
|
||||
&(ff->rw_lower_file));
|
||||
if (!err) {
|
||||
ff->fh = outarg.fh;
|
||||
ff->open_flags = outarg.open_flags;
|
||||
@ -257,6 +275,7 @@ void fuse_release_common(struct file *file, bool isdir)
|
||||
struct fuse_req *req = ff->reserved_req;
|
||||
int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE;
|
||||
|
||||
fuse_shortcircuit_release(ff);
|
||||
fuse_prepare_release(ff, file->f_flags, opcode);
|
||||
|
||||
if (ff->flock) {
|
||||
@ -924,8 +943,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.
|
||||
@ -940,7 +961,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->rw_lower_file)
|
||||
ret_val = fuse_shortcircuit_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,
|
||||
@ -1178,12 +1204,22 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
struct address_space *mapping = file->f_mapping;
|
||||
struct fuse_file *ff = file->private_data;
|
||||
ssize_t written = 0;
|
||||
ssize_t written_buffered = 0;
|
||||
struct inode *inode = mapping->host;
|
||||
ssize_t err;
|
||||
loff_t endbyte = 0;
|
||||
|
||||
if (ff && ff->rw_lower_file) {
|
||||
/* Update size (EOF optimization) and mode (SUID clearing) */
|
||||
err = fuse_update_attributes(mapping->host, file);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return fuse_shortcircuit_write_iter(iocb, from);
|
||||
}
|
||||
|
||||
if (get_fuse_conn(inode)->writeback_cache) {
|
||||
/* Update size (EOF optimization) and mode (SUID clearing) */
|
||||
err = fuse_update_attributes(mapping->host, file);
|
||||
|
@ -155,6 +155,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 */
|
||||
@ -235,6 +238,10 @@ struct fuse_args {
|
||||
unsigned numargs;
|
||||
struct fuse_arg args[2];
|
||||
} out;
|
||||
|
||||
/** fuse shortcircuit file */
|
||||
struct file *private_lower_rw_file;
|
||||
char *iname;
|
||||
};
|
||||
|
||||
#define FUSE_ARGS(args) struct fuse_args args = {}
|
||||
@ -277,6 +284,8 @@ struct fuse_io_priv {
|
||||
* FR_SENT: request is in userspace, waiting for an answer
|
||||
* FR_FINISHED: request is finished
|
||||
* FR_PRIVATE: request is on private list
|
||||
*
|
||||
* FR_BOOST: request can be boost
|
||||
*/
|
||||
enum fuse_req_flag {
|
||||
FR_ISREPLY,
|
||||
@ -290,6 +299,10 @@ enum fuse_req_flag {
|
||||
FR_SENT,
|
||||
FR_FINISHED,
|
||||
FR_PRIVATE,
|
||||
|
||||
#ifdef CONFIG_FUSE_SHORTCIRCUIT
|
||||
FR_BOOST = 30,
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
@ -384,6 +397,10 @@ struct fuse_req {
|
||||
|
||||
/** Request is stolen from fuse_file->reserved_req */
|
||||
struct file *stolen_file;
|
||||
|
||||
/** fuse shortcircuit file */
|
||||
struct file *private_lower_rw_file;
|
||||
char *iname;
|
||||
};
|
||||
|
||||
struct fuse_iqueue {
|
||||
@ -544,6 +561,9 @@ struct fuse_conn {
|
||||
/** handle fs handles killing suid/sgid/cap on write/chown/trunc */
|
||||
unsigned handle_killpriv: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
|
||||
@ -985,5 +1005,6 @@ extern const struct xattr_handler *fuse_acl_xattr_handlers[];
|
||||
struct posix_acl;
|
||||
struct posix_acl *fuse_get_acl(struct inode *inode, int type);
|
||||
int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type);
|
||||
extern int sct_mode;
|
||||
|
||||
#endif /* _FS_FUSE_I_H */
|
||||
|
32
fs/fuse/fuse_shortcircuit.h
Normal file
32
fs/fuse/fuse_shortcircuit.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* 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_read_iter(struct kiocb *iocb, struct iov_iter *to);
|
||||
|
||||
ssize_t fuse_shortcircuit_write_iter(struct kiocb *iocb, struct iov_iter *from);
|
||||
|
||||
void fuse_shortcircuit_release(struct fuse_file *ff);
|
||||
|
||||
#endif /* _FS_FUSE_SHORCIRCUIT_H */
|
@ -49,6 +49,10 @@ MODULE_PARM_DESC(max_user_congthresh,
|
||||
"Global limit for the maximum congestion threshold an "
|
||||
"unprivileged user can set");
|
||||
|
||||
static bool shortcircuit = true;
|
||||
module_param(shortcircuit, bool, 0644);
|
||||
MODULE_PARM_DESC(shortcircuit, "Enable or disable fuse shortcircuit. Default: y/Y/1");
|
||||
|
||||
#define FUSE_SUPER_MAGIC 0x65735546
|
||||
|
||||
#define FUSE_DEFAULT_BLKSIZE 512
|
||||
@ -910,6 +914,13 @@ 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) {
|
||||
/** an ugly way to determine FuseDaemon by writeback_cache
|
||||
* since currently only FuseDaemon enable WBC
|
||||
*/
|
||||
fc->shortcircuit_io = shortcircuit ? 1 : 0;
|
||||
pr_info("fuse sct flag: %d\n", shortcircuit);
|
||||
}
|
||||
if (arg->flags & FUSE_PARALLEL_DIROPS)
|
||||
fc->parallel_dirops = 1;
|
||||
if (arg->flags & FUSE_HANDLE_KILLPRIV)
|
||||
@ -951,7 +962,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
|
||||
FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
|
||||
FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
|
||||
FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
|
||||
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL;
|
||||
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
|
||||
FUSE_SHORTCIRCUIT;
|
||||
req->in.h.opcode = FUSE_INIT;
|
||||
req->in.numargs = 1;
|
||||
req->in.args[0].size = sizeof(*arg);
|
||||
|
197
fs/fuse/shortcircuit.c
Normal file
197
fs/fuse/shortcircuit.c
Normal file
@ -0,0 +1,197 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* 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>
|
||||
#include <linux/fs_stack.h>
|
||||
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/sched/signal.h>
|
||||
|
||||
int __read_mostly sct_mode = 2;
|
||||
module_param(sct_mode, int, 0644);
|
||||
|
||||
static char *__dentry_name(struct dentry *dentry, char *name)
|
||||
{
|
||||
char *p = dentry_path_raw(dentry, name, PATH_MAX);
|
||||
|
||||
if (IS_ERR(p)) {
|
||||
__putname(name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function relies on the fact that dentry_path_raw() will place
|
||||
* the path name at the end of the provided buffer.
|
||||
*/
|
||||
BUG_ON(p + strlen(p) + 1 != name + PATH_MAX);
|
||||
|
||||
if (p > name)
|
||||
strlcpy(name, p, PATH_MAX);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static char *dentry_name(struct dentry *dentry)
|
||||
{
|
||||
char *name = __getname();
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
return __dentry_name(dentry, name);
|
||||
}
|
||||
|
||||
char *inode_name(struct inode *ino)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
char *name;
|
||||
|
||||
if (sct_mode != 1)
|
||||
return NULL;
|
||||
|
||||
dentry = d_find_alias(ino);
|
||||
if (!dentry)
|
||||
return NULL;
|
||||
|
||||
name = dentry_name(dentry);
|
||||
|
||||
dput(dentry);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
void fuse_setup_shortcircuit(struct fuse_conn *fc, struct fuse_req *req)
|
||||
{
|
||||
int fd, flags, open_out_index;
|
||||
struct file *rw_lower_file = NULL;
|
||||
struct fuse_open_out *open_out;
|
||||
struct fuse_package *fp = current->fpack;
|
||||
|
||||
req->private_lower_rw_file = NULL;
|
||||
|
||||
if (!sct_mode)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
if ((open_out_index != 0 && open_out_index != 1) ||
|
||||
(req->out.args[open_out_index].size != sizeof(*open_out)))
|
||||
return;
|
||||
|
||||
open_out = req->out.args[open_out_index].value;
|
||||
if (!open_out->fh)
|
||||
return;
|
||||
|
||||
flags = open_out->open_flags;
|
||||
if ((flags & FOPEN_DIRECT_IO) || !(flags & FOPEN_KEEP_CACHE)) {
|
||||
pr_info("fuse bypass sct #flags:%d\n", flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sct_mode == 1) {
|
||||
if (fp) {
|
||||
req->private_lower_rw_file = fp->filp;
|
||||
fp->filp = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (fp && fp->filp) {
|
||||
fput(fp->filp);
|
||||
fp->filp = NULL;
|
||||
}
|
||||
|
||||
if (get_user(fd, (int __user *)open_out->fh))
|
||||
return;
|
||||
|
||||
if (fd <= 1 || fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_max) {
|
||||
pr_info("fuse bypass sct:%d, %d\n", fd, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
rw_lower_file = fget_raw(fd);
|
||||
if (!rw_lower_file)
|
||||
return;
|
||||
|
||||
req->private_lower_rw_file = rw_lower_file;
|
||||
pr_debug("fuse setup sct:%d, %d\n", fd, flags);
|
||||
}
|
||||
|
||||
static ssize_t fuse_shortcircuit_read_write_iter(struct kiocb *iocb,
|
||||
struct iov_iter *iter,
|
||||
int do_write)
|
||||
{
|
||||
struct file *fuse_filp = iocb->ki_filp;
|
||||
struct fuse_file *ff = fuse_filp->private_data;
|
||||
struct file *lower_file = ff->rw_lower_file;
|
||||
struct inode *fuse_inode, *shortcircuit_inode;
|
||||
ssize_t ret = -EIO;
|
||||
|
||||
fuse_inode = fuse_filp->f_path.dentry->d_inode;
|
||||
shortcircuit_inode = file_inode(lower_file);
|
||||
|
||||
iocb->ki_filp = lower_file;
|
||||
|
||||
if (do_write) {
|
||||
if (!lower_file->f_op->write_iter)
|
||||
goto out;
|
||||
|
||||
ret = call_write_iter(lower_file, iocb, iter);
|
||||
if (ret >= 0 || ret == -EIOCBQUEUED) {
|
||||
fsstack_copy_inode_size(fuse_inode, shortcircuit_inode);
|
||||
fsstack_copy_attr_times(fuse_inode, shortcircuit_inode);
|
||||
}
|
||||
} else {
|
||||
if (!lower_file->f_op->read_iter)
|
||||
goto out;
|
||||
|
||||
ret = call_read_iter(lower_file, iocb, iter);
|
||||
if (ret >= 0 || ret == -EIOCBQUEUED)
|
||||
fsstack_copy_attr_atime(fuse_inode, shortcircuit_inode);
|
||||
}
|
||||
|
||||
out:
|
||||
iocb->ki_filp = fuse_filp;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t fuse_shortcircuit_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
return fuse_shortcircuit_read_write_iter(iocb, to, 0);
|
||||
}
|
||||
|
||||
ssize_t fuse_shortcircuit_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
return fuse_shortcircuit_read_write_iter(iocb, from, 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;
|
||||
}
|
@ -33,3 +33,4 @@ proc-$(CONFIG_PROC_KCORE) += kcore.o
|
||||
proc-$(CONFIG_PROC_VMCORE) += vmcore.o
|
||||
proc-$(CONFIG_PRINTK) += kmsg.o
|
||||
proc-$(CONFIG_PROC_PAGE_MONITOR) += page.o
|
||||
proc-$(CONFIG_FUSE_SHORTCIRCUIT) += fg_uid.o
|
||||
|
@ -238,6 +238,8 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
const struct cred *saved_cred = NULL;
|
||||
struct fuse_package *fp = current->fpack;
|
||||
char *iname;
|
||||
|
||||
/* don't open unhashed/deleted files */
|
||||
if (d_unhashed(dentry)) {
|
||||
@ -257,6 +259,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
file->f_mode |= FMODE_NONMAPPABLE;
|
||||
file->private_data =
|
||||
kmem_cache_zalloc(kmem_file_info_pool, GFP_KERNEL);
|
||||
if (!SDCARDFS_F(file)) {
|
||||
@ -277,6 +280,15 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
|
||||
}
|
||||
} else {
|
||||
sdcardfs_set_lower_file(file, lower_file);
|
||||
if (!err && fp && fp->fuse_open_req && !fp->filp && fp->iname) {
|
||||
iname = inode_name(inode);
|
||||
if (iname && !strcasecmp(iname, fp->iname)) {
|
||||
fp->filp = file;
|
||||
get_file(file);
|
||||
}
|
||||
if (iname)
|
||||
__putname(iname);
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
@ -352,6 +364,11 @@ static int sdcardfs_fasync(int fd, struct file *file, int flag)
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct file *sdcardfs_get_lower_file(struct file *f)
|
||||
{
|
||||
return sdcardfs_lower_file(f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sdcardfs cannot use generic_file_llseek as ->llseek, because it would
|
||||
* only set the offset of the upper file. So we have to implement our
|
||||
@ -448,6 +465,7 @@ const struct file_operations sdcardfs_main_fops = {
|
||||
.release = sdcardfs_file_release,
|
||||
.fsync = sdcardfs_fsync,
|
||||
.fasync = sdcardfs_fasync,
|
||||
.get_lower_file = sdcardfs_get_lower_file,
|
||||
.read_iter = sdcardfs_read_iter,
|
||||
.write_iter = sdcardfs_write_iter,
|
||||
};
|
||||
@ -465,5 +483,6 @@ const struct file_operations sdcardfs_dir_fops = {
|
||||
.release = sdcardfs_file_release,
|
||||
.flush = sdcardfs_flush,
|
||||
.fsync = sdcardfs_fsync,
|
||||
.get_lower_file = sdcardfs_get_lower_file,
|
||||
.fasync = sdcardfs_fasync,
|
||||
};
|
||||
|
@ -153,6 +153,9 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
|
||||
/* File is stream-like */
|
||||
#define FMODE_STREAM ((__force fmode_t)0x200000)
|
||||
|
||||
/* File hasn't page cache and can't be mmaped, for stackable filesystem */
|
||||
#define FMODE_NONMAPPABLE ((__force fmode_t)0x400000)
|
||||
|
||||
/* File was opened by fanotify and shouldn't generate fanotify events */
|
||||
#define FMODE_NONOTIFY ((__force fmode_t)0x4000000)
|
||||
|
||||
|
@ -1392,6 +1392,10 @@ struct task_struct {
|
||||
struct task_struct *simple_lmk_next;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FUSE_SHORTCIRCUIT
|
||||
int fuse_boost;
|
||||
#endif
|
||||
|
||||
struct {
|
||||
struct work_struct work;
|
||||
atomic_t running;
|
||||
@ -1404,6 +1408,8 @@ struct task_struct {
|
||||
*/
|
||||
randomized_struct_fields_end
|
||||
|
||||
struct fuse_package *fpack;
|
||||
|
||||
/* CPU-specific state of this task: */
|
||||
struct thread_struct thread;
|
||||
|
||||
@ -1415,6 +1421,12 @@ struct task_struct {
|
||||
*/
|
||||
};
|
||||
|
||||
struct fuse_package {
|
||||
bool fuse_open_req;
|
||||
struct file *filp;
|
||||
char *iname;
|
||||
};
|
||||
|
||||
static inline struct pid *task_pid(struct task_struct *task)
|
||||
{
|
||||
return task->pids[PIDTYPE_PID].pid;
|
||||
|
@ -269,6 +269,7 @@ struct fuse_file_lock {
|
||||
#define FUSE_PARALLEL_DIROPS (1 << 18)
|
||||
#define FUSE_HANDLE_KILLPRIV (1 << 19)
|
||||
#define FUSE_POSIX_ACL (1 << 20)
|
||||
#define FUSE_SHORTCIRCUIT (1 << 30)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
|
@ -185,6 +185,14 @@ void release_task(struct task_struct *p)
|
||||
{
|
||||
struct task_struct *leader;
|
||||
int zap_leader;
|
||||
|
||||
if (p->fpack) {
|
||||
if (p->fpack->iname)
|
||||
__putname(p->fpack->iname);
|
||||
kfree(p->fpack);
|
||||
p->fpack = NULL;
|
||||
}
|
||||
|
||||
repeat:
|
||||
/* don't need to get the RCU readlock here - the process is dead and
|
||||
* can't be modifying its own credentials. But shut RCU-lockdep up */
|
||||
|
@ -1895,6 +1895,8 @@ static __latent_entropy struct task_struct *copy_process(
|
||||
p->sequential_io_avg = 0;
|
||||
#endif
|
||||
|
||||
p->fpack = NULL;
|
||||
|
||||
/* Perform scheduler related setup. Assign this task to a CPU. */
|
||||
retval = sched_fork(clone_flags, p);
|
||||
if (retval)
|
||||
|
@ -40,6 +40,10 @@
|
||||
#include "tune.h"
|
||||
#include "walt.h"
|
||||
|
||||
#ifdef CONFIG_FUSE_SHORTCIRCUIT
|
||||
extern unsigned int ht_fuse_boost;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static inline bool get_rtg_status(struct task_struct *p);
|
||||
static inline bool task_fits_max(struct task_struct *p, int cpu);
|
||||
@ -7639,6 +7643,12 @@ static int get_start_cpu(struct task_struct *p)
|
||||
if (start_cpu == -1 || start_cpu == rd->max_cap_orig_cpu)
|
||||
return start_cpu;
|
||||
|
||||
#ifdef CONFIG_FUSE_SHORTCIRCUIT
|
||||
if (ht_fuse_boost && p->fuse_boost)
|
||||
return rd->mid_cap_orig_cpu == -1 ?
|
||||
rd->max_cap_orig_cpu : rd->mid_cap_orig_cpu;
|
||||
#endif
|
||||
|
||||
if (start_cpu == rd->min_cap_orig_cpu &&
|
||||
!task_demand_fits(p, start_cpu)) {
|
||||
start_cpu = rd->mid_cap_orig_cpu == -1 ?
|
||||
|
Loading…
x
Reference in New Issue
Block a user