aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--kernel/dev.c20
-rw-r--r--kernel/dir.c101
-rw-r--r--kernel/file.c186
-rw-r--r--kernel/fuse_i.h20
-rw-r--r--kernel/inode.c57
6 files changed, 287 insertions, 110 deletions
diff --git a/ChangeLog b/ChangeLog
index 95d0e2c..55769c8 100644
--- 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
diff --git a/kernel/dev.c b/kernel/dev.c
index a7dacb8..bc59d99 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -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;
diff --git a/kernel/dir.c b/kernel/dir.c
index 519e044..7a8567c 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -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;
}
diff --git a/kernel/file.c b/kernel/file.c
index 8e32f45..2a6fb4b 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -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))
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index fdd4625..4b03990 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -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)
/**
@@ -293,6 +301,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
*/
struct fuse_req *fuse_get_request(struct fuse_conn *fc);
@@ -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
diff --git a/kernel/inode.c b/kernel/inode.c
index 51eed45..8c1cba3 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -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);
}
/*