diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig index 76f09ce7e5b2..8909c106a86e 100644 --- a/fs/fuse/Kconfig +++ b/fs/fuse/Kconfig @@ -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 diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index 60da84a86dab..0d4c2c4a6bfa 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -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 diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index a1e31d6aba04..cb3838e6e32c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -7,6 +7,7 @@ */ #include "fuse_i.h" +#include "fuse_shortcircuit.h" #include #include @@ -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 +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); @@ -1327,6 +1400,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)) @@ -1856,6 +1947,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; @@ -1889,6 +1985,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); @@ -1928,6 +2026,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) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index d84819c9174c..09a937f1783a 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -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) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 25458a78d24f..017fa2cf2d66 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -7,6 +7,7 @@ */ #include "fuse_i.h" +#include "fuse_shortcircuit.h" #include #include @@ -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); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 74bae9703ee6..87aead627b8f 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -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 */ diff --git a/fs/fuse/fuse_shortcircuit.h b/fs/fuse/fuse_shortcircuit.h new file mode 100644 index 000000000000..b326ef9d798f --- /dev/null +++ b/fs/fuse/fuse_shortcircuit.h @@ -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 +#include + +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 */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 97138986f02b..95d24c358abc 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -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); diff --git a/fs/fuse/shortcircuit.c b/fs/fuse/shortcircuit.c new file mode 100644 index 000000000000..8ebd2f78e8b8 --- /dev/null +++ b/fs/fuse/shortcircuit.c @@ -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 +#include + +#include +#include + +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; +} diff --git a/fs/proc/Makefile b/fs/proc/Makefile index d8dcb188db6d..090607087d65 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -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 diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 70134f3f296c..541341be52ae 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -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, }; diff --git a/include/linux/fs.h b/include/linux/fs.h index 179748d9c086..81152b6ae6ed 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -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) diff --git a/include/linux/sched.h b/include/linux/sched.h index 6d13d920394a..347a09c94813 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -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; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 8fb4d4259e9e..52cba55cce27 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -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 diff --git a/kernel/exit.c b/kernel/exit.c index 09ed26021ec9..828c6c442ebf 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -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 */ diff --git a/kernel/fork.c b/kernel/fork.c index cc162d44c536..efe04e6836d3 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -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) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index ac6fae6ce30b..0ddc2cfc7347 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -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 ? diff --git a/mm/mmap.c b/mm/mmap.c index ae196aecefaa..ef631ca0c75d 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -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? *