fixes and improvements
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 16 Jul 2004 16:17:02 +0000 (16:17 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Fri, 16 Jul 2004 16:17:02 +0000 (16:17 +0000)
ChangeLog
kernel/dev.c
kernel/dir.c
kernel/file.c
kernel/fuse_i.h
kernel/inode.c

index 95d0e2cd082d7db3a6bcd5f977bcc48be2bb9e2d..55769c8fce0d5d34d17925557558b95b5872eec2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2004-07-16  Miklos Szeredi <miklos@szeredi.hu>
+
+       * Separate directory entry and inode attribute validity timer
+
+       * New write semaphore to stop page writeback during truncate
+
+       * Fsync now waits for all writes to complete before sending the
+       request
+
+       * Optimization: if a page is completely written by
+       fuse_commit_write(), clear the dirty flag and set the uptodate
+       flag for that page
+
 2004-07-13  Miklos Szeredi <miklos@szeredi.hu>
 
        * Add FUSE_HARD_REMOVE flag, and '-i' option to fuse main, which
index a7dacb88a12b7cac5705c811a882f3c04e10ac66..bc59d9952f8f80baf8754b9c0c4ad34680e7d22b 100644 (file)
@@ -96,6 +96,16 @@ static int get_unique(struct fuse_conn *fc)
        return fc->reqctr;
 }
 
+void fuse_reset_request(struct fuse_req *req)
+{
+       int preallocated = req->preallocated;
+       
+       memset(req, 0, sizeof(*req));
+       INIT_LIST_HEAD(&req->list);
+       init_waitqueue_head(&req->waitq);
+       req->preallocated = preallocated;
+}
+
 static struct fuse_req *do_get_request(struct fuse_conn *fc)
 {
        struct fuse_req *req;
@@ -105,12 +115,7 @@ static struct fuse_req *do_get_request(struct fuse_conn *fc)
        req = list_entry(fc->unused_list.next, struct fuse_req, list);
        list_del_init(&req->list);
        spin_unlock(&fuse_lock);
-       
-       memset(req, 0, sizeof(*req));
-       INIT_LIST_HEAD(&req->list);
-       init_waitqueue_head(&req->waitq);
-       req->preallocated = 1;
-
+       fuse_reset_request(req);
        return req;
 }
 
@@ -422,7 +427,7 @@ static int fuse_invalidate(struct fuse_conn *fc, struct fuse_user_header *uh)
        struct inode *inode = iget(fc->sb, uh->ino);
        int err = -ENOENT;
        if (inode) {
-               if (inode->u.generic_ip) {
+               if (FUSE_FI(inode)) {
                        invalidate_inode_pages(inode);
                        err = 0;
                }
@@ -577,6 +582,7 @@ static struct fuse_conn *new_conn(void)
                                free_conn(fc);
                                return NULL;
                        }
+                       req->preallocated = 1;
                        list_add(&req->list, &fc->unused_list);
                }
                fc->reqctr = 1;
