Commit 9eead2a8 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: add fusectl interface to max_background
  fuse: limit user-specified values of max background requests
  fuse: use drop_nlink() instead of direct nlink manipulation
  fuse: document protocol version negotiation
  fuse: make the number of max background requests and congestion threshold tunable
parents 6f130478 79a9d994
...@@ -61,6 +61,121 @@ static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf, ...@@ -61,6 +61,121 @@ static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf,
return simple_read_from_buffer(buf, len, ppos, tmp, size); return simple_read_from_buffer(buf, len, ppos, tmp, size);
} }
static ssize_t fuse_conn_limit_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos, unsigned val)
{
char tmp[32];
size_t size = sprintf(tmp, "%u\n", val);
return simple_read_from_buffer(buf, len, ppos, tmp, size);
}
static ssize_t fuse_conn_limit_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos, unsigned *val,
unsigned global_limit)
{
unsigned long t;
char tmp[32];
unsigned limit = (1 << 16) - 1;
int err;
if (*ppos || count >= sizeof(tmp) - 1)
return -EINVAL;
if (copy_from_user(tmp, buf, count))
return -EINVAL;
tmp[count] = '\0';
err = strict_strtoul(tmp, 0, &t);
if (err)
return err;
if (!capable(CAP_SYS_ADMIN))
limit = min(limit, global_limit);
if (t > limit)
return -EINVAL;
*val = t;
return count;
}
static ssize_t fuse_conn_max_background_read(struct file *file,
char __user *buf, size_t len,
loff_t *ppos)
{
struct fuse_conn *fc;
unsigned val;
fc = fuse_ctl_file_conn_get(file);
if (!fc)
return 0;
val = fc->max_background;
fuse_conn_put(fc);
return fuse_conn_limit_read(file, buf, len, ppos, val);
}
static ssize_t fuse_conn_max_background_write(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned val;
ssize_t ret;
ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
max_user_bgreq);
if (ret > 0) {
struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
if (fc) {
fc->max_background = val;
fuse_conn_put(fc);
}
}
return ret;
}
static ssize_t fuse_conn_congestion_threshold_read(struct file *file,
char __user *buf, size_t len,
loff_t *ppos)
{
struct fuse_conn *fc;
unsigned val;
fc = fuse_ctl_file_conn_get(file);
if (!fc)
return 0;
val = fc->congestion_threshold;
fuse_conn_put(fc);
return fuse_conn_limit_read(file, buf, len, ppos, val);
}
static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned val;
ssize_t ret;
ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
max_user_congthresh);
if (ret > 0) {
struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
if (fc) {
fc->congestion_threshold = val;
fuse_conn_put(fc);
}
}
return ret;
}
static const struct file_operations fuse_ctl_abort_ops = { static const struct file_operations fuse_ctl_abort_ops = {
.open = nonseekable_open, .open = nonseekable_open,
.write = fuse_conn_abort_write, .write = fuse_conn_abort_write,
...@@ -71,6 +186,18 @@ static const struct file_operations fuse_ctl_waiting_ops = { ...@@ -71,6 +186,18 @@ static const struct file_operations fuse_ctl_waiting_ops = {
.read = fuse_conn_waiting_read, .read = fuse_conn_waiting_read,
}; };
static const struct file_operations fuse_conn_max_background_ops = {
.open = nonseekable_open,
.read = fuse_conn_max_background_read,
.write = fuse_conn_max_background_write,
};
static const struct file_operations fuse_conn_congestion_threshold_ops = {
.open = nonseekable_open,
.read = fuse_conn_congestion_threshold_read,
.write = fuse_conn_congestion_threshold_write,
};
static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, static struct dentry *fuse_ctl_add_dentry(struct dentry *parent,
struct fuse_conn *fc, struct fuse_conn *fc,
const char *name, const char *name,
...@@ -127,9 +254,14 @@ int fuse_ctl_add_conn(struct fuse_conn *fc) ...@@ -127,9 +254,14 @@ int fuse_ctl_add_conn(struct fuse_conn *fc)
goto err; goto err;
if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1, if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1,
NULL, &fuse_ctl_waiting_ops) || NULL, &fuse_ctl_waiting_ops) ||
!fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1, !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1,
NULL, &fuse_ctl_abort_ops)) NULL, &fuse_ctl_abort_ops) ||
!fuse_ctl_add_dentry(parent, fc, "max_background", S_IFREG | 0600,
1, NULL, &fuse_conn_max_background_ops) ||
!fuse_ctl_add_dentry(parent, fc, "congestion_threshold",
S_IFREG | 0600, 1, NULL,
&fuse_conn_congestion_threshold_ops))
goto err; goto err;
return 0; return 0;
...@@ -156,7 +288,7 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc) ...@@ -156,7 +288,7 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc)
d_drop(dentry); d_drop(dentry);
dput(dentry); dput(dentry);
} }
fuse_control_sb->s_root->d_inode->i_nlink--; drop_nlink(fuse_control_sb->s_root->d_inode);
} }
static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent) static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent)
......
...@@ -250,7 +250,7 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req) ...@@ -250,7 +250,7 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
static void flush_bg_queue(struct fuse_conn *fc) static void flush_bg_queue(struct fuse_conn *fc)
{ {
while (fc->active_background < FUSE_MAX_BACKGROUND && while (fc->active_background < fc->max_background &&
!list_empty(&fc->bg_queue)) { !list_empty(&fc->bg_queue)) {
struct fuse_req *req; struct fuse_req *req;
...@@ -280,11 +280,11 @@ __releases(&fc->lock) ...@@ -280,11 +280,11 @@ __releases(&fc->lock)
list_del(&req->intr_entry); list_del(&req->intr_entry);
req->state = FUSE_REQ_FINISHED; req->state = FUSE_REQ_FINISHED;
if (req->background) { if (req->background) {
if (fc->num_background == FUSE_MAX_BACKGROUND) { if (fc->num_background == fc->max_background) {
fc->blocked = 0; fc->blocked = 0;
wake_up_all(&fc->blocked_waitq); wake_up_all(&fc->blocked_waitq);
} }
if (fc->num_background == FUSE_CONGESTION_THRESHOLD && if (fc->num_background == fc->congestion_threshold &&
fc->connected && fc->bdi_initialized) { fc->connected && fc->bdi_initialized) {
clear_bdi_congested(&fc->bdi, BLK_RW_SYNC); clear_bdi_congested(&fc->bdi, BLK_RW_SYNC);
clear_bdi_congested(&fc->bdi, BLK_RW_ASYNC); clear_bdi_congested(&fc->bdi, BLK_RW_ASYNC);
...@@ -410,9 +410,9 @@ static void fuse_request_send_nowait_locked(struct fuse_conn *fc, ...@@ -410,9 +410,9 @@ static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
{ {
req->background = 1; req->background = 1;
fc->num_background++; fc->num_background++;
if (fc->num_background == FUSE_MAX_BACKGROUND) if (fc->num_background == fc->max_background)
fc->blocked = 1; fc->blocked = 1;
if (fc->num_background == FUSE_CONGESTION_THRESHOLD && if (fc->num_background == fc->congestion_threshold &&
fc->bdi_initialized) { fc->bdi_initialized) {
set_bdi_congested(&fc->bdi, BLK_RW_SYNC); set_bdi_congested(&fc->bdi, BLK_RW_SYNC);
set_bdi_congested(&fc->bdi, BLK_RW_ASYNC); set_bdi_congested(&fc->bdi, BLK_RW_ASYNC);
......
...@@ -25,12 +25,6 @@ ...@@ -25,12 +25,6 @@
/** Max number of pages that can be used in a single read request */ /** Max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32 #define FUSE_MAX_PAGES_PER_REQ 32
/** Maximum number of outstanding background requests */
#define FUSE_MAX_BACKGROUND 12
/** Congestion starts at 75% of maximum */
#define FUSE_CONGESTION_THRESHOLD (FUSE_MAX_BACKGROUND * 75 / 100)
/** Bias for fi->writectr, meaning new writepages must not be sent */ /** Bias for fi->writectr, meaning new writepages must not be sent */
#define FUSE_NOWRITE INT_MIN #define FUSE_NOWRITE INT_MIN
...@@ -38,7 +32,7 @@ ...@@ -38,7 +32,7 @@
#define FUSE_NAME_MAX 1024 #define FUSE_NAME_MAX 1024
/** Number of dentries for each connection in the control filesystem */ /** Number of dentries for each connection in the control filesystem */
#define FUSE_CTL_NUM_DENTRIES 3 #define FUSE_CTL_NUM_DENTRIES 5
/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
module will check permissions based on the file mode. Otherwise no module will check permissions based on the file mode. Otherwise no
...@@ -55,6 +49,10 @@ extern struct list_head fuse_conn_list; ...@@ -55,6 +49,10 @@ extern struct list_head fuse_conn_list;
/** Global mutex protecting fuse_conn_list and the control filesystem */ /** Global mutex protecting fuse_conn_list and the control filesystem */
extern struct mutex fuse_mutex; extern struct mutex fuse_mutex;
/** Module parameters */
extern unsigned max_user_bgreq;
extern unsigned max_user_congthresh;
/** FUSE inode */ /** FUSE inode */
struct fuse_inode { struct fuse_inode {
/** Inode data */ /** Inode data */
...@@ -349,6 +347,12 @@ struct fuse_conn { ...@@ -349,6 +347,12 @@ struct fuse_conn {
/** rbtree of fuse_files waiting for poll events indexed by ph */ /** rbtree of fuse_files waiting for poll events indexed by ph */
struct rb_root polled_files; struct rb_root polled_files;
/** Maximum number of outstanding background requests */
unsigned max_background;
/** Number of background requests at which congestion starts */
unsigned congestion_threshold;
/** Number of requests currently in the background */ /** Number of requests currently in the background */
unsigned num_background; unsigned num_background;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/statfs.h> #include <linux/statfs.h>
#include <linux/random.h> #include <linux/random.h>
...@@ -28,10 +29,34 @@ static struct kmem_cache *fuse_inode_cachep; ...@@ -28,10 +29,34 @@ static struct kmem_cache *fuse_inode_cachep;
struct list_head fuse_conn_list; struct list_head fuse_conn_list;
DEFINE_MUTEX(fuse_mutex); DEFINE_MUTEX(fuse_mutex);
static int set_global_limit(const char *val, struct kernel_param *kp);
unsigned max_user_bgreq;
module_param_call(max_user_bgreq, set_global_limit, param_get_uint,
&max_user_bgreq, 0644);
__MODULE_PARM_TYPE(max_user_bgreq, "uint");
MODULE_PARM_DESC(max_user_bgreq,
"Global limit for the maximum number of backgrounded requests an "
"unprivileged user can set");
unsigned max_user_congthresh;
module_param_call(max_user_congthresh, set_global_limit, param_get_uint,
&max_user_congthresh, 0644);
__MODULE_PARM_TYPE(max_user_congthresh, "uint");
MODULE_PARM_DESC(max_user_congthresh,
"Global limit for the maximum congestion threshold an "
"unprivileged user can set");
#define FUSE_SUPER_MAGIC 0x65735546 #define FUSE_SUPER_MAGIC 0x65735546
#define FUSE_DEFAULT_BLKSIZE 512 #define FUSE_DEFAULT_BLKSIZE 512
/** Maximum number of outstanding background requests */
#define FUSE_DEFAULT_MAX_BACKGROUND 12
/** Congestion starts at 75% of maximum */
#define FUSE_DEFAULT_CONGESTION_THRESHOLD (FUSE_DEFAULT_MAX_BACKGROUND * 3 / 4)
struct fuse_mount_data { struct fuse_mount_data {
int fd; int fd;
unsigned rootmode; unsigned rootmode;
...@@ -517,6 +542,8 @@ void fuse_conn_init(struct fuse_conn *fc) ...@@ -517,6 +542,8 @@ void fuse_conn_init(struct fuse_conn *fc)
INIT_LIST_HEAD(&fc->bg_queue); INIT_LIST_HEAD(&fc->bg_queue);
INIT_LIST_HEAD(&fc->entry); INIT_LIST_HEAD(&fc->entry);
atomic_set(&fc->num_waiting, 0); atomic_set(&fc->num_waiting, 0);
fc->max_background = FUSE_DEFAULT_MAX_BACKGROUND;
fc->congestion_threshold = FUSE_DEFAULT_CONGESTION_THRESHOLD;
fc->khctr = 0; fc->khctr = 0;
fc->polled_files = RB_ROOT; fc->polled_files = RB_ROOT;
fc->reqctr = 0; fc->reqctr = 0;
...@@ -727,6 +754,54 @@ static const struct super_operations fuse_super_operations = { ...@@ -727,6 +754,54 @@ static const struct super_operations fuse_super_operations = {
.show_options = fuse_show_options, .show_options = fuse_show_options,
}; };
static void sanitize_global_limit(unsigned *limit)
{
if (*limit == 0)
*limit = ((num_physpages << PAGE_SHIFT) >> 13) /
sizeof(struct fuse_req);
if (*limit >= 1 << 16)
*limit = (1 << 16) - 1;
}
static int set_global_limit(const char *val, struct kernel_param *kp)
{
int rv;
rv = param_set_uint(val, kp);
if (rv)
return rv;
sanitize_global_limit((unsigned *)kp->arg);
return 0;
}
static void process_init_limits(struct fuse_conn *fc, struct fuse_init_out *arg)
{
int cap_sys_admin = capable(CAP_SYS_ADMIN);
if (arg->minor < 13)
return;
sanitize_global_limit(&max_user_bgreq);
sanitize_global_limit(&max_user_congthresh);
if (arg->max_background) {
fc->max_background = arg->max_background;
if (!cap_sys_admin && fc->max_background > max_user_bgreq)
fc->max_background = max_user_bgreq;
}
if (arg->congestion_threshold) {
fc->congestion_threshold = arg->congestion_threshold;
if (!cap_sys_admin &&
fc->congestion_threshold > max_user_congthresh)
fc->congestion_threshold = max_user_congthresh;
}
}
static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
{ {
struct fuse_init_out *arg = &req->misc.init_out; struct fuse_init_out *arg = &req->misc.init_out;
...@@ -736,6 +811,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) ...@@ -736,6 +811,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
else { else {
unsigned long ra_pages; unsigned long ra_pages;
process_init_limits(fc, arg);
if (arg->minor >= 6) { if (arg->minor >= 6) {
ra_pages = arg->max_readahead / PAGE_CACHE_SIZE; ra_pages = arg->max_readahead / PAGE_CACHE_SIZE;
if (arg->flags & FUSE_ASYNC_READ) if (arg->flags & FUSE_ASYNC_READ)
...@@ -1150,6 +1227,9 @@ static int __init fuse_init(void) ...@@ -1150,6 +1227,9 @@ static int __init fuse_init(void)
if (res) if (res)
goto err_sysfs_cleanup; goto err_sysfs_cleanup;
sanitize_global_limit(&max_user_bgreq);
sanitize_global_limit(&max_user_congthresh);
return 0; return 0;
err_sysfs_cleanup: err_sysfs_cleanup:
......
...@@ -30,6 +30,10 @@ ...@@ -30,6 +30,10 @@
* - add umask flag to input argument of open, mknod and mkdir * - add umask flag to input argument of open, mknod and mkdir
* - add notification messages for invalidation of inodes and * - add notification messages for invalidation of inodes and
* directory entries * directory entries
*
* 7.13
* - make max number of background requests and congestion threshold
* tunables
*/ */
#ifndef _LINUX_FUSE_H #ifndef _LINUX_FUSE_H
...@@ -37,11 +41,31 @@ ...@@ -37,11 +41,31 @@
#include <linux/types.h> #include <linux/types.h>
/*
* Version negotiation:
*
* Both the kernel and userspace send the version they support in the
* INIT request and reply respectively.
*
* If the major versions match then both shall use the smallest
* of the two minor versions for communication.
*
* If the kernel supports a larger major version, then userspace shall
* reply with the major version it supports, ignore the rest of the
* INIT message and expect a new INIT message from the kernel with a
* matching major version.
*
* If the library supports a larger major version, then it shall fall
* back to the major protocol version sent by the kernel for
* communication and reply with that major version (and an arbitrary
* supported minor version).
*/
/** Version number of this interface */ /** Version number of this interface */
#define FUSE_KERNEL_VERSION 7 #define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */ /** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 12 #define FUSE_KERNEL_MINOR_VERSION 13
/** The node ID of the root inode */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
...@@ -427,7 +451,8 @@ struct fuse_init_out { ...@@ -427,7 +451,8 @@ struct fuse_init_out {
__u32 minor; __u32 minor;
__u32 max_readahead; __u32 max_readahead;
__u32 flags; __u32 flags;
__u32 unused; __u16 max_background;
__u16 congestion_threshold;
__u32 max_write; __u32 max_write;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment