Commit 334f485d authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Linus Torvalds

[PATCH] FUSE - device functions

This adds the FUSE device handling functions.

This contains the following files:

 o dev.c
    - fuse device operations (read, write, release, poll)
    - registers misc device
    - support for sending requests to userspace
Signed-off-by: default avatarMiklos Szeredi <miklos@szeredi.hu>
Signed-off-by: default avatarAdrian Bunk <bunk@stusta.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent d8a5ba45
This diff is collapsed.
...@@ -4,4 +4,4 @@ ...@@ -4,4 +4,4 @@
obj-$(CONFIG_FUSE_FS) += fuse.o obj-$(CONFIG_FUSE_FS) += fuse.o
fuse-objs := inode.o fuse-objs := dev.o inode.o
This diff is collapsed.
...@@ -15,6 +15,12 @@ ...@@ -15,6 +15,12 @@
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
/** Max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32
/** If more requests are outstanding, then the operation will block */
#define FUSE_MAX_OUTSTANDING 10
/** FUSE inode */ /** FUSE inode */
struct fuse_inode { struct fuse_inode {
/** Inode data */ /** Inode data */
...@@ -28,6 +34,123 @@ struct fuse_inode { ...@@ -28,6 +34,123 @@ struct fuse_inode {
unsigned long i_time; unsigned long i_time;
}; };
/** One input argument of a request */
struct fuse_in_arg {
unsigned size;
const void *value;
};
/** The request input */
struct fuse_in {
/** The request header */
struct fuse_in_header h;
/** True if the data for the last argument is in req->pages */
unsigned argpages:1;
/** Number of arguments */
unsigned numargs;
/** Array of arguments */
struct fuse_in_arg args[3];
};
/** One output argument of a request */
struct fuse_arg {
unsigned size;
void *value;
};
/** The request output */
struct fuse_out {
/** Header returned from userspace */
struct fuse_out_header h;
/** Last argument is variable length (can be shorter than
arg->size) */
unsigned argvar:1;
/** Last argument is a list of pages to copy data to */
unsigned argpages:1;
/** Zero partially or not copied pages */
unsigned page_zeroing:1;
/** Number or arguments */
unsigned numargs;
/** Array of arguments */
struct fuse_arg args[3];
};
struct fuse_req;
struct fuse_conn;
/**
* A request to the client
*/
struct fuse_req {
/** This can be on either unused_list, pending or processing
lists in fuse_conn */
struct list_head list;
/** refcount */
atomic_t count;
/** True if the request has reply */
unsigned isreply:1;
/** The request is preallocated */
unsigned preallocated:1;
/** The request was interrupted */
unsigned interrupted:1;
/** Request is sent in the background */
unsigned background:1;
/** Data is being copied to/from the request */
unsigned locked:1;
/** Request has been sent to userspace */
unsigned sent:1;
/** The request is finished */
unsigned finished:1;
/** The request input */
struct fuse_in in;
/** The request output */
struct fuse_out out;
/** Used to wake up the task waiting for completion of request*/
wait_queue_head_t waitq;
/** Data for asynchronous requests */
union {
struct fuse_init_in_out init_in_out;
} misc;
/** page vector */
struct page *pages[FUSE_MAX_PAGES_PER_REQ];
/** number of pages in vector */
unsigned num_pages;
/** offset of data on first page */
unsigned page_offset;
/** Inode used in the request */
struct inode *inode;
/** Second inode used in the request (or NULL) */
struct inode *inode2;
/** File used in the request (or NULL) */
struct file *file;
};
/** /**
* A Fuse connection. * A Fuse connection.
* *
...@@ -39,9 +162,37 @@ struct fuse_conn { ...@@ -39,9 +162,37 @@ struct fuse_conn {
/** The superblock of the mounted filesystem */ /** The superblock of the mounted filesystem */
struct super_block *sb; struct super_block *sb;
/** The opened client device */
struct file *file;
/** The user id for this mount */ /** The user id for this mount */
uid_t user_id; uid_t user_id;
/** Readers of the connection are waiting on this */
wait_queue_head_t waitq;
/** The list of pending requests */
struct list_head pending;
/** The list of requests being processed */
struct list_head processing;
/** Controls the maximum number of outstanding requests */
struct semaphore outstanding_sem;
/** This counts the number of outstanding requests if
outstanding_sem would go negative */
unsigned outstanding_debt;
/** The list of unused requests */
struct list_head unused_list;
/** The next unique request id */
u64 reqctr;
/** Connection failed (version mismatch) */
unsigned conn_error : 1;
/** Backing dev info */ /** Backing dev info */
struct backing_dev_info bdi; struct backing_dev_info bdi;
}; };
...@@ -71,13 +222,20 @@ static inline u64 get_node_id(struct inode *inode) ...@@ -71,13 +222,20 @@ static inline u64 get_node_id(struct inode *inode)
return get_fuse_inode(inode)->nodeid; return get_fuse_inode(inode)->nodeid;
} }
/** Device operations */
extern struct file_operations fuse_dev_operations;
/** /**
* This is the single global spinlock which protects FUSE's structures * This is the single global spinlock which protects FUSE's structures
* *
* The following data is protected by this lock: * The following data is protected by this lock:
* *
* - the private_data field of the device file
* - the s_fs_info field of the super block * - the s_fs_info field of the super block
* - unused_list, pending, processing lists in fuse_conn
* - the unique request ID counter reqctr in fuse_conn
* - the sb (super_block) field in fuse_conn * - the sb (super_block) field in fuse_conn
* - the file (device file) field in fuse_conn
*/ */
extern spinlock_t fuse_lock; extern spinlock_t fuse_lock;
...@@ -87,3 +245,68 @@ extern spinlock_t fuse_lock; ...@@ -87,3 +245,68 @@ extern spinlock_t fuse_lock;
*/ */
void fuse_release_conn(struct fuse_conn *fc); void fuse_release_conn(struct fuse_conn *fc);
/**
* Initialize the client device
*/
int fuse_dev_init(void);
/**
* Cleanup the client device
*/
void fuse_dev_cleanup(void);
/**
* Allocate a request
*/
struct fuse_req *fuse_request_alloc(void);
/**
* Free a request
*/
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);
/**
* Reserve a preallocated request, only interruptible by SIGKILL
*/
struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc);
/**
* Decrement reference count of a request. If count goes to zero put
* on unused list (preallocated) or free reqest (not preallocated).
*/
void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req);
/**
* Send a request (synchronous, interruptible)
*/
void request_send(struct fuse_conn *fc, struct fuse_req *req);
/**
* Send a request (synchronous, non-interruptible except by SIGKILL)
*/
void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req);
/**
* Send a request with no reply
*/
void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
/**
* Send a request in the background
*/
void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
/**
* Send the INIT message
*/
void fuse_send_init(struct fuse_conn *fc);
...@@ -151,6 +151,8 @@ static void fuse_put_super(struct super_block *sb) ...@@ -151,6 +151,8 @@ static void fuse_put_super(struct super_block *sb)
mount_count --; mount_count --;
fc->sb = NULL; fc->sb = NULL;
fc->user_id = 0; fc->user_id = 0;
/* Flush all readers on this fs */
wake_up_all(&fc->waitq);
fuse_release_conn(fc); fuse_release_conn(fc);
*get_fuse_conn_super_p(sb) = NULL; *get_fuse_conn_super_p(sb) = NULL;
spin_unlock(&fuse_lock); spin_unlock(&fuse_lock);
...@@ -229,22 +231,51 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) ...@@ -229,22 +231,51 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
return 0; return 0;
} }
void fuse_release_conn(struct fuse_conn *fc) static void free_conn(struct fuse_conn *fc)
{ {
while (!list_empty(&fc->unused_list)) {
struct fuse_req *req;
req = list_entry(fc->unused_list.next, struct fuse_req, list);
list_del(&req->list);
fuse_request_free(req);
}
kfree(fc); kfree(fc);
} }
/* Must be called with the fuse lock held */
void fuse_release_conn(struct fuse_conn *fc)
{
if (!fc->sb && !fc->file)
free_conn(fc);
}
static struct fuse_conn *new_conn(void) static struct fuse_conn *new_conn(void)
{ {
struct fuse_conn *fc; struct fuse_conn *fc;
fc = kmalloc(sizeof(*fc), GFP_KERNEL); fc = kmalloc(sizeof(*fc), GFP_KERNEL);
if (fc != NULL) { if (fc != NULL) {
int i;
memset(fc, 0, sizeof(*fc)); memset(fc, 0, sizeof(*fc));
fc->sb = NULL; fc->sb = NULL;
fc->file = NULL;
fc->user_id = 0; fc->user_id = 0;
init_waitqueue_head(&fc->waitq);
INIT_LIST_HEAD(&fc->pending);
INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->unused_list);
sema_init(&fc->outstanding_sem, 0);
for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
struct fuse_req *req = fuse_request_alloc();
if (!req) {
free_conn(fc);
return NULL;
}
list_add(&req->list, &fc->unused_list);
}
fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
fc->bdi.unplug_io_fn = default_unplug_io_fn; fc->bdi.unplug_io_fn = default_unplug_io_fn;
fc->reqctr = 0;
} }
return fc; return fc;
} }
...@@ -253,11 +284,20 @@ static struct fuse_conn *get_conn(struct file *file, struct super_block *sb) ...@@ -253,11 +284,20 @@ static struct fuse_conn *get_conn(struct file *file, struct super_block *sb)
{ {
struct fuse_conn *fc; struct fuse_conn *fc;
if (file->f_op != &fuse_dev_operations)
return ERR_PTR(-EINVAL);
fc = new_conn(); fc = new_conn();
if (fc == NULL) if (fc == NULL)
return NULL; return ERR_PTR(-ENOMEM);
spin_lock(&fuse_lock); spin_lock(&fuse_lock);
if (file->private_data) {
free_conn(fc);
fc = ERR_PTR(-EINVAL);
} else {
file->private_data = fc;
fc->sb = sb; fc->sb = sb;
fc->file = file;
}
spin_unlock(&fuse_lock); spin_unlock(&fuse_lock);
return fc; return fc;
} }
...@@ -315,8 +355,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) ...@@ -315,8 +355,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
fc = get_conn(file, sb); fc = get_conn(file, sb);
fput(file); fput(file);
if (fc == NULL) if (IS_ERR(fc))
return -EINVAL; return PTR_ERR(fc);
fc->user_id = d.user_id; fc->user_id = d.user_id;
...@@ -336,6 +376,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) ...@@ -336,6 +376,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
iput(root); iput(root);
goto err; goto err;
} }
fuse_send_init(fc);
return 0; return 0;
err: err:
...@@ -411,8 +452,14 @@ static int __init fuse_init(void) ...@@ -411,8 +452,14 @@ static int __init fuse_init(void)
if (res) if (res)
goto err; goto err;
res = fuse_dev_init();
if (res)
goto err_fs_cleanup;
return 0; return 0;
err_fs_cleanup:
fuse_fs_cleanup();
err: err:
return res; return res;
} }
...@@ -422,6 +469,7 @@ static void __exit fuse_exit(void) ...@@ -422,6 +469,7 @@ static void __exit fuse_exit(void)
printk(KERN_DEBUG "fuse exit\n"); printk(KERN_DEBUG "fuse exit\n");
fuse_fs_cleanup(); fuse_fs_cleanup();
fuse_dev_cleanup();
} }
module_init(fuse_init); module_init(fuse_init);
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include <asm/types.h> #include <asm/types.h>
/** Version number of this interface */ /** Version number of this interface */
#define FUSE_KERNEL_VERSION 5 #define FUSE_KERNEL_VERSION 6
/** Minor version number of this interface */ /** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 1 #define FUSE_KERNEL_MINOR_VERSION 1
...@@ -19,6 +19,12 @@ ...@@ -19,6 +19,12 @@
/** The node ID of the root inode */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
/** The major number of the fuse character device */
#define FUSE_MAJOR 10
/** The minor number of the fuse character device */
#define FUSE_MINOR 229
struct fuse_attr { struct fuse_attr {
__u64 ino; __u64 ino;
__u64 size; __u64 size;
...@@ -36,3 +42,31 @@ struct fuse_attr { ...@@ -36,3 +42,31 @@ struct fuse_attr {
__u32 rdev; __u32 rdev;
}; };
enum fuse_opcode {
FUSE_INIT = 26
};
/* Conservative buffer size for the client */
#define FUSE_MAX_IN 8192
struct fuse_init_in_out {
__u32 major;
__u32 minor;
};
struct fuse_in_header {
__u32 len;
__u32 opcode;
__u64 unique;
__u64 nodeid;
__u32 uid;
__u32 gid;
__u32 pid;
};
struct fuse_out_header {
__u32 len;
__s32 error;
__u64 unique;
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment