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:
LibXZR 2021-05-03 19:22:11 +08:00 committed by Forenche
parent d88efacc59
commit 0b49172223
No known key found for this signature in database
GPG Key ID: 1337D655BAFE85E2
18 changed files with 489 additions and 6 deletions

View File

@ -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

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 xattr.o acl.o
fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.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>
@ -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)

View File

@ -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) {

View File

@ -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);

View 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 */

View 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 */

View File

@ -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
View 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;
}

View File

@ -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

View File

@ -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,
};

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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)

View File

@ -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 ?

View File

@ -1465,6 +1465,9 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
if (!len)
return -EINVAL;
while (file && (file->f_mode & FMODE_NONMAPPABLE))
file = file->f_op->get_lower_file(file);
/*
* Does the application expect PROT_READ to imply PROT_EXEC?
*