Commit 5ee7a81a authored by Linus Torvalds's avatar Linus Torvalds

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

Pull fuse update from Miklos Szeredi:
 "This contains a fix for a potential use-after-module-unload bug
  noticed by Al and caching improvements for read-only fuse filesystems
  by Andrew Gallagher"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: support clients that don't implement 'open'
  fuse: don't invalidate attrs when not using atime
  fuse: fix SetPageUptodate() condition in STORE
  fuse: fix pipe_buf_operations
parents 0d90d638 7678ac50
...@@ -1296,22 +1296,6 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -1296,22 +1296,6 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov,
return fuse_dev_do_read(fc, file, &cs, iov_length(iov, nr_segs)); return fuse_dev_do_read(fc, file, &cs, iov_length(iov, nr_segs));
} }
static int fuse_dev_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
return 1;
}
static const struct pipe_buf_operations fuse_dev_pipe_buf_ops = {
.can_merge = 0,
.map = generic_pipe_buf_map,
.unmap = generic_pipe_buf_unmap,
.confirm = generic_pipe_buf_confirm,
.release = generic_pipe_buf_release,
.steal = fuse_dev_pipe_buf_steal,
.get = generic_pipe_buf_get,
};
static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, struct pipe_inode_info *pipe,
size_t len, unsigned int flags) size_t len, unsigned int flags)
...@@ -1358,7 +1342,11 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, ...@@ -1358,7 +1342,11 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
buf->page = bufs[page_nr].page; buf->page = bufs[page_nr].page;
buf->offset = bufs[page_nr].offset; buf->offset = bufs[page_nr].offset;
buf->len = bufs[page_nr].len; buf->len = bufs[page_nr].len;
buf->ops = &fuse_dev_pipe_buf_ops; /*
* Need to be careful about this. Having buf->ops in module
* code can Oops if the buffer persists after module unload.
*/
buf->ops = &nosteal_pipe_buf_ops;
pipe->nrbufs++; pipe->nrbufs++;
page_nr++; page_nr++;
...@@ -1599,7 +1587,8 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, ...@@ -1599,7 +1587,8 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset);
err = fuse_copy_page(cs, &page, offset, this_num, 0); err = fuse_copy_page(cs, &page, offset, this_num, 0);
if (!err && offset == 0 && (num != 0 || file_size == end)) if (!err && offset == 0 &&
(this_num == PAGE_CACHE_SIZE || file_size == end))
SetPageUptodate(page); SetPageUptodate(page);
unlock_page(page); unlock_page(page);
page_cache_release(page); page_cache_release(page);
......
...@@ -112,6 +112,16 @@ void fuse_invalidate_attr(struct inode *inode) ...@@ -112,6 +112,16 @@ void fuse_invalidate_attr(struct inode *inode)
get_fuse_inode(inode)->i_time = 0; get_fuse_inode(inode)->i_time = 0;
} }
/**
* Mark the attributes as stale due to an atime change. Avoid the invalidate if
* atime is not used.
*/
void fuse_invalidate_atime(struct inode *inode)
{
if (!IS_RDONLY(inode))
fuse_invalidate_attr(inode);
}
/* /*
* Just mark the entry as stale, so that a next attempt to look it up * Just mark the entry as stale, so that a next attempt to look it up
* will result in a new lookup call to userspace * will result in a new lookup call to userspace
...@@ -1371,7 +1381,7 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) ...@@ -1371,7 +1381,7 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx)
} }
__free_page(page); __free_page(page);
fuse_invalidate_attr(inode); /* atime changed */ fuse_invalidate_atime(inode);
return err; return err;
} }
...@@ -1404,7 +1414,7 @@ static char *read_link(struct dentry *dentry) ...@@ -1404,7 +1414,7 @@ static char *read_link(struct dentry *dentry)
link[req->out.args[0].size] = '\0'; link[req->out.args[0].size] = '\0';
out: out:
fuse_put_request(fc, req); fuse_put_request(fc, req);
fuse_invalidate_attr(inode); /* atime changed */ fuse_invalidate_atime(inode);
return link; return link;
} }
......
...@@ -127,7 +127,15 @@ static void fuse_file_put(struct fuse_file *ff, bool sync) ...@@ -127,7 +127,15 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
if (atomic_dec_and_test(&ff->count)) { if (atomic_dec_and_test(&ff->count)) {
struct fuse_req *req = ff->reserved_req; struct fuse_req *req = ff->reserved_req;
if (sync) { if (ff->fc->no_open) {
/*
* Drop the release request when client does not
* implement 'open'
*/
req->background = 0;
path_put(&req->misc.release.path);
fuse_put_request(ff->fc, req);
} else if (sync) {
req->background = 0; req->background = 0;
fuse_request_send(ff->fc, req); fuse_request_send(ff->fc, req);
path_put(&req->misc.release.path); path_put(&req->misc.release.path);
...@@ -144,27 +152,36 @@ static void fuse_file_put(struct fuse_file *ff, bool sync) ...@@ -144,27 +152,36 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
bool isdir) bool isdir)
{ {
struct fuse_open_out outarg;
struct fuse_file *ff; struct fuse_file *ff;
int err;
int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN; int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
ff = fuse_file_alloc(fc); ff = fuse_file_alloc(fc);
if (!ff) if (!ff)
return -ENOMEM; return -ENOMEM;
err = fuse_send_open(fc, nodeid, file, opcode, &outarg); ff->fh = 0;
if (err) { ff->open_flags = FOPEN_KEEP_CACHE; /* Default for no-open */
fuse_file_free(ff); if (!fc->no_open || isdir) {
return err; struct fuse_open_out outarg;
int err;
err = fuse_send_open(fc, nodeid, file, opcode, &outarg);
if (!err) {
ff->fh = outarg.fh;
ff->open_flags = outarg.open_flags;
} else if (err != -ENOSYS || isdir) {
fuse_file_free(ff);
return err;
} else {
fc->no_open = 1;
}
} }
if (isdir) if (isdir)
outarg.open_flags &= ~FOPEN_DIRECT_IO; ff->open_flags &= ~FOPEN_DIRECT_IO;
ff->fh = outarg.fh;
ff->nodeid = nodeid; ff->nodeid = nodeid;
ff->open_flags = outarg.open_flags;
file->private_data = fuse_file_get(ff); file->private_data = fuse_file_get(ff);
return 0; return 0;
...@@ -687,7 +704,7 @@ static int fuse_readpage(struct file *file, struct page *page) ...@@ -687,7 +704,7 @@ static int fuse_readpage(struct file *file, struct page *page)
SetPageUptodate(page); SetPageUptodate(page);
} }
fuse_invalidate_attr(inode); /* atime changed */ fuse_invalidate_atime(inode);
out: out:
unlock_page(page); unlock_page(page);
return err; return err;
...@@ -716,7 +733,7 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req) ...@@ -716,7 +733,7 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
fuse_read_update_size(inode, pos, fuse_read_update_size(inode, pos,
req->misc.read.attr_ver); req->misc.read.attr_ver);
} }
fuse_invalidate_attr(inode); /* atime changed */ fuse_invalidate_atime(inode);
} }
for (i = 0; i < req->num_pages; i++) { for (i = 0; i < req->num_pages; i++) {
......
...@@ -485,6 +485,9 @@ struct fuse_conn { ...@@ -485,6 +485,9 @@ struct fuse_conn {
* and hence races in setting them will not cause malfunction * and hence races in setting them will not cause malfunction
*/ */
/** Is open/release not implemented by fs? */
unsigned no_open:1;
/** Is fsync not implemented by fs? */ /** Is fsync not implemented by fs? */
unsigned no_fsync:1; unsigned no_fsync:1;
...@@ -788,6 +791,8 @@ void fuse_invalidate_attr(struct inode *inode); ...@@ -788,6 +791,8 @@ void fuse_invalidate_attr(struct inode *inode);
void fuse_invalidate_entry_cache(struct dentry *entry); void fuse_invalidate_entry_cache(struct dentry *entry);
void fuse_invalidate_atime(struct inode *inode);
/** /**
* Acquire reference to fuse_conn * Acquire reference to fuse_conn
*/ */
......
...@@ -555,6 +555,24 @@ static const struct pipe_buf_operations default_pipe_buf_ops = { ...@@ -555,6 +555,24 @@ static const struct pipe_buf_operations default_pipe_buf_ops = {
.get = generic_pipe_buf_get, .get = generic_pipe_buf_get,
}; };
static int generic_pipe_buf_nosteal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
return 1;
}
/* Pipe buffer operations for a socket and similar. */
const struct pipe_buf_operations nosteal_pipe_buf_ops = {
.can_merge = 0,
.map = generic_pipe_buf_map,
.unmap = generic_pipe_buf_unmap,
.confirm = generic_pipe_buf_confirm,
.release = generic_pipe_buf_release,
.steal = generic_pipe_buf_nosteal,
.get = generic_pipe_buf_get,
};
EXPORT_SYMBOL(nosteal_pipe_buf_ops);
static ssize_t kernel_readv(struct file *file, const struct iovec *vec, static ssize_t kernel_readv(struct file *file, const struct iovec *vec,
unsigned long vlen, loff_t offset) unsigned long vlen, loff_t offset)
{ {
......
...@@ -157,6 +157,8 @@ int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); ...@@ -157,6 +157,8 @@ int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *);
int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *);
extern const struct pipe_buf_operations nosteal_pipe_buf_ops;
/* for F_SETPIPE_SZ and F_GETPIPE_SZ */ /* for F_SETPIPE_SZ and F_GETPIPE_SZ */
long pipe_fcntl(struct file *, unsigned int, unsigned long arg); long pipe_fcntl(struct file *, unsigned int, unsigned long arg);
struct pipe_inode_info *get_pipe_info(struct file *file); struct pipe_inode_info *get_pipe_info(struct file *file);
......
...@@ -74,36 +74,6 @@ ...@@ -74,36 +74,6 @@
struct kmem_cache *skbuff_head_cache __read_mostly; struct kmem_cache *skbuff_head_cache __read_mostly;
static struct kmem_cache *skbuff_fclone_cache __read_mostly; static struct kmem_cache *skbuff_fclone_cache __read_mostly;
static void sock_pipe_buf_release(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
put_page(buf->page);
}
static void sock_pipe_buf_get(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
get_page(buf->page);
}
static int sock_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
return 1;
}
/* Pipe buffer operations for a socket. */
static const struct pipe_buf_operations sock_pipe_buf_ops = {
.can_merge = 0,
.map = generic_pipe_buf_map,
.unmap = generic_pipe_buf_unmap,
.confirm = generic_pipe_buf_confirm,
.release = sock_pipe_buf_release,
.steal = sock_pipe_buf_steal,
.get = sock_pipe_buf_get,
};
/** /**
* skb_panic - private function for out-of-line support * skb_panic - private function for out-of-line support
* @skb: buffer * @skb: buffer
...@@ -1830,7 +1800,7 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset, ...@@ -1830,7 +1800,7 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
.partial = partial, .partial = partial,
.nr_pages_max = MAX_SKB_FRAGS, .nr_pages_max = MAX_SKB_FRAGS,
.flags = flags, .flags = flags,
.ops = &sock_pipe_buf_ops, .ops = &nosteal_pipe_buf_ops,
.spd_release = sock_spd_release, .spd_release = sock_spd_release,
}; };
struct sk_buff *frag_iter; struct sk_buff *frag_iter;
......
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