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

Merge tag 'fuse-update-4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse

Pull fuse updates from Miklos Szeredi:
 "As well as the usual bug fixes, this adds the following new features:

   - cached readdir and readlink

   - max I/O size increased from 128k to 1M

   - improved performance and scalability of request queues

   - copy_file_range support

  The only non-fuse bits are trivial cleanups of macros in
  <linux/bitops.h>"

* tag 'fuse-update-4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: (31 commits)
  fuse: enable caching of symlinks
  fuse: only invalidate atime in direct read
  fuse: don't need GETATTR after every READ
  fuse: allow fine grained attr cache invaldation
  bitops: protect variables in bit_clear_unless() macro
  bitops: protect variables in set_mask_bits() macro
  fuse: realloc page array
  fuse: add max_pages to init_out
  fuse: allocate page array more efficiently
  fuse: reduce size of struct fuse_inode
  fuse: use iversion for readdir cache verification
  fuse: use mtime for readdir cache verification
  fuse: add readdir cache version
  fuse: allow using readdir cache
  fuse: allow caching readdir
  fuse: extract fuse_emit() helper
  fuse: add FOPEN_CACHE_DIR
  fuse: split out readdir.c
  fuse: Use hash table to link processing request
  fuse: kill req->intr_unique
  ...
parents 31990f0f 5571f1e6
...@@ -5,4 +5,4 @@ ...@@ -5,4 +5,4 @@
obj-$(CONFIG_FUSE_FS) += fuse.o obj-$(CONFIG_FUSE_FS) += fuse.o
obj-$(CONFIG_CUSE) += cuse.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 readdir.o
...@@ -107,7 +107,7 @@ static ssize_t fuse_conn_max_background_read(struct file *file, ...@@ -107,7 +107,7 @@ static ssize_t fuse_conn_max_background_read(struct file *file,
if (!fc) if (!fc)
return 0; return 0;
val = fc->max_background; val = READ_ONCE(fc->max_background);
fuse_conn_put(fc); fuse_conn_put(fc);
return fuse_conn_limit_read(file, buf, len, ppos, val); return fuse_conn_limit_read(file, buf, len, ppos, val);
...@@ -125,7 +125,12 @@ static ssize_t fuse_conn_max_background_write(struct file *file, ...@@ -125,7 +125,12 @@ static ssize_t fuse_conn_max_background_write(struct file *file,
if (ret > 0) { if (ret > 0) {
struct fuse_conn *fc = fuse_ctl_file_conn_get(file); struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
if (fc) { if (fc) {
spin_lock(&fc->bg_lock);
fc->max_background = val; fc->max_background = val;
fc->blocked = fc->num_background >= fc->max_background;
if (!fc->blocked)
wake_up(&fc->blocked_waitq);
spin_unlock(&fc->bg_lock);
fuse_conn_put(fc); fuse_conn_put(fc);
} }
} }
...@@ -144,7 +149,7 @@ static ssize_t fuse_conn_congestion_threshold_read(struct file *file, ...@@ -144,7 +149,7 @@ static ssize_t fuse_conn_congestion_threshold_read(struct file *file,
if (!fc) if (!fc)
return 0; return 0;
val = fc->congestion_threshold; val = READ_ONCE(fc->congestion_threshold);
fuse_conn_put(fc); fuse_conn_put(fc);
return fuse_conn_limit_read(file, buf, len, ppos, val); return fuse_conn_limit_read(file, buf, len, ppos, val);
...@@ -155,18 +160,31 @@ static ssize_t fuse_conn_congestion_threshold_write(struct file *file, ...@@ -155,18 +160,31 @@ static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
unsigned uninitialized_var(val); unsigned uninitialized_var(val);
struct fuse_conn *fc;
ssize_t ret; ssize_t ret;
ret = fuse_conn_limit_write(file, buf, count, ppos, &val, ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
max_user_congthresh); max_user_congthresh);
if (ret > 0) { if (ret <= 0)
struct fuse_conn *fc = fuse_ctl_file_conn_get(file); goto out;
if (fc) { fc = fuse_ctl_file_conn_get(file);
if (!fc)
goto out;
spin_lock(&fc->bg_lock);
fc->congestion_threshold = val; fc->congestion_threshold = val;
fuse_conn_put(fc); if (fc->sb) {
if (fc->num_background < fc->congestion_threshold) {
clear_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
clear_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
} else {
set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
} }
} }
spin_unlock(&fc->bg_lock);
fuse_conn_put(fc);
out:
return ret; return ret;
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -28,8 +28,11 @@ ...@@ -28,8 +28,11 @@
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
/** Max number of pages that can be used in a single read request */ /** Default max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32 #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
/** Maximum of max_pages received in init_out */
#define FUSE_MAX_MAX_PAGES 256
/** 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
...@@ -77,6 +80,9 @@ struct fuse_inode { ...@@ -77,6 +80,9 @@ struct fuse_inode {
/** Time in jiffies until the file attributes are valid */ /** Time in jiffies until the file attributes are valid */
u64 i_time; u64 i_time;
/* Which attributes are invalid */
u32 inval_mask;
/** The sticky bit in inode->i_mode may have been removed, so /** The sticky bit in inode->i_mode may have been removed, so
preserve the original mode */ preserve the original mode */
umode_t orig_i_mode; umode_t orig_i_mode;
...@@ -87,21 +93,51 @@ struct fuse_inode { ...@@ -87,21 +93,51 @@ struct fuse_inode {
/** Version of last attribute change */ /** Version of last attribute change */
u64 attr_version; u64 attr_version;
/** Files usable in writepage. Protected by fc->lock */ union {
/* Write related fields (regular file only) */
struct {
/* Files usable in writepage. Protected by fc->lock */
struct list_head write_files; struct list_head write_files;
/** Writepages pending on truncate or fsync */ /* Writepages pending on truncate or fsync */
struct list_head queued_writes; struct list_head queued_writes;
/** Number of sent writes, a negative bias (FUSE_NOWRITE) /* Number of sent writes, a negative bias
* means more writes are blocked */ * (FUSE_NOWRITE) means more writes are blocked */
int writectr; int writectr;
/** Waitq for writepage completion */ /* Waitq for writepage completion */
wait_queue_head_t page_waitq; wait_queue_head_t page_waitq;
/** List of writepage requestst (pending or sent) */ /* List of writepage requestst (pending or sent) */
struct list_head writepages; struct list_head writepages;
};
/* readdir cache (directory only) */
struct {
/* true if fully cached */
bool cached;
/* size of cache */
loff_t size;
/* position at end of cache (position of next entry) */
loff_t pos;
/* version of the cache */
u64 version;
/* modification time of directory when cache was
* started */
struct timespec64 mtime;
/* iversion of directory when cache was started */
u64 iversion;
/* protects above fields */
spinlock_t lock;
} rdc;
};
/** Miscellaneous bits describing inode state */ /** Miscellaneous bits describing inode state */
unsigned long state; unsigned long state;
...@@ -148,6 +184,25 @@ struct fuse_file { ...@@ -148,6 +184,25 @@ struct fuse_file {
/** Entry on inode's write_files list */ /** Entry on inode's write_files list */
struct list_head write_entry; struct list_head write_entry;
/* Readdir related */
struct {
/*
* Protects below fields against (crazy) parallel readdir on
* same open file. Uncontended in the normal case.
*/
struct mutex lock;
/* Dir stream position */
loff_t pos;
/* Offset in cache */
loff_t cache_off;
/* Version of cache we are reading */
u64 version;
} readdir;
/** RB node to be linked on fuse_conn->polled_files */ /** RB node to be linked on fuse_conn->polled_files */
struct rb_node polled_node; struct rb_node polled_node;
...@@ -311,9 +366,6 @@ struct fuse_req { ...@@ -311,9 +366,6 @@ struct fuse_req {
/** refcount */ /** refcount */
refcount_t count; refcount_t count;
/** Unique ID for the interrupt request */
u64 intr_unique;
/* Request flags, updated with test/set/clear_bit() */ /* Request flags, updated with test/set/clear_bit() */
unsigned long flags; unsigned long flags;
...@@ -411,6 +463,9 @@ struct fuse_iqueue { ...@@ -411,6 +463,9 @@ struct fuse_iqueue {
struct fasync_struct *fasync; struct fasync_struct *fasync;
}; };
#define FUSE_PQ_HASH_BITS 8
#define FUSE_PQ_HASH_SIZE (1 << FUSE_PQ_HASH_BITS)
struct fuse_pqueue { struct fuse_pqueue {
/** Connection established */ /** Connection established */
unsigned connected; unsigned connected;
...@@ -418,8 +473,8 @@ struct fuse_pqueue { ...@@ -418,8 +473,8 @@ struct fuse_pqueue {
/** Lock protecting accessess to members of this structure */ /** Lock protecting accessess to members of this structure */
spinlock_t lock; spinlock_t lock;
/** The list of requests being processed */ /** Hash table of requests being processed */
struct list_head processing; struct list_head *processing;
/** The list of requests under I/O */ /** The list of requests under I/O */
struct list_head io; struct list_head io;
...@@ -476,6 +531,9 @@ struct fuse_conn { ...@@ -476,6 +531,9 @@ struct fuse_conn {
/** Maximum write size */ /** Maximum write size */
unsigned max_write; unsigned max_write;
/** Maxmum number of pages that can be used in a single request */
unsigned int max_pages;
/** Input queue */ /** Input queue */
struct fuse_iqueue iq; struct fuse_iqueue iq;
...@@ -500,6 +558,10 @@ struct fuse_conn { ...@@ -500,6 +558,10 @@ struct fuse_conn {
/** The list of background requests set aside for later queuing */ /** The list of background requests set aside for later queuing */
struct list_head bg_queue; struct list_head bg_queue;
/** Protects: max_background, congestion_threshold, num_background,
* active_background, bg_queue, blocked */
spinlock_t bg_lock;
/** Flag indicating that INIT reply has been received. Allocating /** Flag indicating that INIT reply has been received. Allocating
* any fuse request will be suspended until the flag is set */ * any fuse request will be suspended until the flag is set */
int initialized; int initialized;
...@@ -551,6 +613,9 @@ struct fuse_conn { ...@@ -551,6 +613,9 @@ struct fuse_conn {
/** handle fs handles killing suid/sgid/cap on write/chown/trunc */ /** handle fs handles killing suid/sgid/cap on write/chown/trunc */
unsigned handle_killpriv:1; unsigned handle_killpriv:1;
/** cache READLINK responses in page cache */
unsigned cache_symlinks:1;
/* /*
* The following bitfields are only for optimization purposes * The following bitfields are only for optimization purposes
* and hence races in setting them will not cause malfunction * and hence races in setting them will not cause malfunction
...@@ -637,6 +702,9 @@ struct fuse_conn { ...@@ -637,6 +702,9 @@ struct fuse_conn {
/** Allow other than the mounter user to access the filesystem ? */ /** Allow other than the mounter user to access the filesystem ? */
unsigned allow_other:1; unsigned allow_other:1;
/** Does the filesystem support copy_file_range? */
unsigned no_copy_file_range:1;
/** The number of requests waiting for completion */ /** The number of requests waiting for completion */
atomic_t num_waiting; atomic_t num_waiting;
...@@ -697,6 +765,11 @@ static inline u64 get_node_id(struct inode *inode) ...@@ -697,6 +765,11 @@ static inline u64 get_node_id(struct inode *inode)
return get_fuse_inode(inode)->nodeid; return get_fuse_inode(inode)->nodeid;
} }
static inline int invalid_nodeid(u64 nodeid)
{
return !nodeid || nodeid == FUSE_ROOT_ID;
}
/** Device operations */ /** Device operations */
extern const struct file_operations fuse_dev_operations; extern const struct file_operations fuse_dev_operations;
...@@ -812,6 +885,10 @@ struct fuse_req *fuse_request_alloc(unsigned npages); ...@@ -812,6 +885,10 @@ struct fuse_req *fuse_request_alloc(unsigned npages);
struct fuse_req *fuse_request_alloc_nofs(unsigned npages); struct fuse_req *fuse_request_alloc_nofs(unsigned npages);
bool fuse_req_realloc_pages(struct fuse_conn *fc, struct fuse_req *req,
gfp_t flags);
/** /**
* Free a request * Free a request
*/ */
...@@ -856,9 +933,7 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args); ...@@ -856,9 +933,7 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args);
* Send a request in the background * Send a request in the background
*/ */
void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req); void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req);
bool fuse_request_queue_background(struct fuse_conn *fc, struct fuse_req *req);
void fuse_request_send_background_locked(struct fuse_conn *fc,
struct fuse_req *req);
/* Abort all requests */ /* Abort all requests */
void fuse_abort_conn(struct fuse_conn *fc, bool is_abort); void fuse_abort_conn(struct fuse_conn *fc, bool is_abort);
...@@ -873,6 +948,9 @@ void fuse_invalidate_entry_cache(struct dentry *entry); ...@@ -873,6 +948,9 @@ void fuse_invalidate_entry_cache(struct dentry *entry);
void fuse_invalidate_atime(struct inode *inode); void fuse_invalidate_atime(struct inode *inode);
u64 entry_attr_timeout(struct fuse_entry_out *o);
void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o);
/** /**
* Acquire reference to fuse_conn * Acquire reference to fuse_conn
*/ */
...@@ -992,4 +1070,8 @@ struct posix_acl; ...@@ -992,4 +1070,8 @@ struct posix_acl;
struct posix_acl *fuse_get_acl(struct inode *inode, int type); struct posix_acl *fuse_get_acl(struct inode *inode, int type);
int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type); int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type);
/* readdir.c */
int fuse_readdir(struct file *file, struct dir_context *ctx);
#endif /* _FS_FUSE_I_H */ #endif /* _FS_FUSE_I_H */
...@@ -90,16 +90,12 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) ...@@ -90,16 +90,12 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
fi = get_fuse_inode(inode); fi = get_fuse_inode(inode);
fi->i_time = 0; fi->i_time = 0;
fi->inval_mask = 0;
fi->nodeid = 0; fi->nodeid = 0;
fi->nlookup = 0; fi->nlookup = 0;
fi->attr_version = 0; fi->attr_version = 0;
fi->writectr = 0;
fi->orig_ino = 0; fi->orig_ino = 0;
fi->state = 0; fi->state = 0;
INIT_LIST_HEAD(&fi->write_files);
INIT_LIST_HEAD(&fi->queued_writes);
INIT_LIST_HEAD(&fi->writepages);
init_waitqueue_head(&fi->page_waitq);
mutex_init(&fi->mutex); mutex_init(&fi->mutex);
fi->forget = fuse_alloc_forget(); fi->forget = fuse_alloc_forget();
if (!fi->forget) { if (!fi->forget) {
...@@ -119,8 +115,10 @@ static void fuse_i_callback(struct rcu_head *head) ...@@ -119,8 +115,10 @@ static void fuse_i_callback(struct rcu_head *head)
static void fuse_destroy_inode(struct inode *inode) static void fuse_destroy_inode(struct inode *inode)
{ {
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
BUG_ON(!list_empty(&fi->write_files)); if (S_ISREG(inode->i_mode)) {
BUG_ON(!list_empty(&fi->queued_writes)); WARN_ON(!list_empty(&fi->write_files));
WARN_ON(!list_empty(&fi->queued_writes));
}
mutex_destroy(&fi->mutex); mutex_destroy(&fi->mutex);
kfree(fi->forget); kfree(fi->forget);
call_rcu(&inode->i_rcu, fuse_i_callback); call_rcu(&inode->i_rcu, fuse_i_callback);
...@@ -167,6 +165,7 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, ...@@ -167,6 +165,7 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
fi->attr_version = ++fc->attr_version; fi->attr_version = ++fc->attr_version;
fi->i_time = attr_valid; fi->i_time = attr_valid;
WRITE_ONCE(fi->inval_mask, 0);
inode->i_ino = fuse_squash_ino(attr->ino); inode->i_ino = fuse_squash_ino(attr->ino);
inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
...@@ -594,9 +593,11 @@ static void fuse_iqueue_init(struct fuse_iqueue *fiq) ...@@ -594,9 +593,11 @@ static void fuse_iqueue_init(struct fuse_iqueue *fiq)
static void fuse_pqueue_init(struct fuse_pqueue *fpq) static void fuse_pqueue_init(struct fuse_pqueue *fpq)
{ {
memset(fpq, 0, sizeof(struct fuse_pqueue)); unsigned int i;
spin_lock_init(&fpq->lock); spin_lock_init(&fpq->lock);
INIT_LIST_HEAD(&fpq->processing); for (i = 0; i < FUSE_PQ_HASH_SIZE; i++)
INIT_LIST_HEAD(&fpq->processing[i]);
INIT_LIST_HEAD(&fpq->io); INIT_LIST_HEAD(&fpq->io);
fpq->connected = 1; fpq->connected = 1;
} }
...@@ -605,6 +606,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns) ...@@ -605,6 +606,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns)
{ {
memset(fc, 0, sizeof(*fc)); memset(fc, 0, sizeof(*fc));
spin_lock_init(&fc->lock); spin_lock_init(&fc->lock);
spin_lock_init(&fc->bg_lock);
init_rwsem(&fc->killsb); init_rwsem(&fc->killsb);
refcount_set(&fc->count, 1); refcount_set(&fc->count, 1);
atomic_set(&fc->dev_count, 1); atomic_set(&fc->dev_count, 1);
...@@ -852,6 +854,7 @@ static void process_init_limits(struct fuse_conn *fc, struct fuse_init_out *arg) ...@@ -852,6 +854,7 @@ static void process_init_limits(struct fuse_conn *fc, struct fuse_init_out *arg)
sanitize_global_limit(&max_user_bgreq); sanitize_global_limit(&max_user_bgreq);
sanitize_global_limit(&max_user_congthresh); sanitize_global_limit(&max_user_congthresh);
spin_lock(&fc->bg_lock);
if (arg->max_background) { if (arg->max_background) {
fc->max_background = arg->max_background; fc->max_background = arg->max_background;
...@@ -865,6 +868,7 @@ static void process_init_limits(struct fuse_conn *fc, struct fuse_init_out *arg) ...@@ -865,6 +868,7 @@ static void process_init_limits(struct fuse_conn *fc, struct fuse_init_out *arg)
fc->congestion_threshold > max_user_congthresh) fc->congestion_threshold > max_user_congthresh)
fc->congestion_threshold = max_user_congthresh; fc->congestion_threshold = max_user_congthresh;
} }
spin_unlock(&fc->bg_lock);
} }
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)
...@@ -924,8 +928,15 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) ...@@ -924,8 +928,15 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
fc->posix_acl = 1; fc->posix_acl = 1;
fc->sb->s_xattr = fuse_acl_xattr_handlers; fc->sb->s_xattr = fuse_acl_xattr_handlers;
} }
if (arg->flags & FUSE_CACHE_SYMLINKS)
fc->cache_symlinks = 1;
if (arg->flags & FUSE_ABORT_ERROR) if (arg->flags & FUSE_ABORT_ERROR)
fc->abort_err = 1; fc->abort_err = 1;
if (arg->flags & FUSE_MAX_PAGES) {
fc->max_pages =
min_t(unsigned int, FUSE_MAX_MAX_PAGES,
max_t(unsigned int, arg->max_pages, 1));
}
} else { } else {
ra_pages = fc->max_read / PAGE_SIZE; ra_pages = fc->max_read / PAGE_SIZE;
fc->no_lock = 1; fc->no_lock = 1;
...@@ -957,7 +968,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) ...@@ -957,7 +968,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT | 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_ABORT_ERROR; FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS;
req->in.h.opcode = FUSE_INIT; req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1; req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg); req->in.args[0].size = sizeof(*arg);
...@@ -1022,16 +1033,25 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb) ...@@ -1022,16 +1033,25 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb)
struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc) struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc)
{ {
struct fuse_dev *fud; struct fuse_dev *fud;
struct list_head *pq;
fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL); fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL);
if (fud) { if (!fud)
return NULL;
pq = kcalloc(FUSE_PQ_HASH_SIZE, sizeof(struct list_head), GFP_KERNEL);
if (!pq) {
kfree(fud);
return NULL;
}
fud->pq.processing = pq;
fud->fc = fuse_conn_get(fc); fud->fc = fuse_conn_get(fc);
fuse_pqueue_init(&fud->pq); fuse_pqueue_init(&fud->pq);
spin_lock(&fc->lock); spin_lock(&fc->lock);
list_add_tail(&fud->entry, &fc->devices); list_add_tail(&fud->entry, &fc->devices);
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
}
return fud; return fud;
} }
...@@ -1141,6 +1161,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1141,6 +1161,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
fc->user_id = d.user_id; fc->user_id = d.user_id;
fc->group_id = d.group_id; fc->group_id = d.group_id;
fc->max_read = max_t(unsigned, 4096, d.max_read); fc->max_read = max_t(unsigned, 4096, d.max_read);
fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
/* Used by get_root_inode() */ /* Used by get_root_inode() */
sb->s_fs_info = fc; sb->s_fs_info = fc;
......
This diff is collapsed.
...@@ -236,33 +236,33 @@ static __always_inline void __assign_bit(long nr, volatile unsigned long *addr, ...@@ -236,33 +236,33 @@ static __always_inline void __assign_bit(long nr, volatile unsigned long *addr,
#ifdef __KERNEL__ #ifdef __KERNEL__
#ifndef set_mask_bits #ifndef set_mask_bits
#define set_mask_bits(ptr, _mask, _bits) \ #define set_mask_bits(ptr, mask, bits) \
({ \ ({ \
const typeof(*ptr) mask = (_mask), bits = (_bits); \ const typeof(*(ptr)) mask__ = (mask), bits__ = (bits); \
typeof(*ptr) old, new; \ typeof(*(ptr)) old__, new__; \
\ \
do { \ do { \
old = READ_ONCE(*ptr); \ old__ = READ_ONCE(*(ptr)); \
new = (old & ~mask) | bits; \ new__ = (old__ & ~mask__) | bits__; \
} while (cmpxchg(ptr, old, new) != old); \ } while (cmpxchg(ptr, old__, new__) != old__); \
\ \
new; \ new__; \
}) })
#endif #endif
#ifndef bit_clear_unless #ifndef bit_clear_unless
#define bit_clear_unless(ptr, _clear, _test) \ #define bit_clear_unless(ptr, clear, test) \
({ \ ({ \
const typeof(*ptr) clear = (_clear), test = (_test); \ const typeof(*(ptr)) clear__ = (clear), test__ = (test);\
typeof(*ptr) old, new; \ typeof(*(ptr)) old__, new__; \
\ \
do { \ do { \
old = READ_ONCE(*ptr); \ old__ = READ_ONCE(*(ptr)); \
new = old & ~clear; \ new__ = old__ & ~clear__; \
} while (!(old & test) && \ } while (!(old__ & test__) && \
cmpxchg(ptr, old, new) != old); \ cmpxchg(ptr, old__, new__) != old__); \
\ \
!(old & test); \ !(old__ & test__); \
}) })
#endif #endif
......
...@@ -116,6 +116,12 @@ ...@@ -116,6 +116,12 @@
* *
* 7.27 * 7.27
* - add FUSE_ABORT_ERROR * - add FUSE_ABORT_ERROR
*
* 7.28
* - add FUSE_COPY_FILE_RANGE
* - add FOPEN_CACHE_DIR
* - add FUSE_MAX_PAGES, add max_pages to init_out
* - add FUSE_CACHE_SYMLINKS
*/ */
#ifndef _LINUX_FUSE_H #ifndef _LINUX_FUSE_H
...@@ -151,7 +157,7 @@ ...@@ -151,7 +157,7 @@
#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 27 #define FUSE_KERNEL_MINOR_VERSION 28
/** The node ID of the root inode */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
...@@ -219,10 +225,12 @@ struct fuse_file_lock { ...@@ -219,10 +225,12 @@ struct fuse_file_lock {
* FOPEN_DIRECT_IO: bypass page cache for this open file * FOPEN_DIRECT_IO: bypass page cache for this open file
* FOPEN_KEEP_CACHE: don't invalidate the data cache on open * FOPEN_KEEP_CACHE: don't invalidate the data cache on open
* FOPEN_NONSEEKABLE: the file is not seekable * FOPEN_NONSEEKABLE: the file is not seekable
* FOPEN_CACHE_DIR: allow caching this directory
*/ */
#define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1) #define FOPEN_KEEP_CACHE (1 << 1)
#define FOPEN_NONSEEKABLE (1 << 2) #define FOPEN_NONSEEKABLE (1 << 2)
#define FOPEN_CACHE_DIR (1 << 3)
/** /**
* INIT request/reply flags * INIT request/reply flags
...@@ -249,6 +257,8 @@ struct fuse_file_lock { ...@@ -249,6 +257,8 @@ struct fuse_file_lock {
* FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc * FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc
* FUSE_POSIX_ACL: filesystem supports posix acls * FUSE_POSIX_ACL: filesystem supports posix acls
* FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED
* FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages
* FUSE_CACHE_SYMLINKS: cache READLINK responses
*/ */
#define FUSE_ASYNC_READ (1 << 0) #define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1) #define FUSE_POSIX_LOCKS (1 << 1)
...@@ -272,6 +282,8 @@ struct fuse_file_lock { ...@@ -272,6 +282,8 @@ struct fuse_file_lock {
#define FUSE_HANDLE_KILLPRIV (1 << 19) #define FUSE_HANDLE_KILLPRIV (1 << 19)
#define FUSE_POSIX_ACL (1 << 20) #define FUSE_POSIX_ACL (1 << 20)
#define FUSE_ABORT_ERROR (1 << 21) #define FUSE_ABORT_ERROR (1 << 21)
#define FUSE_MAX_PAGES (1 << 22)
#define FUSE_CACHE_SYMLINKS (1 << 23)
/** /**
* CUSE INIT request/reply flags * CUSE INIT request/reply flags
...@@ -381,6 +393,7 @@ enum fuse_opcode { ...@@ -381,6 +393,7 @@ enum fuse_opcode {
FUSE_READDIRPLUS = 44, FUSE_READDIRPLUS = 44,
FUSE_RENAME2 = 45, FUSE_RENAME2 = 45,
FUSE_LSEEK = 46, FUSE_LSEEK = 46,
FUSE_COPY_FILE_RANGE = 47,
/* CUSE specific operations */ /* CUSE specific operations */
CUSE_INIT = 4096, CUSE_INIT = 4096,
...@@ -610,7 +623,9 @@ struct fuse_init_out { ...@@ -610,7 +623,9 @@ struct fuse_init_out {
uint16_t congestion_threshold; uint16_t congestion_threshold;
uint32_t max_write; uint32_t max_write;
uint32_t time_gran; uint32_t time_gran;
uint32_t unused[9]; uint16_t max_pages;
uint16_t padding;
uint32_t unused[8];
}; };
#define CUSE_INIT_INFO_MAX 4096 #define CUSE_INIT_INFO_MAX 4096
...@@ -792,4 +807,14 @@ struct fuse_lseek_out { ...@@ -792,4 +807,14 @@ struct fuse_lseek_out {
uint64_t offset; uint64_t offset;
}; };
struct fuse_copy_file_range_in {
uint64_t fh_in;
uint64_t off_in;
uint64_t nodeid_out;
uint64_t fh_out;
uint64_t off_out;
uint64_t len;
uint64_t flags;
};
#endif /* _LINUX_FUSE_H */ #endif /* _LINUX_FUSE_H */
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