aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--dev.c130
-rw-r--r--dir.c5
-rw-r--r--fuse_i.h37
-rw-r--r--inode.c76
-rw-r--r--util.c (renamed from main.c)4
6 files changed, 194 insertions, 64 deletions
diff --git a/Makefile b/Makefile
index aedd26d..f8810fd 100644
--- a/Makefile
+++ b/Makefile
@@ -17,10 +17,10 @@ inode.o: inode.c
dir.o: dir.c
$(CC) $(KCFLAGS) $(KCPPFLAGS) -c dir.c
-main.o: main.c
- $(CC) $(KCFLAGS) $(KCPPFLAGS) -c main.c
+util.o: util.c
+ $(CC) $(KCFLAGS) $(KCPPFLAGS) -c util.c
-fuse_objs = dev.o inode.o dir.o main.o
+fuse_objs = dev.o inode.o dir.o util.o
fuse.o: $(fuse_objs)
ld -r -o fuse.o $(fuse_objs)
diff --git a/dev.c b/dev.c
index c7dd247..159d8d6 100644
--- a/dev.c
+++ b/dev.c
@@ -10,69 +10,155 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/smp_lock.h>
#include <linux/poll.h>
-#include <linux/file.h>
#include <linux/proc_fs.h>
static struct proc_dir_entry *proc_fs_fuse;
struct proc_dir_entry *proc_fuse_dev;
+static struct fuse_req *request_wait(struct fuse_conn *fc)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct fuse_req *req;
+
+ spin_lock(&fuse_lock);
+ add_wait_queue(&fc->waitq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while(list_empty(&fc->pending)) {
+ if(signal_pending(current))
+ break;
+
+ spin_unlock(&fuse_lock);
+ schedule();
+ spin_lock(&fuse_lock);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&fc->waitq, &wait);
+
+ if(list_empty(&fc->pending))
+ return NULL;
+
+ req = list_entry(fc->pending.next, struct fuse_req, list);
+ list_del(&req->list);
+ spin_unlock(&fuse_lock);
+
+ return req;
+}
+
+static void request_processing(struct fuse_conn *fc, struct fuse_req *req)
+{
+ spin_lock(&fuse_lock);
+ list_add_tail(&req->list, &fc->processing);
+ fc->outstanding ++;
+ spin_unlock(&fuse_lock);
+}
+
+static void request_free(struct fuse_req *req)
+{
+ kfree(req);
+}
+
static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
loff_t *off)
{
- printk(KERN_DEBUG "fuse_dev_read\n");
- return 0;
+ ssize_t res;
+ struct fuse_conn *fc = file->private_data;
+ struct fuse_req *req;
+
+ printk(KERN_DEBUG "fuse_dev_read[%i]\n", fc->id);
+
+ res = -ERESTARTSYS;
+ req = request_wait(fc);
+ if(req == NULL)
+ goto err;
+
+ res = -EIO;
+ if(nbytes < req->size) {
+ printk("fuse_dev_read: buffer too small (%i)\n", req->size);
+ goto err_free_req;
+ }
+
+ res = -EFAULT;
+ if(copy_to_user(buf, req->data, req->size))
+ goto err_free_req;
+
+ request_processing(fc, req);
+ return req->size;
+
+ err_free_req:
+ request_free(req);
+ err:
+ return res;
}
static ssize_t fuse_dev_write(struct file *file, const char *buf,
size_t nbytes, loff_t *off)
{
- printk(KERN_DEBUG "fuse_dev_write <%.*s>\n", (int) nbytes, buf);
+ struct fuse_conn *fc = file->private_data;
+
+ printk(KERN_DEBUG "fuse_dev_write[%i] <%.*s>\n", fc->id, (int) nbytes,
+ buf);
return nbytes;
}
static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
{
- printk(KERN_DEBUG "fuse_dev_poll\n");
+ struct fuse_conn *fc = file->private_data;
+
+ printk(KERN_DEBUG "fuse_dev_poll[%i]\n", fc->id);
return 0;
}
+static struct fuse_conn *new_conn(void)
+{
+ static int connctr = 1;
+ struct fuse_conn *fc;
+
+ fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+ if(fc != NULL) {
+ fc->sb = NULL;
+ fc->file = NULL;
+ init_waitqueue_head(&fc->waitq);
+ INIT_LIST_HEAD(&fc->pending);
+ INIT_LIST_HEAD(&fc->processing);
+ fc->outstanding = 0;
+
+ spin_lock(&fuse_lock);
+ fc->id = connctr ++;
+ spin_unlock(&fuse_lock);
+ }
+ return fc;
+}
+
static int fuse_dev_open(struct inode *inode, struct file *file)
{
- int res;
struct fuse_conn *fc;
printk(KERN_DEBUG "fuse_dev_open\n");
- res = -ENOMEM;
- fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+ fc = new_conn();
if(!fc)
- goto out;
-
- fc->sb = NULL;
+ return -ENOMEM;
+
fc->file = file;
-
- lock_kernel();
file->private_data = fc;
- unlock_kernel();
- res = 0;
-
- out:
- return res;
+
+ printk(KERN_DEBUG "new connection: %i\n", fc->id);
+
+ return 0;
}
static int fuse_dev_release(struct inode *inode, struct file *file)
{
struct fuse_conn *fc = file->private_data;
- printk(KERN_DEBUG "fuse_dev_release\n");
+ printk(KERN_DEBUG "fuse_dev_release[%i]\n", fc->id);
- lock_kernel();
+ spin_lock(&fuse_lock);
fc->file = NULL;
fuse_release_conn(fc);
- unlock_kernel();
+ spin_unlock(&fuse_lock);
return 0;
}
diff --git a/dir.c b/dir.c
index afa6106..150c401 100644
--- a/dir.c
+++ b/dir.c
@@ -7,13 +7,10 @@
*/
-#include "fuse.h"
+#include "fuse_i.h"
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/smp_lock.h>
-#include <linux/sched.h>
-#include <linux/file.h>
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
{
diff --git a/fuse_i.h b/fuse_i.h
index 5e1cc1c..70e149c 100644
--- a/fuse_i.h
+++ b/fuse_i.h
@@ -7,6 +7,8 @@
*/
#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
#define FUSE_VERSION "0.1"
@@ -23,6 +25,35 @@ struct fuse_conn {
/** The opened client device */
struct file *file;
+
+ /** The client wait queue */
+ wait_queue_head_t waitq;
+
+ /** The list of pending requests */
+ struct list_head pending;
+
+ /** The list of requests being processed */
+ struct list_head processing;
+
+ /** The number of outstanding requests */
+ int outstanding;
+
+ /** Connnection number (for debuging) */
+ int id;
+};
+
+/**
+ * A filesystem request
+ */
+struct fuse_req {
+ /** The request list */
+ struct list_head list;
+
+ /** The size of the data */
+ size_t size;
+
+ /** A pointer to the data */
+ void *data;
};
/**
@@ -31,11 +62,15 @@ struct fuse_conn {
extern struct proc_dir_entry *proc_fuse_dev;
/**
+ * The lock to protect fuses structures
+ */
+extern spinlock_t fuse_lock;
+
+/**
* Fill in the directory operations
*/
void fuse_dir_init(struct inode *inode);
-
/**
* Check if the connection can be released, and if yes, then free the
* connection structure
diff --git a/inode.c b/inode.c
index 77040fc..da30cac 100644
--- a/inode.c
+++ b/inode.c
@@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/smp_lock.h>
#include <linux/sched.h>
#include <linux/file.h>
@@ -28,10 +27,13 @@ static void fuse_put_super(struct super_block *sb)
{
struct fuse_conn *fc = sb->u.generic_sbp;
- printk(KERN_DEBUG "fuse_put_super\n");
+ printk(KERN_DEBUG "fuse_put_super[%i]\n", fc->id);
+ spin_lock(&fuse_lock);
fc->sb = NULL;
fuse_release_conn(fc);
+ spin_unlock(&fuse_lock);
+
}
static struct super_operations fuse_super_operations = {
@@ -76,60 +78,68 @@ static struct fuse_conn *get_conn(struct fuse_mount_data *d)
}
+static struct inode *get_root_inode(struct super_block *sb)
+{
+ struct inode *root ;
+
+ root = iget(sb, 1);
+ if(root) {
+ root->i_mode = S_IFDIR;
+ root->i_uid = 0;
+ root->i_gid = 0;
+ root->i_nlink = 2;
+ root->i_size = 0;
+ root->i_blksize = 1024;
+ root->i_blocks = 0;
+ root->i_atime = CURRENT_TIME;
+ root->i_mtime = CURRENT_TIME;
+ root->i_ctime = CURRENT_TIME;
+ fuse_dir_init(root);
+ }
+
+ return root;
+}
+
static struct super_block *fuse_read_super(struct super_block *sb,
void *data, int silent)
{
struct fuse_conn *fc;
struct inode *root;
- fc = get_conn(data);
- if(fc == NULL)
- goto err;
-
- if(fc->sb != NULL) {
- printk("fuse_read_super: file already mounted\n");
- goto err;
- }
-
- sb->u.generic_sbp = fc;
sb->s_blocksize = 1024;
sb->s_blocksize_bits = 10;
sb->s_magic = FUSE_SUPER_MAGIC;
sb->s_op = &fuse_super_operations;
- root = iget(sb, 1);
- if(!root) {
+ root = get_root_inode(sb);
+ if(root == NULL) {
printk("fuse_read_super: failed to get root inode\n");
- goto err;
+ return NULL;
}
printk(KERN_DEBUG "root inode: %ld/%d\n", root->i_ino, root->i_dev);
- root->i_mode = S_IFDIR;
- root->i_uid = 0;
- root->i_gid = 0;
- root->i_nlink = 2;
- root->i_size = 0;
- root->i_blksize = 1024;
- root->i_blocks = 0;
- root->i_atime = CURRENT_TIME;
- root->i_mtime = CURRENT_TIME;
- root->i_ctime = CURRENT_TIME;
- fuse_dir_init(root);
+ spin_lock(&fuse_lock);
+ fc = get_conn(data);
+ if(fc == NULL)
+ goto err;
- sb->s_root = d_alloc_root(root);
- if(!sb->s_root) {
- printk("fuse_read_super: failed to allocate root dentry\n");
- goto err_iput;
+ if(fc->sb != NULL) {
+ printk("fuse_read_super: connection %i already mounted\n",
+ fc->id);
+ goto err;
}
+ sb->u.generic_sbp = fc;
+ sb->s_root = d_alloc_root(root);
fc->sb = sb;
-
+ spin_unlock(&fuse_lock);
+
return sb;
- err_iput:
- iput(root);
err:
+ spin_unlock(&fuse_lock);
+ iput(root);
return NULL;
}
diff --git a/main.c b/util.c
index fa4ca8c..281725c 100644
--- a/main.c
+++ b/util.c
@@ -15,11 +15,13 @@
#define FUSE_VERSION "0.1"
+spinlock_t fuse_lock = SPIN_LOCK_UNLOCKED;
+/* Must be called with the fuse lock held */
void fuse_release_conn(struct fuse_conn *fc)
{
if(fc->sb == NULL && fc->file == NULL) {
- printk(KERN_DEBUG "fuse: release connection\n");
+ printk(KERN_DEBUG "fuse: release connection: %i\n", fc->id);
kfree(fc);
}
}