optimize reading
authorMiklos Szeredi <miklos@szeredi.hu>
Tue, 20 Jul 2004 14:22:26 +0000 (14:22 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Tue, 20 Jul 2004 14:22:26 +0000 (14:22 +0000)
.cvsignore
ChangeLog
kernel/dev.c
kernel/file.c
kernel/fuse_i.h

index b305728064c74dd956337bf3eb8832f9d6d5532c..94d2eb8fb7800ec6d71f5d75be28a5e2411b255f 100644 (file)
@@ -1,6 +1,7 @@
 Makefile.in
 Makefile
-aclocal.m4
+*.m4
+ltmain.sh
 configure
 install-sh
 mkinstalldirs
index 12a1a251b33649403b9605d5fb153c3619989512..9f07e262045ffe0a7167c15fa236d5dc13f1e427 100644 (file)
--- 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
index bc59d9952f8f80baf8754b9c0c4ad34680e7d22b..1fc2943ab3f91fa342652b00176574e264d48297 100644 (file)
@@ -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)
index a82c3269e26f90e877615825399d7cd5f791a654..222a2a6051a848fd3bc42b4dee952685217018b6 100644 (file)
@@ -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
 };
index 4b0399074f6c65926dd8738c3e4f174303285306..2b165e941ffedb82e7fb7693d57826724a349847 100644 (file)
@@ -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;
 };
 
 /**