aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2004-07-20 14:22:26 +0000
committerMiklos Szeredi <miklos@szeredi.hu>2004-07-20 14:22:26 +0000
commit588653700e52d72c47c5d5915fc2143db6029474 (patch)
tree8453e809f8145b5114dc10b43a218fdb8d944e97
parent84ba0f45c2e306df8bcf1c1b0b86cb01852b6efb (diff)
downloadlibfuse-588653700e52d72c47c5d5915fc2143db6029474.tar.gz
optimize reading
-rw-r--r--.cvsignore3
-rw-r--r--ChangeLog5
-rw-r--r--kernel/dev.c33
-rw-r--r--kernel/file.c120
-rw-r--r--kernel/fuse_i.h10
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 <miklos@szeredi.hu>
+
+ * Optimize reading under 2.6 kernels by issuing multiple page
+ asynchronous read requests
+
2004-07-18 Miklos Szeredi <miklos@szeredi.hu>
* 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;
};
/**