index 519e0444c7821896ae1a5dae60513aeba10a9f21..7a8567cdce8244134cf69b183ead91034074cced 100644 (file)
@@ -87,14 +87,14 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation,
 
        inode = iget(sb, ino);
        if (inode) {
-               if (!inode->u.generic_ip) {
-                       struct fuse_req *req = fuse_request_alloc();
-                       if (!req) {
+               if (!INO_FI(inode)) {
+                       struct fuse_inode *fi = fuse_inode_alloc();
+                       if (!fi) {
                                iput(inode);
                                inode = NULL;
                                goto out;
                        }
-                       inode->u.generic_ip = req;
+                       INO_FI(inode) = fi;
                        inode->i_generation = generation;
                        fuse_init_inode(inode, attr);
                } else if (inode->i_generation != generation)
@@ -182,25 +182,23 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
        if (err && err != -ENOENT)
                return err;
 
-       if (inode)
-               entry->d_time = time_to_jiffies(outarg.entry_valid,
+       if (inode) {
+               struct fuse_inode *fi = INO_FI(inode);
+               entry->d_time = time_to_jiffies(outarg.entry_valid,
                                                outarg.entry_valid_nsec);
+               fi->i_time = time_to_jiffies(outarg.attr_valid,
+                                            outarg.attr_valid_nsec);
+       }
 
        entry->d_op = &fuse_dentry_operations;
        *inodep = inode;
        return 0;
 }
 
-static void uncache_dir(struct inode *dir)
+static void fuse_invalidate_attr(struct inode *inode)
 {
-       struct dentry *entry = d_find_alias(dir);
-       if (!entry)
-               dir->i_nlink = 0;
-       else {
-               /* FIXME: this should reset the _attribute_ timeout */
-               entry->d_time = jiffies - 1;
-               dput(entry);
-       }
+       struct fuse_inode *fi = INO_FI(inode);
+       fi->i_time = jiffies - 1;
 }
 
 static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req,
@@ -209,6 +207,7 @@ static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req,
                            int mode)
 {
        struct inode *inode;
+       struct fuse_inode *fi;
        inode = fuse_iget(dir->i_sb, outarg->ino, outarg->generation, 
                          &outarg->attr, version);
        if (!inode) {
@@ -227,8 +226,12 @@ static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req,
        entry->d_time = time_to_jiffies(outarg->entry_valid,
                                        outarg->entry_valid_nsec);
 
+       fi = INO_FI(inode);
+       fi->i_time = time_to_jiffies(outarg->attr_valid,
+                                    outarg->attr_valid_nsec);
+
        d_instantiate(entry, inode);
-       uncache_dir(dir);
+       fuse_invalidate_attr(dir);
        return 0;
 }
 
@@ -360,12 +363,14 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
        request_send(fc, req);
        err = req->out.h.error;
        if (!err) {
+               struct inode *inode = entry->d_inode;
+               
                /* Set nlink to zero so the inode can be cleared, if
                    the inode does have more links this will be
                    discovered at the next lookup/getattr */
-               /* FIXME: mark inode "not uptodate" */
-               entry->d_inode->i_nlink = 0;
-               uncache_dir(dir);
+               inode->i_nlink = 0;
+               fuse_invalidate_attr(inode);
+               fuse_invalidate_attr(dir);
        }
        fuse_put_request(fc, req);
        return err;
@@ -389,7 +394,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
        err = req->out.h.error;
        if (!err) {
                entry->d_inode->i_nlink = 0;
-               uncache_dir(dir);
+               fuse_invalidate_attr(dir);
        }
        fuse_put_request(fc, req);
        return err;
@@ -421,9 +426,9 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err) {
-               uncache_dir(olddir);
+               fuse_invalidate_attr(olddir);
                if (olddir != newdir)
-                       uncache_dir(newdir);
+                       fuse_invalidate_attr(newdir);
        }
        return err;
 }
@@ -467,6 +472,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
 
 int fuse_do_getattr(struct inode *inode)
 {
+       struct fuse_inode *fi = INO_FI(inode);
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_req *req = fuse_get_request(fc);
        struct fuse_attr_out arg;
@@ -482,8 +488,11 @@ int fuse_do_getattr(struct inode *inode)
        req->out.args[0].value = &arg;
        request_send(fc, req);
        err = req->out.h.error; 
-       if (!err)
+       if (!err) {
                change_attributes(inode, &arg.attr);
+               fi->i_time = time_to_jiffies(arg.attr_valid,
+                                            arg.attr_valid_nsec);
+       }
        fuse_put_request(fc, req);
        return err;
 }
@@ -491,13 +500,14 @@ int fuse_do_getattr(struct inode *inode)
 static int fuse_revalidate(struct dentry *entry)
 {
        struct inode *inode = entry->d_inode;
+       struct fuse_inode *fi = INO_FI(inode);
        struct fuse_conn *fc = INO_FC(inode);
 
        if (inode->i_ino == FUSE_ROOT_INO) {
                if (!(fc->flags & FUSE_ALLOW_OTHER) &&
                    current->fsuid != fc->uid)
                        return -EACCES;
-       } else if (!entry->d_time || time_before_eq(jiffies, entry->d_time))
+       } else if (!fi->i_time || time_before_eq(jiffies, fi->i_time))
                return 0;
 
        return fuse_do_getattr(inode);
@@ -745,19 +755,33 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
 {
        struct inode *inode = entry->d_inode;
        struct fuse_conn *fc = INO_FC(inode);
+       struct fuse_inode *fi = INO_FI(inode);
        struct fuse_req *req;
        struct fuse_setattr_in inarg;
        struct fuse_attr_out outarg;
        int err;
+       int is_truncate = 0;
+       
+
+       if (attr->ia_valid & ATTR_SIZE) {
+               unsigned long limit;
+               is_truncate = 1;
 
-       /* FIXME: need to fix race between truncate and writepage */
-       if (attr->ia_valid & ATTR_SIZE) 
-               fuse_sync_inode(inode);
+               limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+               if (limit != RLIM_INFINITY && attr->ia_size > limit) {
+                       send_sig(SIGXFSZ, current, 0);
+                       return -EFBIG;
+               }
+               //fuse_sync_inode(inode);
+       }
 
        req = fuse_get_request(fc);
        if (!req)
                return -ERESTARTSYS;
 
+       if (is_truncate)
+               down_write(&fi->write_sem);
+
        memset(&inarg, 0, sizeof(inarg));
        inarg.valid = iattr_to_fattr(attr, &inarg.attr);
        req->in.h.opcode = FUSE_SETATTR;
@@ -770,14 +794,22 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
        req->out.args[0].value = &outarg;
        request_send(fc, req);
        err = req->out.h.error;
-       if (!err) {
-               if (attr->ia_valid & ATTR_SIZE &&
-                   outarg.attr.size < i_size_read(inode))
-                       vmtruncate(inode, outarg.attr.size);
+       fuse_put_request(fc, req);
 
+       if (!err) {
+               if (is_truncate) {
+                       loff_t origsize = i_size_read(inode);
+                       i_size_write(inode, outarg.attr.size);
+                       up_write(&fi->write_sem);
+                       if (origsize > outarg.attr.size)
+                               vmtruncate(inode, outarg.attr.size);
+               }
                change_attributes(inode, &outarg.attr);
-       } 
-       fuse_put_request(fc, req);
+               fi->i_time = time_to_jiffies(outarg.attr_valid,
+                                            outarg.attr_valid_nsec);
+       } else if (is_truncate)
+               up_write(&fi->write_sem);
+               
        return err;
 }
 
@@ -787,6 +819,7 @@ static int _fuse_dentry_revalidate(struct dentry *entry)
                return 0;
        else if (entry->d_time && time_after(jiffies, entry->d_time)) {
                struct inode *inode = entry->d_inode;
+               struct fuse_inode *fi = INO_FI(inode);
                struct fuse_entry_out outarg;
                int version;
                int ret;
@@ -803,6 +836,8 @@ static int _fuse_dentry_revalidate(struct dentry *entry)
                inode->i_version = version;
                entry->d_time = time_to_jiffies(outarg.entry_valid,
                                                outarg.entry_valid_nsec);
+               fi->i_time = time_to_jiffies(outarg.attr_valid,
+                                            outarg.attr_valid_nsec);
        }
        return 1;
 }
