2.6 fixes
authorMiklos Szeredi <miklos@szeredi.hu>
Wed, 14 Jan 2004 16:56:49 +0000 (16:56 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Wed, 14 Jan 2004 16:56:49 +0000 (16:56 +0000)
ChangeLog
configure.in
kernel/dev.c
kernel/dir.c
kernel/file.c
kernel/fuse_i.h
kernel/inode.c

index b065dbfe630ffa4c4ff993bd360309331cd804a6..2c43391c959e51bfcea9ee61e24bf3344e8c201c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2004-01-14  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+       * Support non-blocking writepage on 2.6.  This makes FUSE behave
+       much more nicely in low-memory situations
+
+       * Fix 32-bit dev handling in getattr and mknod for 2.6 kernels.
+       (Note: the mknod method does not yet use 32bit device number)
 2004-01-13  Miklos Szeredi <mszeredi@inf.bme.hu>
 
        * Code cleanups
index 359dc68e3c243ec9baeb56338ee7dc625716740f..be9317087f1c88cfd1333744794d3c32bd51aa2e 100644 (file)
@@ -1,5 +1,5 @@
 AC_INIT(lib/fuse.c)
-AM_INIT_AUTOMAKE(fuse, 1.1-pre1)
+AM_INIT_AUTOMAKE(fuse, 1.1-pre2)
 AM_CONFIG_HEADER(include/config.h)
 
 AC_PROG_CC
index 4f456580b98389ace258be900a9227382f0ea365..d3c56a67517803eb0f93e94222836c4ad178ce64 100644 (file)
@@ -96,6 +96,22 @@ static int get_unique(struct fuse_conn *fc)
        return fc->reqctr;
 }
 
+/* Must be called with fuse_lock held, and unlocks it */
+static void request_end(struct fuse_conn *fc, struct fuse_req *req)
+{
+       fuse_reqend_t endfunc = req->end;
+
+       if(!endfunc) {
+               wake_up(&req->waitq);
+               spin_unlock(&fuse_lock);
+       } else {
+               spin_unlock(&fuse_lock);
+               endfunc(fc, req->in, req->out, req->data);
+               request_free(req);
+               up(&fc->outstanding);
+       }
+}
+
 void request_send(struct fuse_conn *fc, struct fuse_in *in,
                  struct fuse_out *out)
 {
@@ -107,24 +123,24 @@ void request_send(struct fuse_conn *fc, struct fuse_in *in,
 
        out->h.error = -ENOMEM;
        req = request_new();
-       if(!req)
-               return;
-
-       req->in = in;
-       req->out = out;
-       req->issync = 1;
-
-       spin_lock(&fuse_lock);
-       out->h.error = -ENOTCONN;
-       if(fc->file) {
-               in->h.unique = get_unique(fc);          
-               list_add_tail(&req->list, &fc->pending);
-               wake_up(&fc->waitq);
-               request_wait_answer(req);
-               list_del(&req->list);
+       if(req) {
+               req->in = in;
+               req->out = out;
+               req->issync = 1;
+               req->end = NULL;
+               
+               spin_lock(&fuse_lock);
+               out->h.error = -ENOTCONN;
+               if(fc->file) {
+                       in->h.unique = get_unique(fc);          
+                       list_add_tail(&req->list, &fc->pending);
+                       wake_up(&fc->waitq);
+                       request_wait_answer(req);
+                       list_del(&req->list);
+               }
+               spin_unlock(&fuse_lock);
+               request_free(req);
        }
-       spin_unlock(&fuse_lock);
-       request_free(req);
 
        up(&fc->outstanding);
 }
@@ -133,10 +149,6 @@ void request_send(struct fuse_conn *fc, struct fuse_in *in,
 static inline void destroy_request(struct fuse_req *req)
 {
        if(req) {
-               int i;
-
-               for(i = 0; i < req->in->numargs; i++)
-                       kfree(req->in->args[i].value);
                kfree(req->in);
                request_free(req);
        }
@@ -169,6 +181,42 @@ int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in)
        return 0;
 }
 
+int request_send_nonblock(struct fuse_conn *fc, struct fuse_in *in,
+                         struct fuse_out *out, fuse_reqend_t end, void *data)
+{
+       int err;
+       struct fuse_req *req;
+
+       BUG_ON(!end);
+
+       if(down_trylock(&fc->outstanding))
+               return -EWOULDBLOCK;
+
+       err = -ENOMEM;
+       req = request_new();
+       if(req) {
+               req->in = in;
+               req->out = out;
+               req->issync = 1;
+               req->end = end;
+               req->data = data;
+
+               spin_lock(&fuse_lock);
+               err = -ENOTCONN;
+               if(fc->file) {
+                       in->h.unique = get_unique(fc);          
+                       list_add_tail(&req->list, &fc->pending);
+                       wake_up(&fc->waitq);
+                       spin_unlock(&fuse_lock);
+                       return 0;
+               }
+               spin_unlock(&fuse_lock);
+               request_free(req);
+       }
+       up(&fc->outstanding);
+       return err;
+}
+
 static void request_wait(struct fuse_conn *fc)
 {
        DECLARE_WAITQUEUE(wait, current);
@@ -257,13 +305,14 @@ static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
                }
                req->locked = 0;
                if(ret < 0 || req->interrupted)
-                       wake_up(&req->waitq);
-               
-               req = NULL;
+                       /* Unlocks fuse_lock: */
+                       request_end(fc, req);
+               else
+                       spin_unlock(&fuse_lock);
+       } else {
+               spin_unlock(&fuse_lock);
+               destroy_request(req);
        }
-       spin_unlock(&fuse_lock);
-       destroy_request(req);
-
        return ret;
 }
 
