diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2010-07-12 17:17:25 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2010-07-12 17:17:25 +0200 |
commit | 0065e6db7c86b20d24d76dce395dcd3f65b8771d (patch) | |
tree | 56ab8d15f16a2efc925c96452141b2060d392f13 | |
parent | 33b719f7d96dc8e0f9408f2725c9a81d0ae84c27 (diff) | |
download | fuse-store-retrieve.tar.gz |
libfuse: add retrieve requeststore-retrieve
Retrieve data stored in the kernel buffers for a given inode.
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | include/fuse_kernel.h | 21 | ||||
-rw-r--r-- | include/fuse_lowlevel.h | 39 | ||||
-rw-r--r-- | lib/fuse_i.h | 10 | ||||
-rw-r--r-- | lib/fuse_lowlevel.c | 127 | ||||
-rw-r--r-- | lib/fuse_versionscript | 1 |
6 files changed, 201 insertions, 0 deletions
@@ -9,6 +9,9 @@ * libfuse: add store request. Request data to be stored in the kernel buffers for a given inode. + * libfuse: add retrieve request. Retrieve data stored in the + kernel buffers for a given inode. + 2010-06-23 Miklos Szeredi <miklos@szeredi.hu> * Make the number of max background requests and congestion diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index 25d3555..c7c99a5 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -66,6 +66,7 @@ * * 7.15 * - add store notify + * - add retrieve notify */ #ifndef _LINUX_FUSE_H @@ -285,6 +286,7 @@ enum fuse_opcode { FUSE_DESTROY = 38, FUSE_IOCTL = 39, FUSE_POLL = 40, + FUSE_NOTIFY_REPLY = 41, /* CUSE specific operations */ CUSE_INIT = 4096, @@ -295,6 +297,7 @@ enum fuse_notify_code { FUSE_NOTIFY_INVAL_INODE = 2, FUSE_NOTIFY_INVAL_ENTRY = 3, FUSE_NOTIFY_STORE = 4, + FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_CODE_MAX, }; @@ -610,4 +613,22 @@ struct fuse_notify_store_out { __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 */ diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index e0e0364..7bf4df4 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -877,6 +877,17 @@ struct fuse_lowlevel_ops { */ void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph); + + /** + * Callback function for the retrieve request + * + * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() + * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() + * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() + * @param bufv the buffer containing the returned data + */ + void (*retrieve_reply) (void *cookie, fuse_ino_t ino, off_t offset, + struct fuse_bufvec *bufv); }; /** @@ -1223,6 +1234,34 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent, int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags); +/** + * Retrieve data from the kernel buffers + * + * Retrieve data in the kernel buffers belonging to the given inode. + * If successful then the retrieve_reply() method will be called with + * the returned data. + * + * Only present pages are returned in the retrieve reply. Retrieving + * stops when it finds a non-present page and only data prior to that is + * returned. + * + * If this function returns an error, then the retrieve will not be + * completed and no reply will be sent. + * + * This function doesn't change the dirty state of pages in the kernel + * buffer. For dirty pages the write() method will be called + * regardless of having been retrieved previously. + * + * @param ch the channel through which to send the invalidation + * @param ino the inode number + * @param size the number of bytes to retrieve + * @param offset the starting offset into the file to retrieve from + * @param cookie user data to supply to the reply callback + * @return zero for success, -errno for failure + */ +int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino, + size_t size, off_t offset, void *cookie); + /* ----------------------------------------------------------- * * Utility functions * diff --git a/lib/fuse_i.h b/lib/fuse_i.h index 0206336..66d6ecb 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -43,6 +43,14 @@ struct fuse_req { struct fuse_req *prev; }; +struct fuse_notify_req { + uint64_t unique; + void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, + const void *); + struct fuse_notify_req *next; + struct fuse_notify_req *prev; +}; + struct fuse_ll { int debug; int allow_root; @@ -63,6 +71,8 @@ struct fuse_ll { int got_destroy; pthread_key_t pipe_key; int broken_splice_nonblock; + uint64_t notify_ctr; + struct fuse_notify_req notify_list; }; struct fuse_cmd { diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index e6171cd..9a52765 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -34,6 +34,10 @@ #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) #define OFFSET_MAX 0x7fffffffffffffffLL +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + struct fuse_pollhandle { uint64_t kh; struct fuse_chan *ch; @@ -1633,11 +1637,59 @@ static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) send_reply_ok(req, NULL, 0); } +static void list_del_nreq(struct fuse_notify_req *nreq) +{ + struct fuse_notify_req *prev = nreq->prev; + struct fuse_notify_req *next = nreq->next; + prev->next = next; + next->prev = prev; +} + +static void list_add_nreq(struct fuse_notify_req *nreq, + struct fuse_notify_req *next) +{ + struct fuse_notify_req *prev = next->prev; + nreq->next = next; + nreq->prev = prev; + prev->next = nreq; + next->prev = nreq; +} + +static void list_init_nreq(struct fuse_notify_req *nreq) +{ + nreq->next = nreq; + nreq->prev = nreq; +} + +static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, + const void *inarg) +{ + struct fuse_ll *f = req->f; + struct fuse_notify_req *nreq; + struct fuse_notify_req *head; + + pthread_mutex_lock(&f->lock); + head = &f->notify_list; + for (nreq = head->next; nreq != head; nreq = nreq->next) { + if (nreq->unique == req->unique) { + list_del_nreq(nreq); + break; + } + } + pthread_mutex_unlock(&f->lock); + + if (nreq != head) + nreq->reply(nreq, req, nodeid, inarg); +} + static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch, int notify_code, struct iovec *iov, int count) { struct fuse_out_header out; + if (!f->got_init) + return -ENOTCONN; + out.unique = 0; out.error = notify_code; iov[0].iov_base = &out; @@ -1750,6 +1802,78 @@ int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino, return res; } +struct fuse_retrieve_req { + struct fuse_notify_req nreq; + void *cookie; +}; + +static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, + fuse_req_t req, fuse_ino_t ino, + const void *inarg) +{ + struct fuse_retrieve_req *rreq = container_of(nreq, struct fuse_retrieve_req, nreq); + struct fuse_notify_retrieve_in *arg = (struct fuse_notify_retrieve_in *) inarg; + struct fuse_buf buf = { + .size = arg->size, + .mem = PARAM(arg), + }; + struct fuse_bufvec bufv = { + .buf = &buf, + .count = 1, + }; + + if (req->f->op.retrieve_reply) + req->f->op.retrieve_reply(rreq->cookie, ino, arg->offset, &bufv); + fuse_reply_none(req); + free(rreq); +} + +int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino, + size_t size, off_t offset, void *cookie) +{ + struct fuse_notify_retrieve_out outarg; + struct fuse_ll *f; + struct iovec iov[2]; + struct fuse_retrieve_req *rreq; + int err; + + if (!ch) + return -EINVAL; + + f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch)); + if (!f) + return -ENODEV; + + rreq = malloc(sizeof(*rreq)); + if (rreq == NULL) + return -ENOMEM; + + pthread_mutex_lock(&f->lock); + rreq->cookie = cookie; + rreq->nreq.unique = f->notify_ctr++; + rreq->nreq.reply = fuse_ll_retrieve_reply; + list_add_nreq(&rreq->nreq, &f->notify_list); + pthread_mutex_unlock(&f->lock); + + outarg.notify_unique = rreq->nreq.unique; + outarg.nodeid = ino; + outarg.offset = offset; + outarg.size = size; + + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + + err = send_notify_iov(f, ch, FUSE_NOTIFY_RETRIEVE, iov, 2); + if (err) { + pthread_mutex_lock(&f->lock); + list_del_nreq(&rreq->nreq); + pthread_mutex_unlock(&f->lock); + free(rreq); + } + + return err; +} + void *fuse_req_userdata(fuse_req_t req) { return req->f->userdata; @@ -1839,6 +1963,7 @@ static struct { [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, [FUSE_POLL] = { do_poll, "POLL" }, [FUSE_DESTROY] = { do_destroy, "DESTROY" }, + [FUSE_NOTIFY_REPLY] = { do_notify_reply, "NOTIFY_REPLY" }, [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" }, }; @@ -2057,6 +2182,8 @@ struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args, f->atomic_o_trunc = 0; list_init_req(&f->list); list_init_req(&f->interrupts); + list_init_nreq(&f->notify_list); + f->notify_ctr = 1; fuse_mutex_init(&f->lock); err = pthread_key_create(&f->pipe_key, fuse_ll_pipe_destructor); diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 258081a..bdc67e9 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -185,6 +185,7 @@ FUSE_2.9 { global: fuse_buf_copy; fuse_buf_size; + fuse_lowlevel_notify_retrieve; fuse_lowlevel_notify_store; fuse_reply_data; |