summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2004-01-14 16:56:49 +0000
committerMiklos Szeredi <miklos@szeredi.hu>2004-01-14 16:56:49 +0000
commit7c35cf9df254b3d315a0036f8102afa72f0a382d (patch)
tree6dd07298fb2f7365e73280dbf8a16c946ed17bac
parenta055d4ea6484742069fc1596bbd0e35fc7fd8782 (diff)
downloadfuse-7c35cf9df254b3d315a0036f8102afa72f0a382d.tar.gz
2.6 fixes
-rw-r--r--ChangeLog8
-rw-r--r--configure.in2
-rw-r--r--kernel/dev.c114
-rw-r--r--kernel/dir.c10
-rw-r--r--kernel/file.c138
-rw-r--r--kernel/fuse_i.h17
-rw-r--r--kernel/inode.c14
7 files changed, 234 insertions, 69 deletions
diff --git a/ChangeLog b/ChangeLog
index b065dbf..2c43391 100644
--- 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
diff --git a/configure.in b/configure.in
index 359dc68..be93170 100644
--- a/configure.in
+++ b/configure.in
@@ -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
diff --git a/kernel/dev.c b/kernel/dev.c
index 4f45658..d3c56a6 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -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);
}
}
diff --git a/kernel/dir.c b/kernel/dir.c
index 7da5bc2..00b1e98 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -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;
diff --git a/kernel/file.c b/kernel/file.c
index e8fe696..0752943 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -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)
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index a667176..f2e55d4 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -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
*/
diff --git a/kernel/inode.c b/kernel/inode.c
index 81a6fa8..a868edb 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -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);
}