From 588653700e52d72c47c5d5915fc2143db6029474 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 20 Jul 2004 14:22:26 +0000 Subject: [PATCH] optimize reading --- .cvsignore | 3 +- ChangeLog | 5 ++ kernel/dev.c | 33 +++++++------ kernel/file.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++-- kernel/fuse_i.h | 10 ++++ 5 files changed, 151 insertions(+), 20 deletions(-) diff --git a/.cvsignore b/.cvsignore index b305728..94d2eb8 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1,6 +1,7 @@ Makefile.in Makefile -aclocal.m4 +*.m4 +ltmain.sh configure install-sh mkinstalldirs diff --git a/ChangeLog b/ChangeLog index 12a1a25..9f07e26 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2004-07-20 Miklos Szeredi + + * Optimize reading under 2.6 kernels by issuing multiple page + asynchronous read requests + 2004-07-18 Miklos Szeredi * Only use redirty_page_for_writepage() for kernels >= 2.6.6 diff --git a/kernel/dev.c b/kernel/dev.c index bc59d99..1fc2943 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -364,9 +364,10 @@ static inline int copy_out_one(struct fuse_out_arg *arg, const char **srcp, return 0; } -static inline int copy_out_args(struct fuse_out *out, const char *buf, +static inline int copy_out_args(struct fuse_req *req, const char *buf, size_t nbytes) { + struct fuse_out *out = &req->out; int err; int i; @@ -374,18 +375,22 @@ static inline int copy_out_args(struct fuse_out *out, const char *buf, nbytes -= sizeof(struct fuse_out_header); if (!out->h.error) { - for (i = 0; i < out->numargs; i++) { - struct fuse_out_arg *arg = &out->args[i]; - int allowvar; - - if (out->argvar && i == out->numargs - 1) - allowvar = 1; - else - allowvar = 0; - - err = copy_out_one(arg, &buf, &nbytes, allowvar); - if (err) - return err; + if (req->copy_out) + return req->copy_out(req, buf, nbytes); + else { + for (i = 0; i < out->numargs; i++) { + struct fuse_out_arg *arg = &out->args[i]; + int allowvar; + + if (out->argvar && i == out->numargs - 1) + allowvar = 1; + else + allowvar = 0; + + err = copy_out_one(arg, &buf, &nbytes, allowvar); + if (err) + return err; + } } } @@ -499,7 +504,7 @@ static ssize_t fuse_dev_write(struct file *file, const char *buf, return -ENOENT; req->out.h = oh; - err = copy_out_args(&req->out, buf, nbytes); + err = copy_out_args(req, buf, nbytes); spin_lock(&fuse_lock); if (err) diff --git a/kernel/file.c b/kernel/file.c index a82c326..222a2a6 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -220,7 +220,7 @@ static int fuse_readpage(struct file *file, struct page *page) ssize_t res; loff_t pos; - pos = (unsigned long long) page->index << PAGE_CACHE_SHIFT; + pos = (loff_t) page->index << PAGE_CACHE_SHIFT; buffer = kmap(page); res = fuse_send_read(inode, buffer, pos, PAGE_CACHE_SIZE); if (res >= 0) { @@ -235,6 +235,115 @@ static int fuse_readpage(struct file *file, struct page *page) return res; } +#ifdef KERNEL_2_6 + +static int read_pages_copyout(struct fuse_req *req, const char *buf, + size_t nbytes) +{ + unsigned i; + unsigned long base_index = req->pages[0]->index; + for (i = 0; i < req->num_pages; i++) { + struct page *page = req->pages[i]; + unsigned long offset; + unsigned count; + char *tmpbuf; + int err; + + offset = (page->index - base_index) * PAGE_CACHE_SIZE; + if (offset >= nbytes) + count = 0; + else if (offset + PAGE_CACHE_SIZE <= nbytes) + count = PAGE_CACHE_SIZE; + else + count = nbytes - offset; + + tmpbuf = kmap(page); + err = 0; + if (count) + err = copy_from_user(tmpbuf, buf + offset, count); + if (count < PAGE_CACHE_SIZE) + memset(tmpbuf + count, 0, PAGE_CACHE_SIZE - count); + kunmap(page); + if (err) + return -EFAULT; + + flush_dcache_page(page); + SetPageUptodate(page); + } + return 0; +} + +static void read_pages_end(struct fuse_conn *fc, struct fuse_req *req) +{ + unsigned i; + + for (i = 0; i < req->num_pages; i++) + unlock_page(req->pages[i]); + + fuse_put_request(fc, req); +} + +static void fuse_send_readpages(struct fuse_req *req, struct inode *inode) +{ + struct fuse_conn *fc = INO_FC(inode); + struct fuse_read_in *inarg; + loff_t pos; + unsigned numpages; + + pos = (loff_t) req->pages[0]->index << PAGE_CACHE_SHIFT; + /* Allow for holes between the pages */ + numpages = req->pages[req->num_pages - 1]->index + 1 + - req->pages[0]->index; + + inarg = &req->misc.read_in; + inarg->offset = pos; + inarg->size = numpages * PAGE_CACHE_SIZE; + req->in.h.opcode = FUSE_READ; + req->in.h.ino = inode->i_ino; + req->in.numargs = 1; + req->in.args[0].size = sizeof(struct fuse_read_in); + req->in.args[0].value = inarg; + req->copy_out = read_pages_copyout; + request_send_nonblock(fc, req, read_pages_end, NULL); +} + +static int fuse_readpages_fill(void *_reqp, struct page *page) +{ + struct inode *inode = page->mapping->host; + struct fuse_conn *fc = INO_FC(inode); + struct fuse_req **reqp = (struct fuse_req **) _reqp; + struct fuse_req *req = *reqp; + + if (req->num_pages && + (req->num_pages == FUSE_MAX_PAGES_PER_REQ || + (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read || + req->pages[req->num_pages - 1]->index + 1 != page->index)) { + struct fuse_conn *fc = INO_FC(page->mapping->host); + fuse_send_readpages(req, inode); + *reqp = req = fuse_get_request(fc); + } + req->pages[req->num_pages] = page; + req->num_pages ++; + return 0; +} + +static int fuse_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + struct inode *inode = mapping->host; + struct fuse_conn *fc = INO_FC(inode); + struct fuse_req *req = fuse_get_request(fc); + + read_cache_pages(mapping, pages, fuse_readpages_fill, &req); + if (req->num_pages) + fuse_send_readpages(req, inode); + else + fuse_put_request(fc, req); + + return 0; +} +#endif + static int fuse_is_block_uptodate(struct inode *inode, size_t bl_index) { size_t index = bl_index << FUSE_BLOCK_PAGE_SHIFT; @@ -305,7 +414,7 @@ static int fuse_file_read_block(struct inode *inode, char *bl_buf, ssize_t res; loff_t offset; - offset = (unsigned long long) bl_index << FUSE_BLOCK_SHIFT; + offset = (loff_t) bl_index << FUSE_BLOCK_SHIFT; res = fuse_send_read(inode, bl_buf, offset, FUSE_BLOCK_SIZE); if (res >= 0) { if (res < FUSE_BLOCK_SIZE) @@ -451,7 +560,7 @@ static int write_buffer(struct inode *inode, struct page *page, if (!req) return -ERESTARTSYS; - pos = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) + offset; + pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + offset; buffer = kmap(page); res = fuse_send_write(req, inode, buffer + offset, pos, count); fuse_put_request(fc, req); @@ -504,7 +613,7 @@ static int write_page_block(struct inode *inode, struct page *page) count = get_write_count(inode, page); res = 0; if (count) { - pos = ((unsigned long long) page->index << PAGE_CACHE_SHIFT); + pos = ((loff_t) page->index << PAGE_CACHE_SHIFT); buffer = kmap(page); res = fuse_send_write(req, inode, buffer, pos, count); if (res >= 0) { @@ -561,7 +670,7 @@ static void send_write_nonblock(struct fuse_req *req, struct inode *inode, inarg = &req->misc.write.in; buffer = kmap(page); - inarg->offset = ((unsigned long long) page->index << PAGE_CACHE_SHIFT); + inarg->offset = ((loff_t) page->index << PAGE_CACHE_SHIFT); inarg->size = count; req->in.h.opcode = FUSE_WRITE; req->in.h.ino = inode->i_ino; @@ -762,6 +871,7 @@ static struct address_space_operations fuse_file_aops = { .prepare_write = fuse_prepare_write, .commit_write = fuse_commit_write, #ifdef KERNEL_2_6 + .readpages = fuse_readpages, .set_page_dirty = __set_page_dirty_nobuffers, #endif }; diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index 4b03990..2b165e9 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -43,6 +43,8 @@ #define FUSE_BLOCK_PAGE_SHIFT (FUSE_BLOCK_SHIFT - PAGE_CACHE_SHIFT) +#define FUSE_MAX_PAGES_PER_REQ 32 + /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem module will check permissions based on the file mode. Otherwise no permission checking is done in the kernel */ @@ -103,6 +105,7 @@ struct fuse_req; struct fuse_conn; typedef void (*fuse_reqend_t)(struct fuse_conn *, struct fuse_req *); +typedef int (*fuse_copyout_t)(struct fuse_req *, const char *, size_t); /** * A request to the client @@ -141,6 +144,9 @@ struct fuse_req { /** Request completion callback */ fuse_reqend_t end; + /** Request copy out function */ + fuse_copyout_t copy_out; + /** User data */ void *data; @@ -151,9 +157,13 @@ struct fuse_req { struct fuse_write_out out; } write; + struct fuse_read_in read_in; struct fuse_open_in open_in; struct fuse_forget_in forget_in; } misc; + + struct page *pages[FUSE_MAX_PAGES_PER_REQ]; + unsigned num_pages; }; /** -- 2.30.2