@@ -452,8 +501,8 @@ static ssize_t fuse_dev_write(struct file *file, const char *buf,
        }       
        req->finished = 1;
        req->locked = 0;
-       wake_up(&req->waitq);
-       spin_unlock(&fuse_lock);
+       /* Unlocks fuse_lock: */
+       request_end(fc, req);
 
   out:
        if(!err)
@@ -523,9 +572,10 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head)
                if(req->issync) {
                        req->out->h.error = -ECONNABORTED;
                        req->finished = 1;
-                       wake_up(&req->waitq);
-               }
-               else
+                       /* Unlocks fuse_lock: */
+                       request_end(fc, req);
+                       spin_lock(&fuse_lock);
+               } else
                        destroy_request(req);
        }
 }
index 7da5bc235bda05c52daadad367fbe8de687a029f..00b1e983adc9e10ea74921fdde35c279648b1e79 100644 (file)
@@ -23,6 +23,11 @@ static struct dentry_operations fuse_dentry_opertations;
 /* FIXME: This should be user configurable */
 #define FUSE_REVALIDATE_TIME (1 * HZ)
 
+#ifndef KERNEL_2_6
+#define new_decode_dev(x) (x)
+#define new_encode_dev(x) (x)
+#endif
+
 static void change_attributes(struct inode *inode, struct fuse_attr *attr)
 {
        if(S_ISREG(inode->i_mode) && inode->i_size != attr->size) {
@@ -71,7 +76,8 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
        }
        else {
                inode->i_op = &fuse_file_inode_operations;
-               init_special_inode(inode, inode->i_mode, attr->rdev);
+               init_special_inode(inode, inode->i_mode,
+                                  new_decode_dev(attr->rdev));
        }
        inode->u.generic_ip = inode;
 }
@@ -176,7 +182,7 @@ static int _fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.mode = mode;
-       inarg.rdev = rdev;
+       inarg.rdev = new_encode_dev(rdev);
 
        in.h.opcode = FUSE_MKNOD;
        in.h.ino = dir->i_ino;
index e8fe69667f8e45b202d66b34b28f022ffd5624e7..0752943f1c262b5d54f509f9312005fbaea7ef83 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/slab.h>
 #ifdef KERNEL_2_6
 #include <linux/backing-dev.h>
+#include <linux/writeback.h>
 #endif
 
 #ifndef KERNEL_2_6
@@ -57,17 +58,13 @@ static int fuse_release(struct inode *inode, struct file *file)
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_in *in = NULL;
        struct fuse_open_in *inarg = NULL;
+       unsigned int s = sizeof(struct fuse_in) + sizeof(struct fuse_open_in);
 