index 8e32f45928195c78a14144cccd94a99e8172f9ea..2a6fb4b025dfac56e68dc49dac0f11ffa6d36311 100644 (file)
@@ -145,6 +145,7 @@ static int fuse_flush(struct file *file)
 static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
 {
        struct inode *inode = de->d_inode;
+       struct fuse_inode *fi = INO_FI(inode);
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_req *req;
        struct fuse_fsync_in inarg;
@@ -156,7 +157,12 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
        req = fuse_get_request(fc);
        if (!req)
                return -ERESTARTSYS;
-       
+
+       /* Make sure all writes to this inode are completed before
+          issuing the FSYNC request */
+       down_write(&fi->write_sem);
+       up_write(&fi->write_sem);
+
        memset(&inarg, 0, sizeof(inarg));
        inarg.datasync = datasync;
        req->in.h.opcode = FUSE_FSYNC;
@@ -172,10 +178,6 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
        }
        fuse_put_request(fc, req);
        return err;
-
-       /* FIXME: need to ensure, that all write requests issued
-           before this request are completed.  Should userspace take
-           care of this? */
 }
 
 static ssize_t fuse_send_read(struct inode *inode, char *buf, loff_t pos,
@@ -392,34 +394,29 @@ static ssize_t fuse_file_read(struct file *file, char *buf,
        struct fuse_conn *fc = INO_FC(inode);
        ssize_t res;
 
-       down(&inode->i_sem);
        if (fc->flags & FUSE_DIRECT_IO) {
                res = fuse_read(file, buf, count, ppos);
        }
        else {
-               if (fc->flags & FUSE_LARGE_READ)
+               if (fc->flags & FUSE_LARGE_READ) {
+                       down(&inode->i_sem);
                        fuse_file_bigread(inode, *ppos, count);
-               
+                       up(&inode->i_sem);
+               }
                res = generic_file_read(file, buf, count, ppos);
        }
-       up(&inode->i_sem);
 
        return res;
 }  
 
-static ssize_t fuse_send_write(struct inode *inode, const char *buf,
-                              loff_t pos, size_t count)
+static ssize_t fuse_send_write(struct fuse_req *req, struct inode *inode,
+                              const char *buf, loff_t pos, size_t count)
 {
        struct fuse_conn *fc = INO_FC(inode);
-       struct fuse_req *req;
        struct fuse_write_in inarg;
        struct fuse_write_out outarg;
        ssize_t res;
        
-       req = fuse_get_request(fc);
-       if (!req)
-               return -ERESTARTSYS;
-       
        memset(&inarg, 0, sizeof(inarg));
        inarg.offset = pos;
        inarg.size = count;
@@ -436,21 +433,28 @@ static ssize_t fuse_send_write(struct inode *inode, const char *buf,
        request_send(fc, req);
        res = req->out.h.error;
        if (!res)
-               res = outarg.size;
-       fuse_put_request(fc, req);
-       return res;
+               return outarg.size;
+       else
+               return res;
 }
 
 static int write_buffer(struct inode *inode, struct page *page,
                        unsigned offset, size_t count)
 {
+       struct fuse_conn *fc = INO_FC(inode);
        char *buffer;
        ssize_t res;
        loff_t pos;
+       struct fuse_req *req;
 
+       req = fuse_get_request(fc);
+       if (!req)
+               return -ERESTARTSYS;
+       
        pos = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) + offset;
        buffer = kmap(page);
-       res = fuse_send_write(inode, buffer + offset, pos, count);
+       res = fuse_send_write(req, inode, buffer + offset, pos, count);
+       fuse_put_request(fc, req);
        if (res >= 0) {
                if (res < count) {
                        printk("fuse: short write\n");
@@ -481,11 +485,53 @@ static int get_write_count(struct inode *inode, struct page *page)
        return count;
 }
 
+
+static int write_page_block(struct inode *inode, struct page *page)
+{
+       struct fuse_conn *fc = INO_FC(inode);
+       struct fuse_inode *fi = INO_FI(inode);
+       char *buffer;
+       ssize_t res;
+       loff_t pos;
+       unsigned count;
+       struct fuse_req *req;
+
+       req = fuse_get_request(fc);
+       if (!req)
+               return -ERESTARTSYS;
+       
+       down_read(&fi->write_sem);
+       count = get_write_count(inode, page);
+       res = 0;
+       if (count) {
+               pos = ((unsigned long long) page->index << PAGE_CACHE_SHIFT);
+               buffer = kmap(page);
+               res = fuse_send_write(req, inode, buffer, pos, count);
+               if (res >= 0) {
+                       if (res < count) {
+                               printk("fuse: short write\n");
+                               res = -EPROTO;
+                       } else
+                               res = 0;
+               }
+       }
+       up_read(&fi->write_sem);
+       fuse_put_request(fc, req);
+       kunmap(page);
+       if (res)
+               SetPageError(page);
+       return res;
+}
+
+
 #ifdef KERNEL_2_6
 
-static void write_buffer_end(struct fuse_conn *fc, struct fuse_req *req)
+
+static void write_page_nonblock_end(struct fuse_conn *fc, struct fuse_req *req)
 {
        struct page *page = (struct page *) req->data;
+       struct inode *inode = page->mapping->host;
+       struct fuse_inode *fi = INO_FI(inode);
        struct fuse_write_out *outarg = req->out.args[0].value;
        if (!req->out.h.error && outarg->size != req->in.args[1].size) {
                printk("fuse: short write\n");
@@ -499,26 +545,23 @@ static void write_buffer_end(struct fuse_conn *fc, struct fuse_req *req)
                else
                        set_bit(AS_EIO, &page->mapping->flags);
        }
+       up_read(&fi->write_sem);
+
        end_page_writeback(page);
        kunmap(page);
        fuse_put_request(fc, req);
 }
 
-static int write_buffer_nonblock(struct inode *inode, struct page *page,
-                                unsigned offset, size_t count)
+static void send_write_nonblock(struct fuse_req *req, struct inode *inode,
+                                   struct page *page, unsigned count)
 {
        struct fuse_conn *fc = INO_FC(inode);
-       struct fuse_req *req;
        struct fuse_write_in *inarg;
        char *buffer;
 
-       req = fuse_get_request_nonblock(fc);
-       if (!req)
-               return -EWOULDBLOCK;
-
        inarg = &req->misc.write.in;
        buffer = kmap(page);
-       inarg->offset = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) + offset;
+       inarg->offset = ((unsigned long long) page->index << PAGE_CACHE_SHIFT);
        inarg->size = count;
        req->in.h.opcode = FUSE_WRITE;
        req->in.h.ino = inode->i_ino;
@@ -526,36 +569,52 @@ static int write_buffer_nonblock(struct inode *inode, struct page *page,
        req->in.args[0].size = sizeof(struct fuse_write_in);
        req->in.args[0].value = inarg;
        req->in.args[1].size = count;
-       req->in.args[1].value = buffer + offset;
+       req->in.args[1].value = buffer;
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(struct fuse_write_out);
        req->out.args[0].value = &req->misc.write.out;
-       request_send_nonblock(fc, req, write_buffer_end, page);
-       return 0;
+       request_send_nonblock(fc, req, write_page_nonblock_end, page);
 }
 
-static int fuse_writepage(struct page *page, struct writeback_control *wbc)
+static int write_page_nonblock(struct inode *inode, struct page *page)
 {
+       struct fuse_conn *fc = INO_FC(inode);
+       struct fuse_inode *fi = INO_FI(inode);
+       struct fuse_req *req;
        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) {
-                       SetPageWriteback(page);
-                       err = write_buffer_nonblock(inode, page, 0, count);
-                       if (err)
-                               ClearPageWriteback(page);
-                       if (err == -EWOULDBLOCK) {
-                               redirty_page_for_writepage(wbc, page);
-                               err = 0;
+       err = -EWOULDBLOCK;
+       req = fuse_get_request_nonblock(fc);
+       if (req) {
+               if (down_read_trylock(&fi->write_sem)) {
+                       unsigned count;
+                       err = 0;
+                       count = get_write_count(inode, page);
+                       if (count) {
+                               SetPageWriteback(page);         
+                               send_write_nonblock(req, inode, page, count);
+                               return 0;
                        }
-               } else
-                       err = write_buffer(inode, page, 0, count);
+                       up_read(&fi->write_sem);
+               }
+               fuse_put_request(fc, req);
        }
+       return err;
+}
+
+static int fuse_writepage(struct page *page, struct writeback_control *wbc)
+{
+       int err;
+       struct inode *inode = page->mapping->host;
+
+       if (wbc->nonblocking) {
+               err = write_page_nonblock(inode, page);
+               if (err == -EWOULDBLOCK) {
+                       redirty_page_for_writepage(wbc, page);
+                       err = 0;
+               }
+       } else
+               err = write_page_block(inode, page);
 
        unlock_page(page);
        return err;
@@ -563,13 +622,7 @@ static int fuse_writepage(struct page *page, struct writeback_control *wbc)
 #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);
-
+       int err = write_page_block(page->mapping->host, page);
        unlock_page(page);
        return err;
 }
@@ -593,6 +646,12 @@ static int fuse_commit_write(struct file *file, struct page *page,
                loff_t pos = (page->index << PAGE_CACHE_SHIFT) + to;
                if (pos > i_size_read(inode))
                        i_size_write(inode, pos);
+               
+               if (offset == 0 && to == PAGE_CACHE_SIZE) {
+                       clear_page_dirty(page);
+                       SetPageUptodate(page);
+               }
+
        }
        return err;
 }
@@ -605,11 +664,18 @@ static ssize_t fuse_write(struct file *file, const char *buf, size_t count,
        char *tmpbuf;
        ssize_t res = 0;
        loff_t pos = *ppos;
+       struct fuse_req *req;
+
+       req = fuse_get_request(fc);
+       if (!req)
+               return -ERESTARTSYS;
 
        tmpbuf = kmalloc(count < fc->max_write ? count : fc->max_write,
                         GFP_KERNEL);
-       if (!tmpbuf)
+       if (!tmpbuf) {
+               fuse_put_request(fc, req);
                return -ENOMEM;
+       }
 
        while (count) {
                size_t nbytes = count < fc->max_write ? count : fc->max_write;
@@ -618,7 +684,7 @@ static ssize_t fuse_write(struct file *file, const char *buf, size_t count,
                        res = -EFAULT;
                        break;
                }
-               res1 = fuse_send_write(inode, tmpbuf, pos, nbytes);
+               res1 = fuse_send_write(req, inode, tmpbuf, pos, nbytes);
                if (res1 < 0) {
                        res = res1;
                        break;
@@ -629,8 +695,12 @@ static ssize_t fuse_write(struct file *file, const char *buf, size_t count,
                pos += res1;
                if (res1 < nbytes)
                        break;
+
+               if (count)
+                       fuse_reset_request(req);
        }
        kfree(tmpbuf);
+       fuse_put_request(fc, req);
 
        if (res > 0) {
                if (pos > i_size_read(inode))
index fdd462525f6b4b912dfd9d150b67b8ef963f8acd..4b0399074f6c65926dd8738c3e4f174303285306 100644 (file)
@@ -65,6 +65,13 @@ permission checking is done in the kernel */
 /** Bypass the page cache for read and write operations  */
 #define FUSE_DIRECT_IO           (1 << 4)
 
+/** FUSE specific inode data */
+struct fuse_inode {
+       struct fuse_req *forget_req;
+       struct rw_semaphore write_sem;
+       unsigned long i_time;
+};
+
 /** One input argument of a request */
 struct fuse_in_arg {
        unsigned int size;
@@ -223,7 +230,8 @@ struct fuse_getdir_out_i {
 #define SB_FC(sb) ((sb)->u.generic_sbp)
 #endif
 #define INO_FC(inode) SB_FC((inode)->i_sb)
-#define DEV_FC(file) ((struct fuse_conn *) (file)->private_data)
+#define DEV_FC(file) ((file)->private_data)
+#define INO_FI(inode) ((inode)->u.generic_ip)
 
 
 /**
@@ -292,6 +300,11 @@ struct fuse_req *fuse_request_alloc(void);
  */
 void fuse_request_free(struct fuse_req *req);
 
+/**
+ * Reinitialize a request, the preallocated flag is left unmodified
+ */
+void fuse_reset_request(struct fuse_req *req);
+
 /**
  * Reserve a preallocated request
  */
@@ -333,6 +346,11 @@ int fuse_do_getattr(struct inode *inode);
  */
 void fuse_sync_inode(struct inode *inode);
 
+/**
+ * Allocate fuse specific inode data
+ */
+struct fuse_inode *fuse_inode_alloc(void);
+
 /*
  * Local Variables:
  * indent-tabs-mode: t
index 51eed459f56f587cb113d458c96d6356bd75cc80..8c1cba3f87e1ef881b52f300b2366329021d1573 100644 (file)
@@ -24,6 +24,7 @@
 
 
 static int user_allow_other;
+static kmem_cache_t *fuse_inode_cachep;
 
 #ifdef KERNEL_2_6
 #include <linux/moduleparam.h>
@@ -53,6 +54,29 @@ struct fuse_mount_data {
        unsigned int max_read;
 };
 
+struct fuse_inode *fuse_inode_alloc(void)
+{
+       struct fuse_inode *fi;
+
+       fi = kmem_cache_alloc(fuse_inode_cachep, SLAB_KERNEL);
+       if (fi) {
+               memset(fi, 0, sizeof(*fi));
+               fi->forget_req = fuse_request_alloc();
+               if (!fi->forget_req) {
+                       kmem_cache_free(fuse_inode_cachep, fi);
+                       fi = NULL;
+               } else
+                       init_rwsem(&fi->write_sem);
+       }
+
+       return fi;
+}
+
+static void fuse_inode_free(struct fuse_inode *fi)
+{
+       kmem_cache_free(fuse_inode_cachep, fi);
+}
+
 static void fuse_read_inode(struct inode *inode)
 {
        /* No op */
@@ -74,15 +98,16 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, ino_t ino,
 static void fuse_clear_inode(struct inode *inode)
 {
        struct fuse_conn *fc = INO_FC(inode);
-       struct fuse_req *req = inode->u.generic_ip;
+       struct fuse_inode *fi = INO_FI(inode);
        
-       if (fc == NULL) {
-               if (req)
-                       fuse_request_free(req);
-               return;
+       if (fi) {
+               if (fc == NULL)
+                       fuse_request_free(fi->forget_req);
+               else 
+                       fuse_send_forget(fc, fi->forget_req, inode->i_ino,
+                                        inode->i_version);
+               fuse_inode_free(fi);
        }
-       if (req != NULL)
-               fuse_send_forget(fc, req, inode->i_ino, inode->i_version);
 }
 
 static void fuse_put_super(struct super_block *sb)
@@ -409,18 +434,28 @@ static DECLARE_FSTYPE(fuse_fs_type, "fuse", fuse_read_super_compat, 0);
 
 int fuse_fs_init()
 {
-       int res;
+       int err;
 
-       res = register_filesystem(&fuse_fs_type);
-       if (res)
+       err = register_filesystem(&fuse_fs_type);
+       if (err)
                printk("fuse: failed to register filesystem\n");
+       else {
+               fuse_inode_cachep = kmem_cache_create("fuse_inode",
+                                                     sizeof(struct fuse_inode),
+                                                     0, 0, NULL, NULL);
+               if (!fuse_inode_cachep) {
+                       unregister_filesystem(&fuse_fs_type);
+                       err = -ENOMEM;
+               }
+       }
 
-       return res;
+       return err;
 }
 
 void fuse_fs_cleanup()
 {
        unregister_filesystem(&fuse_fs_type);
+       kmem_cache_destroy(fuse_inode_cachep);
 }
 
 /*