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 retrieve request fuse: add store request fuse: don't use atomic kmap
This commit is contained in:
commit
fe21ea18c7
229
fs/fuse/dev.c
229
fs/fuse/dev.c
|
@ -239,7 +239,6 @@ static u64 fuse_get_unique(struct fuse_conn *fc)
|
||||||
|
|
||||||
static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
|
static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
{
|
{
|
||||||
req->in.h.unique = fuse_get_unique(fc);
|
|
||||||
req->in.h.len = sizeof(struct fuse_in_header) +
|
req->in.h.len = sizeof(struct fuse_in_header) +
|
||||||
len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
|
len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
|
||||||
list_add_tail(&req->list, &fc->pending);
|
list_add_tail(&req->list, &fc->pending);
|
||||||
|
@ -261,6 +260,7 @@ static void flush_bg_queue(struct fuse_conn *fc)
|
||||||
req = list_entry(fc->bg_queue.next, struct fuse_req, list);
|
req = list_entry(fc->bg_queue.next, struct fuse_req, list);
|
||||||
list_del(&req->list);
|
list_del(&req->list);
|
||||||
fc->active_background++;
|
fc->active_background++;
|
||||||
|
req->in.h.unique = fuse_get_unique(fc);
|
||||||
queue_request(fc, req);
|
queue_request(fc, req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,6 +398,7 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
else if (fc->conn_error)
|
else if (fc->conn_error)
|
||||||
req->out.h.error = -ECONNREFUSED;
|
req->out.h.error = -ECONNREFUSED;
|
||||||
else {
|
else {
|
||||||
|
req->in.h.unique = fuse_get_unique(fc);
|
||||||
queue_request(fc, req);
|
queue_request(fc, req);
|
||||||
/* acquire extra reference, since request is still needed
|
/* acquire extra reference, since request is still needed
|
||||||
after request_end() */
|
after request_end() */
|
||||||
|
@ -450,6 +451,23 @@ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fuse_request_send_background);
|
EXPORT_SYMBOL_GPL(fuse_request_send_background);
|
||||||
|
|
||||||
|
static int fuse_request_send_notify_reply(struct fuse_conn *fc,
|
||||||
|
struct fuse_req *req, u64 unique)
|
||||||
|
{
|
||||||
|
int err = -ENODEV;
|
||||||
|
|
||||||
|
req->isreply = 0;
|
||||||
|
req->in.h.unique = unique;
|
||||||
|
spin_lock(&fc->lock);
|
||||||
|
if (fc->connected) {
|
||||||
|
queue_request(fc, req);
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
spin_unlock(&fc->lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called under fc->lock
|
* Called under fc->lock
|
||||||
*
|
*
|
||||||
|
@ -535,13 +553,13 @@ static void fuse_copy_finish(struct fuse_copy_state *cs)
|
||||||
if (!cs->write) {
|
if (!cs->write) {
|
||||||
buf->ops->unmap(cs->pipe, buf, cs->mapaddr);
|
buf->ops->unmap(cs->pipe, buf, cs->mapaddr);
|
||||||
} else {
|
} else {
|
||||||
kunmap_atomic(cs->mapaddr, KM_USER0);
|
kunmap(buf->page);
|
||||||
buf->len = PAGE_SIZE - cs->len;
|
buf->len = PAGE_SIZE - cs->len;
|
||||||
}
|
}
|
||||||
cs->currbuf = NULL;
|
cs->currbuf = NULL;
|
||||||
cs->mapaddr = NULL;
|
cs->mapaddr = NULL;
|
||||||
} else if (cs->mapaddr) {
|
} else if (cs->mapaddr) {
|
||||||
kunmap_atomic(cs->mapaddr, KM_USER0);
|
kunmap(cs->pg);
|
||||||
if (cs->write) {
|
if (cs->write) {
|
||||||
flush_dcache_page(cs->pg);
|
flush_dcache_page(cs->pg);
|
||||||
set_page_dirty_lock(cs->pg);
|
set_page_dirty_lock(cs->pg);
|
||||||
|
@ -572,7 +590,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
|
||||||
|
|
||||||
BUG_ON(!cs->nr_segs);
|
BUG_ON(!cs->nr_segs);
|
||||||
cs->currbuf = buf;
|
cs->currbuf = buf;
|
||||||
cs->mapaddr = buf->ops->map(cs->pipe, buf, 1);
|
cs->mapaddr = buf->ops->map(cs->pipe, buf, 0);
|
||||||
cs->len = buf->len;
|
cs->len = buf->len;
|
||||||
cs->buf = cs->mapaddr + buf->offset;
|
cs->buf = cs->mapaddr + buf->offset;
|
||||||
cs->pipebufs++;
|
cs->pipebufs++;
|
||||||
|
@ -592,7 +610,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
|
||||||
buf->len = 0;
|
buf->len = 0;
|
||||||
|
|
||||||
cs->currbuf = buf;
|
cs->currbuf = buf;
|
||||||
cs->mapaddr = kmap_atomic(page, KM_USER0);
|
cs->mapaddr = kmap(page);
|
||||||
cs->buf = cs->mapaddr;
|
cs->buf = cs->mapaddr;
|
||||||
cs->len = PAGE_SIZE;
|
cs->len = PAGE_SIZE;
|
||||||
cs->pipebufs++;
|
cs->pipebufs++;
|
||||||
|
@ -611,7 +629,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
|
||||||
return err;
|
return err;
|
||||||
BUG_ON(err != 1);
|
BUG_ON(err != 1);
|
||||||
offset = cs->addr % PAGE_SIZE;
|
offset = cs->addr % PAGE_SIZE;
|
||||||
cs->mapaddr = kmap_atomic(cs->pg, KM_USER0);
|
cs->mapaddr = kmap(cs->pg);
|
||||||
cs->buf = cs->mapaddr + offset;
|
cs->buf = cs->mapaddr + offset;
|
||||||
cs->len = min(PAGE_SIZE - offset, cs->seglen);
|
cs->len = min(PAGE_SIZE - offset, cs->seglen);
|
||||||
cs->seglen -= cs->len;
|
cs->seglen -= cs->len;
|
||||||
|
@ -1231,6 +1249,199 @@ err:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
|
||||||
|
struct fuse_copy_state *cs)
|
||||||
|
{
|
||||||
|
struct fuse_notify_store_out outarg;
|
||||||
|
struct inode *inode;
|
||||||
|
struct address_space *mapping;
|
||||||
|
u64 nodeid;
|
||||||
|
int err;
|
||||||
|
pgoff_t index;
|
||||||
|
unsigned int offset;
|
||||||
|
unsigned int num;
|
||||||
|
loff_t file_size;
|
||||||
|
loff_t end;
|
||||||
|
|
||||||
|
err = -EINVAL;
|
||||||
|
if (size < sizeof(outarg))
|
||||||
|
goto out_finish;
|
||||||
|
|
||||||
|
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
||||||
|
if (err)
|
||||||
|
goto out_finish;
|
||||||
|
|
||||||
|
err = -EINVAL;
|
||||||
|
if (size - sizeof(outarg) != outarg.size)
|
||||||
|
goto out_finish;
|
||||||
|
|
||||||
|
nodeid = outarg.nodeid;
|
||||||
|
|
||||||
|
down_read(&fc->killsb);
|
||||||
|
|
||||||
|
err = -ENOENT;
|
||||||
|
if (!fc->sb)
|
||||||
|
goto out_up_killsb;
|
||||||
|
|
||||||
|
inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
|
||||||
|
if (!inode)
|
||||||
|
goto out_up_killsb;
|
||||||
|
|
||||||
|
mapping = inode->i_mapping;
|
||||||
|
index = outarg.offset >> PAGE_CACHE_SHIFT;
|
||||||
|
offset = outarg.offset & ~PAGE_CACHE_MASK;
|
||||||
|
file_size = i_size_read(inode);
|
||||||
|
end = outarg.offset + outarg.size;
|
||||||
|
if (end > file_size) {
|
||||||
|
file_size = end;
|
||||||
|
fuse_write_update_size(inode, file_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
num = outarg.size;
|
||||||
|
while (num) {
|
||||||
|
struct page *page;
|
||||||
|
unsigned int this_num;
|
||||||
|
|
||||||
|
err = -ENOMEM;
|
||||||
|
page = find_or_create_page(mapping, index,
|
||||||
|
mapping_gfp_mask(mapping));
|
||||||
|
if (!page)
|
||||||
|
goto out_iput;
|
||||||
|
|
||||||
|
this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset);
|
||||||
|
err = fuse_copy_page(cs, &page, offset, this_num, 0);
|
||||||
|
if (!err && offset == 0 && (num != 0 || file_size == end))
|
||||||
|
SetPageUptodate(page);
|
||||||
|
unlock_page(page);
|
||||||
|
page_cache_release(page);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
goto out_iput;
|
||||||
|
|
||||||
|
num -= this_num;
|
||||||
|
offset = 0;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
out_iput:
|
||||||
|
iput(inode);
|
||||||
|
out_up_killsb:
|
||||||
|
up_read(&fc->killsb);
|
||||||
|
out_finish:
|
||||||
|
fuse_copy_finish(cs);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < req->num_pages; i++) {
|
||||||
|
struct page *page = req->pages[i];
|
||||||
|
page_cache_release(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
|
||||||
|
struct fuse_notify_retrieve_out *outarg)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct address_space *mapping = inode->i_mapping;
|
||||||
|
struct fuse_req *req;
|
||||||
|
pgoff_t index;
|
||||||
|
loff_t file_size;
|
||||||
|
unsigned int num;
|
||||||
|
unsigned int offset;
|
||||||
|
size_t total_len;
|
||||||
|
|
||||||
|
req = fuse_get_req(fc);
|
||||||
|
if (IS_ERR(req))
|
||||||
|
return PTR_ERR(req);
|
||||||
|
|
||||||
|
offset = outarg->offset & ~PAGE_CACHE_MASK;
|
||||||
|
|
||||||
|
req->in.h.opcode = FUSE_NOTIFY_REPLY;
|
||||||
|
req->in.h.nodeid = outarg->nodeid;
|
||||||
|
req->in.numargs = 2;
|
||||||
|
req->in.argpages = 1;
|
||||||
|
req->page_offset = offset;
|
||||||
|
req->end = fuse_retrieve_end;
|
||||||
|
|
||||||
|
index = outarg->offset >> PAGE_CACHE_SHIFT;
|
||||||
|
file_size = i_size_read(inode);
|
||||||
|
num = outarg->size;
|
||||||
|
if (outarg->offset > file_size)
|
||||||
|
num = 0;
|
||||||
|
else if (outarg->offset + num > file_size)
|
||||||
|
num = file_size - outarg->offset;
|
||||||
|
|
||||||
|
while (num) {
|
||||||
|
struct page *page;
|
||||||
|
unsigned int this_num;
|
||||||
|
|
||||||
|
page = find_get_page(mapping, index);
|
||||||
|
if (!page)
|
||||||
|
break;
|
||||||
|
|
||||||
|
this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset);
|
||||||
|
req->pages[req->num_pages] = page;
|
||||||
|
req->num_pages++;
|
||||||
|
|
||||||
|
num -= this_num;
|
||||||
|
total_len += this_num;
|
||||||
|
}
|
||||||
|
req->misc.retrieve_in.offset = outarg->offset;
|
||||||
|
req->misc.retrieve_in.size = total_len;
|
||||||
|
req->in.args[0].size = sizeof(req->misc.retrieve_in);
|
||||||
|
req->in.args[0].value = &req->misc.retrieve_in;
|
||||||
|
req->in.args[1].size = total_len;
|
||||||
|
|
||||||
|
err = fuse_request_send_notify_reply(fc, req, outarg->notify_unique);
|
||||||
|
if (err)
|
||||||
|
fuse_retrieve_end(fc, req);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
|
||||||
|
struct fuse_copy_state *cs)
|
||||||
|
{
|
||||||
|
struct fuse_notify_retrieve_out outarg;
|
||||||
|
struct inode *inode;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = -EINVAL;
|
||||||
|
if (size != sizeof(outarg))
|
||||||
|
goto copy_finish;
|
||||||
|
|
||||||
|
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
||||||
|
if (err)
|
||||||
|
goto copy_finish;
|
||||||
|
|
||||||
|
fuse_copy_finish(cs);
|
||||||
|
|
||||||
|
down_read(&fc->killsb);
|
||||||
|
err = -ENOENT;
|
||||||
|
if (fc->sb) {
|
||||||
|
u64 nodeid = outarg.nodeid;
|
||||||
|
|
||||||
|
inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
|
||||||
|
if (inode) {
|
||||||
|
err = fuse_retrieve(fc, inode, &outarg);
|
||||||
|
iput(inode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
up_read(&fc->killsb);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
|
||||||
|
copy_finish:
|
||||||
|
fuse_copy_finish(cs);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
|
static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
|
||||||
unsigned int size, struct fuse_copy_state *cs)
|
unsigned int size, struct fuse_copy_state *cs)
|
||||||
{
|
{
|
||||||
|
@ -1244,6 +1455,12 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
|
||||||
case FUSE_NOTIFY_INVAL_ENTRY:
|
case FUSE_NOTIFY_INVAL_ENTRY:
|
||||||
return fuse_notify_inval_entry(fc, size, cs);
|
return fuse_notify_inval_entry(fc, size, cs);
|
||||||
|
|
||||||
|
case FUSE_NOTIFY_STORE:
|
||||||
|
return fuse_notify_store(fc, size, cs);
|
||||||
|
|
||||||
|
case FUSE_NOTIFY_RETRIEVE:
|
||||||
|
return fuse_notify_retrieve(fc, size, cs);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fuse_copy_finish(cs);
|
fuse_copy_finish(cs);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -706,7 +706,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fuse_write_update_size(struct inode *inode, loff_t pos)
|
void fuse_write_update_size(struct inode *inode, loff_t pos)
|
||||||
{
|
{
|
||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||||
|
|
|
@ -272,6 +272,7 @@ struct fuse_req {
|
||||||
struct fuse_write_in in;
|
struct fuse_write_in in;
|
||||||
struct fuse_write_out out;
|
struct fuse_write_out out;
|
||||||
} write;
|
} write;
|
||||||
|
struct fuse_notify_retrieve_in retrieve_in;
|
||||||
struct fuse_lk_in lk_in;
|
struct fuse_lk_in lk_in;
|
||||||
} misc;
|
} misc;
|
||||||
|
|
||||||
|
@ -748,4 +749,6 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
|
||||||
unsigned fuse_file_poll(struct file *file, poll_table *wait);
|
unsigned fuse_file_poll(struct file *file, poll_table *wait);
|
||||||
int fuse_dev_release(struct inode *inode, struct file *file);
|
int fuse_dev_release(struct inode *inode, struct file *file);
|
||||||
|
|
||||||
|
void fuse_write_update_size(struct inode *inode, loff_t pos);
|
||||||
|
|
||||||
#endif /* _FS_FUSE_I_H */
|
#endif /* _FS_FUSE_I_H */
|
||||||
|
|
|
@ -37,6 +37,10 @@
|
||||||
*
|
*
|
||||||
* 7.14
|
* 7.14
|
||||||
* - add splice support to fuse device
|
* - add splice support to fuse device
|
||||||
|
*
|
||||||
|
* 7.15
|
||||||
|
* - add store notify
|
||||||
|
* - add retrieve notify
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LINUX_FUSE_H
|
#ifndef _LINUX_FUSE_H
|
||||||
|
@ -68,7 +72,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 14
|
#define FUSE_KERNEL_MINOR_VERSION 15
|
||||||
|
|
||||||
/** The node ID of the root inode */
|
/** The node ID of the root inode */
|
||||||
#define FUSE_ROOT_ID 1
|
#define FUSE_ROOT_ID 1
|
||||||
|
@ -251,6 +255,7 @@ enum fuse_opcode {
|
||||||
FUSE_DESTROY = 38,
|
FUSE_DESTROY = 38,
|
||||||
FUSE_IOCTL = 39,
|
FUSE_IOCTL = 39,
|
||||||
FUSE_POLL = 40,
|
FUSE_POLL = 40,
|
||||||
|
FUSE_NOTIFY_REPLY = 41,
|
||||||
|
|
||||||
/* CUSE specific operations */
|
/* CUSE specific operations */
|
||||||
CUSE_INIT = 4096,
|
CUSE_INIT = 4096,
|
||||||
|
@ -260,6 +265,8 @@ enum fuse_notify_code {
|
||||||
FUSE_NOTIFY_POLL = 1,
|
FUSE_NOTIFY_POLL = 1,
|
||||||
FUSE_NOTIFY_INVAL_INODE = 2,
|
FUSE_NOTIFY_INVAL_INODE = 2,
|
||||||
FUSE_NOTIFY_INVAL_ENTRY = 3,
|
FUSE_NOTIFY_INVAL_ENTRY = 3,
|
||||||
|
FUSE_NOTIFY_STORE = 4,
|
||||||
|
FUSE_NOTIFY_RETRIEVE = 5,
|
||||||
FUSE_NOTIFY_CODE_MAX,
|
FUSE_NOTIFY_CODE_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -568,4 +575,29 @@ struct fuse_notify_inval_entry_out {
|
||||||
__u32 padding;
|
__u32 padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct fuse_notify_store_out {
|
||||||
|
__u64 nodeid;
|
||||||
|
__u64 offset;
|
||||||
|
__u32 size;
|
||||||
|
__u32 padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_notify_retrieve_out {
|
||||||
|
__u64 notify_unique;
|
||||||
|
__u64 nodeid;
|
||||||
|
__u64 offset;
|
||||||
|
__u32 size;
|
||||||
|
__u32 padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Matches the size of fuse_write_in */
|
||||||
|
struct fuse_notify_retrieve_in {
|
||||||
|
__u64 dummy1;
|
||||||
|
__u64 offset;
|
||||||
|
__u32 size;
|
||||||
|
__u32 dummy2;
|
||||||
|
__u64 dummy3;
|
||||||
|
__u64 dummy4;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _LINUX_FUSE_H */
|
#endif /* _LINUX_FUSE_H */
|
||||||
|
|
Loading…
Reference in a new issue