-       in = kmalloc(sizeof(struct fuse_in), GFP_NOFS);
+       in = kmalloc(s, GFP_NOFS);
        if(!in)
                return -ENOMEM;
-       memset(in, 0, sizeof(struct fuse_in));
-       
-       inarg = kmalloc(sizeof(struct fuse_open_in), GFP_NOFS);
-       if(!inarg) 
-               goto out_free;
-       memset(inarg, 0, sizeof(struct fuse_open_in));
-
+       memset(in, 0, s);
+       inarg = (struct fuse_open_in *) (in + 1);
        inarg->flags = file->f_flags & ~O_EXCL;
 
        in->h.opcode = FUSE_RELEASE;
@@ -78,8 +75,6 @@ static int fuse_release(struct inode *inode, struct file *file)
        if(!request_send_noreply(fc, in))
                return 0;
 
- out_free:
-       kfree(inarg);
        kfree(in);
        return 0;
 }
@@ -102,6 +97,10 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
        in.args[0].value = &inarg;
        request_send(fc, &in, &out);
        return out.h.error;
+
+       /* FIXME: need to ensure, that all write requests issued
+           before this request are completed.  Should userspace take
+           care of this? */
 }
 
 static int fuse_readpage(struct file *file, struct page *page)
@@ -302,39 +301,130 @@ static int write_buffer(struct inode *inode, struct page *page,
        in.args[1].size = count;
        in.args[1].value = buffer + offset;
        request_send(fc, &in, &out);
-
        kunmap(page);
+       if(out.h.error)
+               SetPageError(page);
 
        return out.h.error;
 }
 
-#ifdef KERNEL_2_6
-static int fuse_writepage(struct page *page, struct writeback_control *wbc)
-#else
-static int fuse_writepage(struct page *page)
-#endif
+static int get_write_count(struct inode *inode, struct page *page)
 {
-       struct inode *inode = page->mapping->host;
-       unsigned count;
        unsigned long end_index;
-       int err;
+       int count;
        
        end_index = inode->i_size >> PAGE_CACHE_SHIFT;
        if(page->index < end_index)
                count = PAGE_CACHE_SIZE;
        else {
                count = inode->i_size & (PAGE_CACHE_SIZE - 1);
-               err = -EIO;
                if(page->index > end_index || count == 0)
-                       goto out;
+                       return 0;
+       }
+       return count;
+}
 
+#ifdef KERNEL_2_6
+
+static void write_buffer_end(struct fuse_conn *fc, struct fuse_in *in,
+                            struct fuse_out *out, void *_page)
+{
+       struct page *page = (struct page *) _page;
+       
+       lock_page(page);
+       if(out->h.error) {
+               SetPageError(page);
+               if(out->h.error == -ENOSPC)
+                       set_bit(AS_ENOSPC, &page->mapping->flags);
+               else
+                       set_bit(AS_EIO, &page->mapping->flags);
        }
-       err = write_buffer(inode, page, 0, count);
-  out:
+       end_page_writeback(page);
+       kunmap(page);
        unlock_page(page);
-       return 0;
+       kfree(in);      
 }
 
+static int write_buffer_nonblock(struct inode *inode, struct page *page,
+                                unsigned offset, size_t count)
+{
+       int err;
+       struct fuse_conn *fc = INO_FC(inode);
+       struct fuse_in *in = NULL;
+       struct fuse_out *out = NULL;
+       struct fuse_write_in *inarg = NULL;
+       char *buffer;
+       unsigned int s = sizeof(struct fuse_in) + sizeof(struct fuse_out) +
+               sizeof(struct fuse_write_in);
+
+       in = kmalloc(s, GFP_NOFS);
+       if(!in)
+               return -ENOMEM;
+       memset(in, 0, s);
+       out = (struct fuse_out *)(in + 1);
+       inarg = (struct fuse_write_in *)(out + 1);
+       
+       buffer = kmap(page);
+
+       inarg->offset = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) + offset;
+       inarg->size = count;
+       
+       in->h.opcode = FUSE_WRITE;
+       in->h.ino = inode->i_ino;
+       in->numargs = 2;
+       in->args[0].size = sizeof(struct fuse_write_in);
+       in->args[0].value = inarg;
+       in->args[1].size = count;
+       in->args[1].value = buffer + offset;
+       err = request_send_nonblock(fc, in, out, write_buffer_end, page);
+       if(err) {
+               if(err != -EWOULDBLOCK)
+                       SetPageError(page);
+               kunmap(page);
+               kfree(in);
+       }
+       return err;
+}
+
+static int fuse_writepage(struct page *page, struct writeback_control *wbc)
+{
+       int err;
+       struct inode *inode = page->mapping->host;
+       unsigned count = get_write_count(inode, page);
+
+       err = -EINVAL;
+       if(count) {
+               /* FIXME: check sync_mode, and wait for previous writes (or
+                  signal userspace to do this) */
+               if(wbc->nonblocking) {
+                       err = write_buffer_nonblock(inode, page, 0, count);
+                       if(!err)
+                               SetPageWriteback(page);
+                       else if(err == -EWOULDBLOCK) {
+                               __set_page_dirty_nobuffers(page);
+                               err = 0;
+                       }
+               } else
+                       err = write_buffer(inode, page, 0, count);
+       }
+
+       unlock_page(page);
+       return err;
+}
+#else
+static int fuse_writepage(struct page *page)
+{
+       int err;
+       struct inode *inode = page->mapping->host;
+       int count = get_write_count(inode, page);
+       err = -EINVAL;
+       if(count)
+               err = write_buffer(inode, page, 0, count);
+
+       unlock_page(page);
+       return err;
+}
+#endif
 
 static int fuse_prepare_write(struct file *file, struct page *page,
                              unsigned offset, unsigned to)
index a6671761f170d31ef31e0fe3e2c313febea80e5a..f2e55d4caf50a0d70c1e81d838893844ac8832b6 100644 (file)
@@ -103,6 +103,10 @@ struct fuse_out {
 #define FUSE_IN_INIT { {0, 0, 0, current->fsuid, current->fsgid}, 0}
 #define FUSE_OUT_INIT { {0, 0}, 0, 0}
 
+struct fuse_req;
+typedef void (*fuse_reqend_t)(struct fuse_conn *, struct fuse_in *,
+                             struct fuse_out *, void *data);
+
 /**
  * A request to the client
  */
@@ -133,6 +137,12 @@ struct fuse_req {
 
        /** Used to wake up the task waiting for completion of request*/
        wait_queue_head_t waitq;
+
+       /** Request completion callback */
+       fuse_reqend_t end;
+
+       /** User data */
+       void *data;
 };
 
 #ifdef KERNEL_2_6
@@ -205,6 +215,13 @@ void request_send(struct fuse_conn *fc, struct fuse_in *in,
  */
 int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in);
 
+
+/**
+ * Send a synchronous request without blocking
+ */
+int request_send_nonblock(struct fuse_conn *fc, struct fuse_in *in,
+                         struct fuse_out *out, fuse_reqend_t end, void *data);
+
 /**
  * Get the attributes of a file
  */
index 81a6fa89bda5e8e3c3d6b91b83edf42fd723f6e5..a868edb8eb1d72cf2e51b4b45b845890e972759c 100644 (file)
@@ -33,20 +33,16 @@ static void fuse_clear_inode(struct inode *inode)
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_in *in = NULL;
        struct fuse_forget_in *inarg = NULL;
+       unsigned int s = sizeof(struct fuse_in) + sizeof(struct fuse_forget_in);
        
        if(fc == NULL)
                return;
 
-       in = kmalloc(sizeof(struct fuse_in), GFP_NOFS);
+       in = kmalloc(s, GFP_NOFS);
        if(!in)
                return;
-       memset(in, 0, sizeof(struct fuse_in));
-
-       inarg = kmalloc(sizeof(struct fuse_forget_in), GFP_NOFS);
-       if(!inarg) 
-               goto out_free;
-       
-       memset(inarg, 0, sizeof(struct fuse_forget_in));
+       memset(in, 0, s);
+       inarg = (struct fuse_forget_in *) (in + 1);
        inarg->version = inode->i_version;
                
        in->h.opcode = FUSE_FORGET;
@@ -58,8 +54,6 @@ static void fuse_clear_inode(struct inode *inode)
        if(!request_send_noreply(fc, in))
                return;
 
-  out_free:
-       kfree(inarg);
        kfree(in);
 }