2008-11-20 14:24:05

by Tejun Heo

[permalink] [raw]
Subject: [PATCHSET] CUSE: implement CUSE, take #2


This is the second take of implement-CUSE patchset. Changes from the
last take[L] are...

* hotplug_info stuff (uevent info overriding) dropped

* a lot more comments

* now uses CUSE_MINOR which is 230 and also added module alias for it

This patchset is consisted of the following five patches.

0001-FUSE-add-fuse_-prefix-to-several-functions.patch
0002-FUSE-export-symbols-to-be-used-by-CUSE.patch
0003-FUSE-separate-out-fuse_conn_init-from-new_conn.patch
0004-FUSE-add-fuse_conn-release.patch
0005-CUSE-implement-CUSE-Character-device-in-Userspace.patch

0001-0004 prepares FUSE for CUSE addition and 0005 implements CUSE.
Corresponding libfuse changes will be posted separately.

This patchset is on top of...

master (ee2f6cc7f9ea2542ad46070ed62ba7aa04d08871)
+ [1] poll-allow-f_op_poll-to-sleep-take-2
+ [2] add-cdev_release-and-convert-cdev_alloc-to-use-it
+ [3] extend-FUSE patchset, take #2

and is also available in the following git tree.

http://git.kernel.org/?p=linux/kernel/git/tj/misc.git;a=shortlog;h=cuse
git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc.git cuse

and contains the following changes.

fs/Kconfig | 10
fs/fuse/Makefile | 1
fs/fuse/cuse.c | 720 +++++++++++++++++++++++++++++++++++++++++++++
fs/fuse/dev.c | 32 +-
fs/fuse/dir.c | 34 +-
fs/fuse/file.c | 61 ++-
fs/fuse/fuse_i.h | 45 ++
fs/fuse/inode.c | 145 +++++----
include/linux/cuse.h | 47 ++
include/linux/fuse.h | 2
include/linux/magic.h | 5
include/linux/miscdevice.h | 1
12 files changed, 979 insertions(+), 124 deletions(-)

Thanks.

--
tejun

[L] http://thread.gmane.org/gmane.comp.file-systems.fuse.devel/6818
[1] http://lkml.org/lkml/2008/11/20/161
[2] http://article.gmane.org/gmane.linux.kernel/727133
[3] http://lkml.org/lkml/2008/11/20/171


2008-11-20 14:23:44

by Tejun Heo

[permalink] [raw]
Subject: [PATCH 3/5] FUSE: separate out fuse_conn_init() from new_conn()

Separate out fuse_conn_init() from new_conn() and while at it
initialize fuse_conn->entry during conn initialization.

This will be used by CUSE.

Signed-off-by: Tejun Heo <[email protected]>
---
fs/fuse/fuse_i.h | 5 ++
fs/fuse/inode.c | 118 +++++++++++++++++++++++++++++-------------------------
2 files changed, 69 insertions(+), 54 deletions(-)

diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 83673f7..c4164b2 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -678,6 +678,11 @@ void fuse_invalidate_entry_cache(struct dentry *entry);
struct fuse_conn *fuse_conn_get(struct fuse_conn *fc);

/**
+ * Initialize fuse_conn
+ */
+int fuse_conn_init(struct fuse_conn *fc, struct super_block *sb);
+
+/**
* Release reference to fuse_conn
*/
void fuse_conn_put(struct fuse_conn *fc);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 6a103b9..068e0f2 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -462,67 +462,77 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
return 0;
}

-static struct fuse_conn *new_conn(struct super_block *sb)
+int fuse_conn_init(struct fuse_conn *fc, struct super_block *sb)
{
- struct fuse_conn *fc;
int err;

- fc = kzalloc(sizeof(*fc), GFP_KERNEL);
- if (fc) {
- spin_lock_init(&fc->lock);
- mutex_init(&fc->inst_mutex);
- atomic_set(&fc->count, 1);
- init_waitqueue_head(&fc->waitq);
- init_waitqueue_head(&fc->blocked_waitq);
- init_waitqueue_head(&fc->reserved_req_waitq);
- INIT_LIST_HEAD(&fc->pending);
- INIT_LIST_HEAD(&fc->processing);
- INIT_LIST_HEAD(&fc->io);
- INIT_LIST_HEAD(&fc->interrupts);
- INIT_LIST_HEAD(&fc->bg_queue);
- atomic_set(&fc->num_waiting, 0);
- fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
- fc->bdi.unplug_io_fn = default_unplug_io_fn;
- /* fuse does it's own writeback accounting */
- fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB;
- fc->khctr = 0;
- fc->polled_files = RB_ROOT;
- fc->dev = sb->s_dev;
- err = bdi_init(&fc->bdi);
- if (err)
- goto error_kfree;
- if (sb->s_bdev) {
- err = bdi_register(&fc->bdi, NULL, "%u:%u-fuseblk",
- MAJOR(fc->dev), MINOR(fc->dev));
- } else {
- err = bdi_register_dev(&fc->bdi, fc->dev);
- }
- if (err)
- goto error_bdi_destroy;
- /*
- * For a single fuse filesystem use max 1% of dirty +
- * writeback threshold.
- *
- * This gives about 1M of write buffer for memory maps on a
- * machine with 1G and 10% dirty_ratio, which should be more
- * than enough.
- *
- * Privileged users can raise it by writing to
- *
- * /sys/class/bdi/<bdi>/max_ratio
- */
- bdi_set_max_ratio(&fc->bdi, 1);
- fc->reqctr = 0;
- fc->blocked = 1;
- fc->attr_version = 1;
- get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
+ memset(fc, 0, sizeof(*fc));
+ spin_lock_init(&fc->lock);
+ mutex_init(&fc->inst_mutex);
+ atomic_set(&fc->count, 1);
+ init_waitqueue_head(&fc->waitq);
+ init_waitqueue_head(&fc->blocked_waitq);
+ init_waitqueue_head(&fc->reserved_req_waitq);
+ INIT_LIST_HEAD(&fc->pending);
+ INIT_LIST_HEAD(&fc->processing);
+ INIT_LIST_HEAD(&fc->io);
+ INIT_LIST_HEAD(&fc->interrupts);
+ INIT_LIST_HEAD(&fc->bg_queue);
+ INIT_LIST_HEAD(&fc->entry);
+ atomic_set(&fc->num_waiting, 0);
+ fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
+ fc->bdi.unplug_io_fn = default_unplug_io_fn;
+ /* fuse does it's own writeback accounting */
+ fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB;
+ fc->khctr = 0;
+ fc->polled_files = RB_ROOT;
+ fc->dev = sb->s_dev;
+ err = bdi_init(&fc->bdi);
+ if (err)
+ goto error_mutex_destroy;
+ if (sb->s_bdev) {
+ err = bdi_register(&fc->bdi, NULL, "%u:%u-fuseblk",
+ MAJOR(fc->dev), MINOR(fc->dev));
+ } else {
+ err = bdi_register_dev(&fc->bdi, fc->dev);
}
- return fc;
+ if (err)
+ goto error_bdi_destroy;
+ /*
+ * For a single fuse filesystem use max 1% of dirty +
+ * writeback threshold.
+ *
+ * This gives about 1M of write buffer for memory maps on a
+ * machine with 1G and 10% dirty_ratio, which should be more
+ * than enough.
+ *
+ * Privileged users can raise it by writing to
+ *
+ * /sys/class/bdi/<bdi>/max_ratio
+ */
+ bdi_set_max_ratio(&fc->bdi, 1);
+ fc->reqctr = 0;
+ fc->blocked = 1;
+ fc->attr_version = 1;
+ get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));

-error_bdi_destroy:
+ return 0;
+
+ error_bdi_destroy:
bdi_destroy(&fc->bdi);
-error_kfree:
+ error_mutex_destroy:
mutex_destroy(&fc->inst_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(fuse_conn_init);
+
+static struct fuse_conn *new_conn(struct super_block *sb)
+{
+ struct fuse_conn *fc;
+
+ fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+ if (fc && fuse_conn_init(fc, sb) == 0)
+ return fc;
kfree(fc);
return NULL;
}
--
1.5.6

2008-11-20 14:24:29

by Tejun Heo

[permalink] [raw]
Subject: [PATCH 4/5] FUSE: add fuse_conn->release()

Add fuse_conn->release() so that fuse_conn can be embedded in other
structures. If unspecified, the original action - kfree() - is done.

Signed-off-by: Tejun Heo <[email protected]>
---
fs/fuse/fuse_i.h | 3 +++
fs/fuse/inode.c | 6 +++++-
2 files changed, 8 insertions(+), 1 deletions(-)

diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index c4164b2..23df478 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -465,6 +465,9 @@ struct fuse_conn {

/** Version counter for attribute changes */
u64 attr_version;
+
+ /** Called on final put. If implemented, should free the connection */
+ void (*release)(struct fuse_conn *);
};

static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 068e0f2..eae4ff9 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -544,7 +544,11 @@ void fuse_conn_put(struct fuse_conn *fc)
fuse_request_free(fc->destroy_req);
mutex_destroy(&fc->inst_mutex);
bdi_destroy(&fc->bdi);
- kfree(fc);
+
+ if (fc->release)
+ fc->release(fc);
+ else
+ kfree(fc);
}
}
EXPORT_SYMBOL_GPL(fuse_conn_put);
--
1.5.6

2008-11-20 14:24:44

by Tejun Heo

[permalink] [raw]
Subject: [PATCH 2/5] FUSE: export symbols to be used by CUSE

Export the following symbols for CUSE.

fuse_conn_put()
fuse_conn_get()
fuse_get_root_inode()
fuse_super_operations
fuse_send_init()
fuse_flush()
fuse_fsync()
fuse_direct_io()
fuse_file_lock()
fuse_file_flock()
fuse_file_llseek()
fuse_file_ioctl()
fuse_file_compat_ioctl()
fuse_file_poll()

Signed-off-by: Tejun Heo <[email protected]>
---
fs/fuse/dev.c | 9 ++++++++-
fs/fuse/file.c | 33 +++++++++++++++++++++------------
fs/fuse/fuse_i.h | 28 ++++++++++++++++++++++++++++
fs/fuse/inode.c | 11 ++++++++---
4 files changed, 65 insertions(+), 16 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index f11439a..b8f70a0 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -46,6 +46,7 @@ struct fuse_req *fuse_request_alloc(void)
fuse_request_init(req);
return req;
}
+EXPORT_SYMBOL_GPL(fuse_request_alloc);

struct fuse_req *fuse_request_alloc_nofs(void)
{
@@ -124,6 +125,7 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc)
atomic_dec(&fc->num_waiting);
return ERR_PTR(err);
}
+EXPORT_SYMBOL_GPL(fuse_get_req);

/*
* Return request in fuse_file->reserved_req. However that may
@@ -208,6 +210,7 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
fuse_request_free(req);
}
}
+EXPORT_SYMBOL_GPL(fuse_put_request);

static unsigned len_args(unsigned numargs, struct fuse_arg *args)
{
@@ -398,6 +401,7 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
}
spin_unlock(&fc->lock);
}
+EXPORT_SYMBOL_GPL(fuse_request_send);

static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
struct fuse_req *req)
@@ -1092,8 +1096,9 @@ void fuse_abort_conn(struct fuse_conn *fc)
}
spin_unlock(&fc->lock);
}
+EXPORT_SYMBOL_GPL(fuse_abort_conn);

-static int fuse_dev_release(struct inode *inode, struct file *file)
+int fuse_dev_release(struct inode *inode, struct file *file)
{
struct fuse_conn *fc = fuse_get_conn(file);
if (fc) {
@@ -1107,6 +1112,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file)

return 0;
}
+EXPORT_SYMBOL_GPL(fuse_dev_release);

static int fuse_dev_fasync(int fd, struct file *file, int on)
{
@@ -1129,6 +1135,7 @@ const struct file_operations fuse_dev_operations = {
.release = fuse_dev_release,
.fasync = fuse_dev_fasync,
};
+EXPORT_SYMBOL_GPL(fuse_dev_operations);

static struct miscdevice fuse_miscdevice = {
.minor = FUSE_MINOR,
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 580873a..4d535ae 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/module.h>

static const struct file_operations fuse_direct_io_file_operations;

@@ -265,7 +266,7 @@ static int fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index)
return 0;
}

-static int fuse_flush(struct file *file, fl_owner_t id)
+int fuse_flush(struct file *file, fl_owner_t id)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -299,6 +300,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
}
return err;
}
+EXPORT_SYMBOL_GPL(fuse_flush);

/*
* Wait for all pending writepages on the inode to finish.
@@ -367,10 +369,11 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
return err;
}

-static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
+int fuse_fsync(struct file *file, struct dentry *de, int datasync)
{
return fuse_fsync_common(file, de, datasync, 0);
}
+EXPORT_SYMBOL_GPL(fuse_fsync);

void fuse_read_fill(struct fuse_req *req, struct file *file,
struct inode *inode, loff_t pos, size_t count, int opcode)
@@ -961,8 +964,8 @@ static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf,
return 0;
}

-static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos, int write)
+ssize_t fuse_direct_io(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos, int write)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1027,6 +1030,7 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,

return res;
}
+EXPORT_SYMBOL_GPL(fuse_direct_io);

static ssize_t fuse_direct_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
@@ -1376,7 +1380,7 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
return err;
}

-static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
+int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1398,8 +1402,9 @@ static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
}
return err;
}
+EXPORT_SYMBOL_GPL(fuse_file_lock);

-static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
+int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1415,6 +1420,7 @@ static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)

return err;
}
+EXPORT_SYMBOL_GPL(fuse_file_flock);

static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
{
@@ -1452,7 +1458,7 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
return err ? 0 : outarg.block;
}

-static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
+loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
{
loff_t retval;
struct inode *inode = file->f_path.dentry->d_inode;
@@ -1479,6 +1485,7 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
mutex_unlock(&inode->i_mutex);
return retval;
}
+EXPORT_SYMBOL_GPL(fuse_file_llseek);

static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
unsigned int nr_segs, size_t bytes, bool to_user)
@@ -1569,8 +1576,8 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
* limits ioctl data transfers to well-formed ioctls and is the forced
* behavior for all FUSE servers.
*/
-static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg, unsigned int flags)
+long fuse_file_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
+ unsigned int flags)
{
struct inode *inode = file->f_dentry->d_inode;
struct fuse_file *ff = file->private_data;
@@ -1609,12 +1616,12 @@ static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
iov->iov_base = (void *)arg;
iov->iov_len = _IOC_SIZE(cmd);

- if (_IOC_DIR(cmd) & _IOC_READ) {
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
in_iov = iov;
in_iovs = 1;
}

- if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ if (_IOC_DIR(cmd) & _IOC_READ) {
out_iov = iov;
out_iovs = 1;
}
@@ -1736,6 +1743,7 @@ static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,

return err ? err : outarg.result;
}
+EXPORT_SYMBOL_GPL(fuse_file_do_ioctl);

static long fuse_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
@@ -1804,7 +1812,7 @@ static void fuse_register_polled_file(struct fuse_conn *fc,
spin_unlock(&fc->lock);
}

-static unsigned fuse_file_poll(struct file *file, poll_table *wait)
+unsigned fuse_file_poll(struct file *file, poll_table *wait)
{
struct inode *inode = file->f_dentry->d_inode;
struct fuse_file *ff = file->private_data;
@@ -1852,6 +1860,7 @@ static unsigned fuse_file_poll(struct file *file, poll_table *wait)
}
return POLLERR;
}
+EXPORT_SYMBOL_GPL(fuse_file_poll);

/*
* This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 2006656..83673f7 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -488,10 +488,14 @@ static inline u64 get_node_id(struct inode *inode)
}

/** Device operations */
+extern const struct super_operations fuse_super_operations;
+
extern const struct file_operations fuse_dev_operations;

extern struct dentry_operations fuse_dentry_operations;

+struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode);
+
/**
* Get a filled in inode
*/
@@ -503,6 +507,11 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name,
struct fuse_entry_out *outarg, struct inode **inode);

/**
+ * Send INIT command
+ */
+void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req);
+
+/**
* Send FORGET command
*/
void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
@@ -539,6 +548,20 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
int isdir);

/**
+ * Exported file operations
+ */
+loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin);
+unsigned fuse_file_poll(struct file *file, poll_table *wait);
+ssize_t fuse_direct_io(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos, int write);
+int fuse_flush(struct file *file, fl_owner_t id);
+int fuse_fsync(struct file *file, struct dentry *de, int datasync);
+int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl);
+int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl);
+long fuse_file_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
+ unsigned int flags);
+
+/**
* Notify poll wakeup
*/
int fuse_notify_poll_wakeup(struct fuse_conn *fc,
@@ -581,6 +604,11 @@ void fuse_truncate(struct address_space *mapping, loff_t offset);
int fuse_dev_init(void);

/**
+ * Release the client device
+ */
+int fuse_dev_release(struct inode *inode, struct file *file);
+
+/**
* Cleanup the client device
*/
void fuse_dev_cleanup(void);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 283cce5..6a103b9 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -537,14 +537,16 @@ void fuse_conn_put(struct fuse_conn *fc)
kfree(fc);
}
}
+EXPORT_SYMBOL_GPL(fuse_conn_put);

struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)
{
atomic_inc(&fc->count);
return fc;
}
+EXPORT_SYMBOL_GPL(fuse_conn_get);

-static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
+struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
{
struct fuse_attr attr;
memset(&attr, 0, sizeof(attr));
@@ -554,6 +556,7 @@ static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
attr.nlink = 1;
return fuse_iget(sb, 1, 0, &attr, 0, 0);
}
+EXPORT_SYMBOL_GPL(fuse_get_root_inode);

struct fuse_inode_handle
{
@@ -716,7 +719,7 @@ static const struct export_operations fuse_export_operations = {
.get_parent = fuse_get_parent,
};

-static const struct super_operations fuse_super_operations = {
+const struct super_operations fuse_super_operations = {
.alloc_inode = fuse_alloc_inode,
.destroy_inode = fuse_destroy_inode,
.clear_inode = fuse_clear_inode,
@@ -727,6 +730,7 @@ static const struct super_operations fuse_super_operations = {
.statfs = fuse_statfs,
.show_options = fuse_show_options,
};
+EXPORT_SYMBOL_GPL(fuse_super_operations);

static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
{
@@ -768,7 +772,7 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
wake_up_all(&fc->blocked_waitq);
}

-static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
{
struct fuse_init_in *arg = &req->misc.init_in;

@@ -791,6 +795,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
req->end = process_init_reply;
fuse_request_send_background(fc, req);
}
+EXPORT_SYMBOL_GPL(fuse_send_init);

static int fuse_fill_super(struct super_block *sb, void *data, int silent)
{
--
1.5.6

2008-11-20 14:25:03

by Tejun Heo

[permalink] [raw]
Subject: [PATCH 1/5] FUSE: add fuse_ prefix to several functions

Add fuse_ prefix to request_send*() and get_root_inode() as some of
those functions will be exported for CUSE. With or without CUSE
export, having the function names scoped is a good idea for
debuggability.

Signed-off-by: Tejun Heo <[email protected]>
---
fs/fuse/dev.c | 23 ++++++++++++-----------
fs/fuse/dir.c | 34 +++++++++++++++++-----------------
fs/fuse/file.c | 28 ++++++++++++++--------------
fs/fuse/fuse_i.h | 9 +++++----
fs/fuse/inode.c | 12 ++++++------
5 files changed, 54 insertions(+), 52 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index b71cc89..f11439a 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -380,7 +380,7 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
}
}

-void request_send(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
{
req->isreply = 1;
spin_lock(&fc->lock);
@@ -399,8 +399,8 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req)
spin_unlock(&fc->lock);
}

-static void request_send_nowait_locked(struct fuse_conn *fc,
- struct fuse_req *req)
+static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
+ struct fuse_req *req)
{
req->background = 1;
fc->num_background++;
@@ -414,11 +414,11 @@ static void request_send_nowait_locked(struct fuse_conn *fc,
flush_bg_queue(fc);
}

-static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
+static void fuse_request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
{
spin_lock(&fc->lock);
if (fc->connected) {
- request_send_nowait_locked(fc, req);
+ fuse_request_send_nowait_locked(fc, req);
spin_unlock(&fc->lock);
} else {
req->out.h.error = -ENOTCONN;
@@ -426,16 +426,16 @@ static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
}
}

-void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
{
req->isreply = 0;
- request_send_nowait(fc, req);
+ fuse_request_send_nowait(fc, req);
}

-void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
{
req->isreply = 1;
- request_send_nowait(fc, req);
+ fuse_request_send_nowait(fc, req);
}

/*
@@ -443,10 +443,11 @@ void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
*
* fc->connected must have been checked previously
*/
-void request_send_background_locked(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_request_send_background_locked(struct fuse_conn *fc,
+ struct fuse_req *req)
{
req->isreply = 1;
- request_send_nowait_locked(fc, req);
+ fuse_request_send_nowait_locked(fc, req);
}

/*
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 7c6821e..b640b4c 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -189,7 +189,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
parent = dget_parent(entry);
fuse_lookup_init(fc, req, get_node_id(parent->d_inode),
&entry->d_name, &outarg);
- request_send(fc, req);
+ fuse_request_send(fc, req);
dput(parent);
err = req->out.h.error;
fuse_put_request(fc, req);
@@ -283,7 +283,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name,
attr_version = fuse_get_attr_version(fc);

fuse_lookup_init(fc, req, nodeid, name, outarg);
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
/* Zero nodeid is same as -ENOENT, but with valid timeout */
@@ -369,7 +369,7 @@ static void fuse_sync_release(struct fuse_conn *fc, struct fuse_file *ff,
{
fuse_release_fill(ff, nodeid, flags, FUSE_RELEASE);
ff->reserved_req->force = 1;
- request_send(fc, ff->reserved_req);
+ fuse_request_send(fc, ff->reserved_req);
fuse_put_request(fc, ff->reserved_req);
kfree(ff);
}
@@ -432,7 +432,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
req->out.args[0].value = &outentry;
req->out.args[1].size = sizeof(outopen);
req->out.args[1].value = &outopen;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
if (err) {
if (err == -ENOSYS)
@@ -502,7 +502,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
else
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (err)
@@ -631,7 +631,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
req->in.numargs = 1;
req->in.args[0].size = entry->d_name.len + 1;
req->in.args[0].value = entry->d_name.name;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err) {
@@ -662,7 +662,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
req->in.numargs = 1;
req->in.args[0].size = entry->d_name.len + 1;
req->in.args[0].value = entry->d_name.name;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err) {
@@ -695,7 +695,7 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
req->in.args[1].value = oldent->d_name.name;
req->in.args[2].size = newent->d_name.len + 1;
req->in.args[2].value = newent->d_name.name;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err) {
@@ -811,7 +811,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
else
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err) {
@@ -904,7 +904,7 @@ static int fuse_access(struct inode *inode, int mask)
req->in.numargs = 1;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (err == -ENOSYS) {
@@ -1026,7 +1026,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
req->num_pages = 1;
req->pages[0] = page;
fuse_read_fill(req, file, inode, file->f_pos, PAGE_SIZE, FUSE_READDIR);
- request_send(fc, req);
+ fuse_request_send(fc, req);
nbytes = req->out.args[0].size;
err = req->out.h.error;
fuse_put_request(fc, req);
@@ -1060,7 +1060,7 @@ static char *read_link(struct dentry *dentry)
req->out.numargs = 1;
req->out.args[0].size = PAGE_SIZE - 1;
req->out.args[0].value = link;
- request_send(fc, req);
+ fuse_request_send(fc, req);
if (req->out.h.error) {
free_page((unsigned long) link);
link = ERR_PTR(req->out.h.error);
@@ -1266,7 +1266,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
else
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (err) {
@@ -1360,7 +1360,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
req->in.args[1].value = name;
req->in.args[2].size = size;
req->in.args[2].value = value;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (err == -ENOSYS) {
@@ -1406,7 +1406,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
}
- request_send(fc, req);
+ fuse_request_send(fc, req);
ret = req->out.h.error;
if (!ret)
ret = size ? req->out.args[0].size : outarg.size;
@@ -1456,7 +1456,7 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
}
- request_send(fc, req);
+ fuse_request_send(fc, req);
ret = req->out.h.error;
if (!ret)
ret = size ? req->out.args[0].size : outarg.size;
@@ -1489,7 +1489,7 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
req->in.numargs = 1;
req->in.args[0].size = strlen(name) + 1;
req->in.args[0].value = name;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (err == -ENOSYS) {
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 3ea9304..580873a 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -39,7 +39,7 @@ static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
req->out.numargs = 1;
req->out.args[0].size = sizeof(*outargp);
req->out.args[0].value = outargp;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);

@@ -94,7 +94,7 @@ static void fuse_file_put(struct fuse_file *ff)
struct inode *inode = req->misc.release.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
req->end = fuse_release_end;
- request_send_background(fc, req);
+ fuse_request_send_background(fc, req);
kfree(ff);
}
}
@@ -290,7 +290,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
req->force = 1;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (err == -ENOSYS) {
@@ -354,7 +354,7 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
req->in.numargs = 1;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (err == -ENOSYS) {
@@ -406,7 +406,7 @@ static size_t fuse_send_read(struct fuse_req *req, struct file *file,
inarg->read_flags |= FUSE_READ_LOCKOWNER;
inarg->lock_owner = fuse_lock_owner_id(fc, owner);
}
- request_send(fc, req);
+ fuse_request_send(fc, req);
return req->out.args[0].size;
}

@@ -519,9 +519,9 @@ static void fuse_send_readpages(struct fuse_req *req, struct file *file,
struct fuse_file *ff = file->private_data;
req->ff = fuse_file_get(ff);
req->end = fuse_readpages_end;
- request_send_background(fc, req);
+ fuse_request_send_background(fc, req);
} else {
- request_send(fc, req);
+ fuse_request_send(fc, req);
fuse_readpages_end(fc, req);
}
}
@@ -646,7 +646,7 @@ static size_t fuse_send_write(struct fuse_req *req, struct file *file,
inarg->write_flags |= FUSE_WRITE_LOCKOWNER;
inarg->lock_owner = fuse_lock_owner_id(fc, owner);
}
- request_send(fc, req);
+ fuse_request_send(fc, req);
return req->misc.write.out.size;
}

@@ -1089,7 +1089,7 @@ static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req)

req->in.args[1].size = inarg->size;
fi->writectr++;
- request_send_background_locked(fc, req);
+ fuse_request_send_background_locked(fc, req);
return;

out_free:
@@ -1335,7 +1335,7 @@ static int fuse_getlk(struct file *file, struct file_lock *fl)
req->out.numargs = 1;
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err)
@@ -1367,7 +1367,7 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
return PTR_ERR(req);

fuse_lk_fill(req, file, fl, opcode, pid, flock);
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
/* locking is restartable */
if (err == -EINTR)
@@ -1443,7 +1443,7 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
req->out.numargs = 1;
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (err == -ENOSYS)
@@ -1675,7 +1675,7 @@ static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
req->out.argpages = 1;
req->out.argvar = 1;

- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
transferred = req->out.args[1].size;
fuse_put_request(fc, req);
@@ -1840,7 +1840,7 @@ static unsigned fuse_file_poll(struct file *file, poll_table *wait)
req->out.numargs = 1;
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);

diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 66f2400..2006656 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -619,19 +619,20 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req);
/**
* Send a request (synchronous)
*/
-void request_send(struct fuse_conn *fc, struct fuse_req *req);
+void fuse_request_send(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);
+void fuse_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);
+void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req);

-void request_send_background_locked(struct fuse_conn *fc, struct fuse_req *req);
+void fuse_request_send_background_locked(struct fuse_conn *fc,
+ struct fuse_req *req);

/* Abort all requests */
void fuse_abort_conn(struct fuse_conn *fc);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 77fcedb..283cce5 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -94,7 +94,7 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
req->in.numargs = 1;
req->in.args[0].size = sizeof(struct fuse_forget_in);
req->in.args[0].value = inarg;
- request_send_noreply(fc, req);
+ fuse_request_send_noreply(fc, req);
}

static void fuse_clear_inode(struct inode *inode)
@@ -269,7 +269,7 @@ static void fuse_send_destroy(struct fuse_conn *fc)
fc->destroy_req = NULL;
req->in.h.opcode = FUSE_DESTROY;
req->force = 1;
- request_send(fc, req);
+ fuse_request_send(fc, req);
fuse_put_request(fc, req);
}
}
@@ -334,7 +334,7 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
req->out.args[0].size =
fc->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(outarg);
req->out.args[0].value = &outarg;
- request_send(fc, req);
+ fuse_request_send(fc, req);
err = req->out.h.error;
if (!err)
convert_fuse_statfs(buf, &outarg.st);
@@ -544,7 +544,7 @@ struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)
return fc;
}

-static struct inode *get_root_inode(struct super_block *sb, unsigned mode)
+static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
{
struct fuse_attr attr;
memset(&attr, 0, sizeof(attr));
@@ -789,7 +789,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
req->out.args[0].size = sizeof(struct fuse_init_out);
req->out.args[0].value = &req->misc.init_out;
req->end = process_init_reply;
- request_send_background(fc, req);
+ fuse_request_send_background(fc, req);
}

static int fuse_fill_super(struct super_block *sb, void *data, int silent)
@@ -843,7 +843,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
sb->s_fs_info = fc;

err = -ENOMEM;
- root = get_root_inode(sb, d.rootmode);
+ root = fuse_get_root_inode(sb, d.rootmode);
if (!root)
goto err;

--
1.5.6

2008-11-20 14:25:33

by Tejun Heo

[permalink] [raw]
Subject: [PATCH 5/5] CUSE: implement CUSE - Character device in Userspace

CUSE enables implementing character devices in userspace. With recent
additions of nonblock, lseek, ioctl and poll support, FUSE already has
most of what's necessary to implement character devices. All CUSE has
to do is bonding all those components - FUSE, chardev and the driver
model - nicely.

Due to the number of different objects involved and many ways an
instance can fail, object lifetime rules are a tad bit complex.
Please take a look at the comment on top of fs/fuse/cuse.c for
details.

Other than that, it's mostly straight forward. Client opens
/dev/cuse, kernel starts conversation with CUSE_INIT. The client
tells CUSE which device it wants to create. CUSE creates the device
for the client and the rest works the same way as in a direct IO FUSE
session.

Each CUSE device has a corresponding directory /sys/class/cuse/DEVNAME
(which is symlink to /sys/devices/virtual/class/DEVNAME if
SYSFS_DEPRECATED is turned off) which hosts "waiting" and "abort"
among other things. Those two files have the same meaning as the FUSE
control files.

The only notable lacking feature compared to in-kernel implementation
is mmap support.

Signed-off-by: Tejun Heo <[email protected]>
---
fs/Kconfig | 10 +
fs/fuse/Makefile | 1 +
fs/fuse/cuse.c | 720 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/cuse.h | 47 +++
include/linux/fuse.h | 2 +
include/linux/magic.h | 5 +-
include/linux/miscdevice.h | 1 +
7 files changed, 784 insertions(+), 2 deletions(-)
create mode 100644 fs/fuse/cuse.c
create mode 100644 include/linux/cuse.h

diff --git a/fs/Kconfig b/fs/Kconfig
index 522469a..3d0c8c9 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -419,6 +419,16 @@ config FUSE_FS
If you want to develop a userspace FS, or if you want to use
a filesystem based on FUSE, answer Y or M.

+config CUSE
+ tristate "Character device in Userpace support"
+ depends on FUSE_FS
+ help
+ This FUSE extension allows character devices to be
+ implemented in userspace.
+
+ If you want to develop or use userspace character device
+ based on CUSE, answer Y or M.
+
config GENERIC_ACL
bool
select FS_POSIX_ACL
diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile
index 7243706..e95eeb4 100644
--- a/fs/fuse/Makefile
+++ b/fs/fuse/Makefile
@@ -3,5 +3,6 @@
#

obj-$(CONFIG_FUSE_FS) += fuse.o
+obj-$(CONFIG_CUSE) += cuse.o

fuse-objs := dev.o dir.o file.o inode.o control.o
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
new file mode 100644
index 0000000..048e67d
--- /dev/null
+++ b/fs/fuse/cuse.c
@@ -0,0 +1,720 @@
+/*
+ * CUSE: Character device in Userspace
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ * Copyright (C) 2008 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ *
+ * CUSE enables character devices to be implemented from userland much
+ * like FUSE allows filesystems. On initialization /dev/cuse is
+ * created. By opening the file and replying to the CUSE_INIT request
+ * userland CUSE server can create a character device. After that the
+ * operation is very similar to FUSE.
+ *
+ * CUSE bridges several objects to implement a character device using
+ * userland backend. The lifetime rules of the involved objects are a
+ * bit complex.
+ *
+ * cuse_conn : contains fuse_conn and serves as bonding structure
+ * channel : file handle connected to the userland CUSE server
+ * cdev : the implemented character device
+ * mnt : vfsmount which serves dentry and inode for cdev
+ * dev : generic device for cdev
+ *
+ * Note that 'channel' is what 'dev' is in FUSE. As CUSE deals with
+ * devices, it's called 'channel' to reduce confusion.
+ *
+ * channel determines when the character device dies. When channel is
+ * closed, everything should begin to destruct. As cuse_conn and mnt
+ * dereference each other unlike FUSE where mnt can outlive fuse_conn,
+ * both should be destructed at the same time. This is achieved by
+ * giving the base reference of cuse_conn to mnt and never referencing
+ * cuse_conn directly, so both channel and cdev have reference to mnt
+ * which in turn has single reference to cuse_conn.
+ *
+ * On CUSE server disconnect, cuse_channel_release() unregisters dev,
+ * deletes cdev and puts mnt. When the cdev is released, it puts mnt
+ * which in turn puts the cuse_conn on release.
+ *
+ * cuse_conn_get/put() takes cuse_conn and manipulates the reference
+ * count of mnt for convenience.
+ */
+
+#include <linux/cuse.h>
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/kthread.h>
+#include <linux/magic.h>
+#include <linux/miscdevice.h>
+#include <linux/stat.h>
+
+#include "fuse_i.h"
+
+struct cuse_conn {
+ struct fuse_conn fc;
+ struct cdev cdev;
+ struct vfsmount *mnt;
+ struct device *dev;
+
+ /* init parameters */
+ bool unrestricted_ioctl:1;
+
+ /* the following two bools are protected by cuse_disconnect_lock */
+ bool cdev_added;
+ bool disconnected; /* channel disconnected */
+};
+
+static struct class *cuse_class;
+static DEFINE_SPINLOCK(cuse_disconnect_lock);
+
+static inline struct cuse_conn *fc_to_cc(struct fuse_conn *fc)
+{
+ return container_of(fc, struct cuse_conn, fc);
+}
+
+static inline struct cuse_conn *cdev_to_cc(struct cdev *cdev)
+{
+ return container_of(cdev, struct cuse_conn, cdev);
+}
+
+/*
+ * Connection reference count helpers. Note that instead of directly
+ * referencing cc->fc, its mnt, which holds single reference to
+ * cc->fc, is referenced.
+ */
+static inline struct cuse_conn *cuse_conn_get(struct cuse_conn *cc)
+{
+ mntget(cc->mnt);
+ return cc;
+}
+
+static inline void cuse_conn_put(struct cuse_conn *cc)
+{
+ mntput(cc->mnt);
+}
+
+
+/**************************************************************************
+ * CUSE frontend operations
+ *
+ * These are file operations for the character device.
+ *
+ * On open, CUSE opens a file from the FUSE mnt and stores it to
+ * private_data of the open file. All other ops call FUSE ops on the
+ * FUSE file.
+ */
+
+static ssize_t cuse_direct_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return fuse_direct_io(file->private_data, buf, count, ppos, 0);
+}
+
+static ssize_t cuse_direct_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /*
+ * No locking or generic_write_checks(), the server is
+ * responsible for locking and sanity checks.
+ */
+ return fuse_direct_io(file->private_data, buf, count, ppos, 1);
+}
+
+static int cuse_open(struct inode *inode, struct file *file)
+{
+ struct cuse_conn *cc = cdev_to_cc(inode->i_cdev);
+ struct file *cfile;
+
+ cfile = dentry_open(dget(cc->mnt->mnt_root), mntget(cc->mnt),
+ file->f_flags);
+ if (IS_ERR(cfile))
+ return PTR_ERR(cfile);
+
+ file->private_data = cfile;
+ return 0;
+}
+
+static int cuse_flush(struct file *file, fl_owner_t id)
+{
+ return fuse_flush(file->private_data, id);
+}
+
+static int cuse_release(struct inode *inode, struct file *file)
+{
+ return filp_close(file->private_data, NULL);
+}
+
+static int cuse_fsync(struct file *file, struct dentry *de, int datasync)
+{
+ return fuse_fsync(file->private_data, de, datasync);
+}
+
+static unsigned cuse_file_poll(struct file *file, poll_table *wait)
+{
+ return fuse_file_poll(file->private_data, wait);
+}
+
+static long cuse_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cuse_conn *cc = cdev_to_cc(file->f_dentry->d_inode->i_cdev);
+ unsigned int flags = 0;
+
+ if (cc->unrestricted_ioctl)
+ flags |= FUSE_IOCTL_UNRESTRICTED;
+
+ return fuse_file_do_ioctl(file->private_data, cmd, arg, flags);
+}
+
+static long cuse_file_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cuse_conn *cc = cdev_to_cc(file->f_dentry->d_inode->i_cdev);
+ unsigned int flags = FUSE_IOCTL_COMPAT;
+
+ if (cc->unrestricted_ioctl)
+ flags |= FUSE_IOCTL_UNRESTRICTED;
+
+ return fuse_file_do_ioctl(file->private_data, cmd, arg, flags);
+}
+
+static const struct file_operations cuse_frontend_fops = {
+ .read = cuse_direct_read,
+ .write = cuse_direct_write,
+ .open = cuse_open,
+ .flush = cuse_flush,
+ .release = cuse_release,
+ .fsync = cuse_fsync,
+ .poll = cuse_file_poll,
+ .unlocked_ioctl = cuse_file_ioctl,
+ .compat_ioctl = cuse_file_compat_ioctl,
+};
+
+
+/**************************************************************************
+ * CUSE filesystem operations
+ *
+ * CUSE filesystem type implementation. Each CUSE device instance is
+ * served by a separate superblock of cuse_fs type.
+ */
+
+static void cuse_fc_release(struct fuse_conn *fc)
+{
+ struct cuse_conn *cc = fc_to_cc(fc);
+
+ kfree(cc);
+}
+
+static int cuse_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct cuse_conn *cc = NULL;
+ struct dentry *root_dentry = NULL;
+ struct inode *root = NULL;
+ int rc;
+
+ sb->s_magic = CUSE_SUPER_MAGIC;
+ sb->s_op = &fuse_super_operations;
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+ cc = kzalloc(sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ goto err_nomem;
+ rc = fuse_conn_init(&cc->fc, sb);
+ if (rc)
+ goto err;
+
+ /* cuse isn't accessible to mortal users, give it some latitude */
+ cc->fc.flags = FUSE_ALLOW_OTHER;
+ cc->fc.user_id = current->euid;
+ cc->fc.group_id = current->egid;
+ cc->fc.max_read = FUSE_MAX_PAGES_PER_REQ * PAGE_SIZE;
+ cc->fc.release = cuse_fc_release;
+
+ /* transfer the initial cc refcnt to sb */
+ sb->s_fs_info = &cc->fc;
+ cc = NULL;
+
+ root = fuse_get_root_inode(sb, S_IFREG);
+ if (!root)
+ goto err_nomem;
+
+ root_dentry = d_alloc_root(root);
+ if (!root_dentry)
+ goto err_nomem;
+
+ sb->s_root = root_dentry;
+
+ return 0;
+
+err_nomem:
+ rc = -ENOMEM;
+err:
+ if (root_dentry)
+ dput(root_dentry);
+ else if (root)
+ iput(root);
+ kfree(cc);
+ return rc;
+}
+
+static int cuse_get_sb(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data, struct vfsmount *mnt)
+{
+ return get_sb_nodev(fs_type, flags, data, cuse_fill_super, mnt);
+}
+
+static struct file_system_type cuse_fs = {
+ .name = "cuse",
+ .get_sb = cuse_get_sb,
+ .kill_sb = kill_anon_super,
+};
+
+
+/**************************************************************************
+ * CUSE channel initialization and destruction
+ */
+
+struct cuse_devinfo {
+ const char *name;
+};
+
+/**
+ * cuse_parse_one - parse one key=value pair
+ * @pp: i/o parameter for the current position
+ * @end: points to one past the end of the packed string
+ * @keyp: out parameter for key
+ * @valp: out parameter for value
+ *
+ * *@pp points to packed strings - "key0=val0\0key1=val1\0" which ends
+ * at @end - 1. This function parses one pair and set *@keyp to the
+ * start of the key and *@valp to the start of the value. Note that
+ * the original string is modified such that the key string is
+ * terminated with '\0'. *@pp is updated to point to the next string.
+ *
+ * RETURNS:
+ * 1 on successful parse, 0 on EOF, -errno on failure.
+ */
+static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp)
+{
+ char *p = *pp;
+ char *key, *val;
+
+ while (p < end && *p == '\0')
+ p++;
+ if (p == end)
+ return 0;
+
+ if (end[-1] != '\0') {
+ printk(KERN_ERR "CUSE: info not properly terminated\n");
+ return -EINVAL;
+ }
+
+ key = val = p;
+ p += strlen(p);
+
+ if (valp) {
+ strsep(&val, "=");
+ if (!val)
+ val = key + strlen(key);
+ key = strstrip(key);
+ val = strstrip(val);
+ } else
+ key = strstrip(key);
+
+ if (!strlen(key)) {
+ printk(KERN_ERR "CUSE: zero length info key specified\n");
+ return -EINVAL;
+ }
+
+ *pp = p;
+ *keyp = key;
+ if (valp)
+ *valp = val;
+
+ return 1;
+}
+
+/**
+ * cuse_parse_dev_info - parse device info
+ * @p: device info string
+ * @len: length of device info string
+ * @devinfo: out parameter for parsed device info
+ *
+ * Parse @p to extract device info and store it into @devinfo. String
+ * pointed to by @p is modified by parsing and @devinfo points into
+ * them, so @p shouldn't be freed while @devinfo is in use.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo)
+{
+ char *end = p + len;
+ char *key, *val;
+ int rc;
+
+ while (true) {
+ rc = cuse_parse_one(&p, end, &key, &val);
+ if (rc < 0)
+ return rc;
+ if (!rc)
+ break;
+ if (strcmp(key, "DEVNAME") == 0)
+ devinfo->name = val;
+ else
+ printk(KERN_WARNING "CUSE: unknown device info \"%s\"\n",
+ key);
+ }
+
+ if (!devinfo->name || !strlen(devinfo->name)) {
+ printk(KERN_ERR "CUSE: DEVNAME unspecified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void cuse_gendev_release(struct device *dev)
+{
+ kfree(dev);
+}
+
+static void cuse_cdev_release(struct cdev *cdev)
+{
+ cuse_conn_put(cdev_to_cc(cdev));
+}
+
+/**
+ * cuse_init_worker - worker to finish initializing CUSE channel
+ * @data: cuse_conn to initialize
+ *
+ * CUSE channel is created by opening /dev/cuse but the open itself
+ * can't do much of initialization as it must return to the userland
+ * so that CUSE server and kernel can talk via the open file, so
+ * cuse_channel_open() performs minimal initialization and schedules
+ * this function on a kthread to finish up initialization.
+ *
+ * This function queries userland CUSE server which character device
+ * it wants to create and creates the character device and sets up all
+ * the required data structures for it. As there are quite a few data
+ * structures involved, object lifetime rule is a bit complex. Please
+ * read the comment at the top of this file for high level overview.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_init_worker(void *data)
+{
+ struct cuse_conn *cc = data;
+ struct cuse_init_in iin = { };
+ struct cuse_init_out iout = { };
+ struct cuse_devinfo devinfo = { };
+ struct fuse_req *req;
+ struct page *page = NULL;
+ struct device *dev;
+ bool disconnected;
+ dev_t devt;
+ int rc;
+
+ BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE);
+
+ /* identify ourself and query what the CUSE server wants */
+ req = fuse_get_req(&cc->fc);
+ if (IS_ERR(req)) {
+ rc = PTR_ERR(req);
+ goto out;
+ }
+
+ rc = -ENOMEM;
+ page = alloc_pages(GFP_KERNEL | __GFP_ZERO, 1);
+ if (!page)
+ goto out;
+
+ req->pages[0] = nth_page(page, 0);
+ req->pages[1] = nth_page(page, 1);
+ req->num_pages = 2;
+
+ req->in.h.opcode = CUSE_INIT;
+ req->in.h.nodeid = get_node_id(cc->mnt->mnt_sb->s_root->d_inode);
+ req->in.numargs = 1;
+ req->in.args[0].size = sizeof(iin);
+ req->in.args[0].value = &iin;
+
+ iin.ver_major = CUSE_KERNEL_VERSION;
+ iin.ver_minor = CUSE_KERNEL_MINOR_VERSION;
+
+ req->out.numargs = 2;
+ req->out.args[0].size = sizeof(iout);
+ req->out.args[0].value = &iout;
+ req->out.args[1].size = CUSE_INIT_INFO_MAX;
+ req->out.argpages = 1;
+ req->out.argvar = 1;
+
+ fuse_request_send(&cc->fc, req);
+ rc = req->out.h.error;
+ if (rc)
+ goto out;
+
+ /* parse init reply */
+ cc->unrestricted_ioctl = iout.flags & CUSE_UNRESTRICTED_IOCTL;
+
+ rc = cuse_parse_devinfo(page_address(page), req->out.args[1].size,
+ &devinfo);
+ if (rc)
+ goto out;
+
+ /* MAJ, MIN and name available, let's create the device */
+ devt = MKDEV(iout.dev_major, iout.dev_minor);
+ if (!MAJOR(devt))
+ rc = alloc_chrdev_region(&devt, MINOR(devt), 1, devinfo.name);
+ else
+ rc = register_chrdev_region(devt, 1, devinfo.name);
+ if (rc) {
+ printk(KERN_ERR "CUSE: failed to register chrdev region\n");
+ goto out;
+ }
+
+ rc = -ENOMEM;
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ goto out_unregister_chrdev_region;
+ device_initialize(dev);
+ dev->class = cuse_class;
+ dev->devt = devt;
+ dev->release = cuse_gendev_release;
+ dev_set_drvdata(dev, cc);
+ dev_set_name(dev, "%s", devinfo.name);
+
+ rc = device_add(dev);
+ if (rc)
+ goto out_put_device;
+
+ /* register cdev */
+ cdev_init(&cc->cdev, &cuse_frontend_fops);
+ cc->cdev.owner = THIS_MODULE;
+ cc->cdev.release = cuse_cdev_release;
+
+ rc = cdev_add(&cc->cdev, devt, 1);
+ if (rc)
+ goto out_put_device;
+ cuse_conn_get(cc); /* will be released on cdev final put */
+
+ /* transfer dev and cdev ownership to channel */
+ spin_lock(&cuse_disconnect_lock);
+ disconnected = cc->disconnected;
+ if (!disconnected) {
+ cc->dev = dev;
+ cc->cdev_added = true;
+ }
+ spin_unlock(&cuse_disconnect_lock);
+
+ if (disconnected)
+ goto out_cdev_del;
+
+ rc = 0;
+ goto out;
+
+out_cdev_del:
+ cdev_del(&cc->cdev);
+out_put_device:
+ put_device(dev);
+out_unregister_chrdev_region:
+ unregister_chrdev_region(devt, 1);
+out:
+ if (!IS_ERR(req))
+ fuse_put_request(&cc->fc, req);
+ if (page)
+ __free_pages(page, 1);
+
+ if (rc)
+ fuse_abort_conn(&cc->fc);
+
+ cuse_conn_put(cc);
+ return rc;
+}
+
+/**
+ * cuse_channel_open - open method for /dev/cuse
+ * @inode: inode for /dev/cuse
+ * @file: file struct being opened
+ *
+ * Userland CUSE server can create a CUSE device by opening /dev/cuse
+ * and replying to initilaization request the kernel sends. This
+ * function is responsible for handling CUSE device initialization.
+ * Because the fd opened by this function is used during
+ * initialization, only mnt and cuse_conn are created here and the
+ * reset of initialization is delegated to a kthread.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_channel_open(struct inode *inode, struct file *file)
+{
+ struct cuse_conn *cc;
+ struct vfsmount *mnt;
+ struct fuse_req *init_req;
+ struct task_struct *worker;
+ int rc;
+
+ /* Set up cuse_conn. cuse_conn will be created when filling
+ * in superblock for the following kern_mount().
+ */
+ mnt = kern_mount(&cuse_fs);
+ if (IS_ERR(mnt))
+ return PTR_ERR(mnt);
+
+ cc = fc_to_cc(get_fuse_conn_super(mnt->mnt_sb));
+ cc->mnt = mnt;
+
+ /* let's send fuse init request */
+ rc = -ENOMEM;
+ init_req = fuse_request_alloc();
+ if (!init_req)
+ goto err_cc_put;
+
+ cc->fc.connected = 1;
+ file->private_data = fuse_conn_get(&cc->fc);
+ fuse_send_init(&cc->fc, init_req);
+
+ /* Okay, FUSE part of initialization is complete. The rest of
+ * the initialization is a bit more involved and requires
+ * conversing with userland. Start a kthread.
+ */
+ worker = kthread_run(cuse_init_worker, cuse_conn_get(cc),
+ "cuse-init-pid%d", current->pid);
+ if (IS_ERR(worker)) {
+ fput(file);
+ rc = PTR_ERR(worker);
+ goto err_cc_put;
+ }
+
+ return 0;
+
+err_cc_put:
+ cuse_conn_put(cc);
+ return rc;
+}
+
+/**
+ * cuse_channel_release - release method for /dev/cuse
+ * @inode: inode for /dev/cuse
+ * @file: file struct being closed
+ *
+ * Disconnect the channel, deregister CUSE device and initiate
+ * destruction by putting the default reference.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_channel_release(struct inode *inode, struct file *file)
+{
+ struct cuse_conn *cc = fc_to_cc(file->private_data);
+ int rc;
+
+ spin_lock(&cuse_disconnect_lock);
+ cc->disconnected = true;
+ spin_unlock(&cuse_disconnect_lock);
+
+ rc = fuse_dev_release(inode, file);
+ if (rc)
+ return rc;
+
+ if (cc->dev)
+ device_unregister(cc->dev);
+ if (cc->cdev_added) {
+ unregister_chrdev_region(cc->cdev.dev, 1);
+ cdev_del(&cc->cdev);
+ }
+ cuse_conn_put(cc);
+
+ return 0;
+}
+
+static struct file_operations cuse_channel_fops; /* initialized during init */
+
+
+/**************************************************************************
+ * Misc stuff and module initializatiion
+ *
+ * CUSE exports the same set of attributes to sysfs as fusectl.
+ */
+
+ssize_t cuse_class_waiting_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cuse_conn *cc = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", atomic_read(&cc->fc.num_waiting));
+}
+
+ssize_t cuse_class_abort_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cuse_conn *cc = dev_get_drvdata(dev);
+
+ fuse_abort_conn(&cc->fc);
+ return count;
+}
+
+static struct device_attribute cuse_class_dev_attrs[] = {
+ __ATTR(waiting, S_IFREG | 0400, cuse_class_waiting_show, NULL),
+ __ATTR(abort, S_IFREG | 0200, NULL, cuse_class_abort_store),
+ { }
+};
+
+static struct miscdevice cuse_miscdev = {
+ .minor = CUSE_MINOR,
+ .name = "cuse",
+ .fops = &cuse_channel_fops,
+};
+
+static int __init cuse_init(void)
+{
+ int rc;
+
+ /* inherit and extend fuse_dev_operations */
+ cuse_channel_fops = fuse_dev_operations;
+ cuse_channel_fops.owner = THIS_MODULE;
+ cuse_channel_fops.open = cuse_channel_open;
+ cuse_channel_fops.release = cuse_channel_release;
+
+ cuse_class = class_create(THIS_MODULE, "cuse");
+ if (IS_ERR(cuse_class))
+ return PTR_ERR(cuse_class);
+ cuse_class->dev_attrs = cuse_class_dev_attrs;
+
+ rc = misc_register(&cuse_miscdev);
+ if (rc)
+ goto destroy_class;
+ rc = register_filesystem(&cuse_fs);
+ if (rc)
+ goto misc_deregister;
+ return 0;
+
+misc_deregister:
+ misc_deregister(&cuse_miscdev);
+destroy_class:
+ class_destroy(cuse_class);
+ return rc;
+}
+
+static void __exit cuse_exit(void)
+{
+ unregister_filesystem(&cuse_fs);
+ misc_deregister(&cuse_miscdev);
+ class_destroy(cuse_class);
+}
+
+module_init(cuse_init);
+module_exit(cuse_exit);
+
+MODULE_ALIAS_MISCDEV(CUSE_MINOR);
+MODULE_AUTHOR("Tejun Heo <[email protected]>");
+MODULE_DESCRIPTION("Character device in Userspace");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/cuse.h b/include/linux/cuse.h
new file mode 100644
index 0000000..35281d6
--- /dev/null
+++ b/include/linux/cuse.h
@@ -0,0 +1,47 @@
+/*
+ * CUSE: Character device in Userspace
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ * Copyright (C) 2008 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _CUSE_H_
+#define _CUSE_H_
+
+#include <linux/major.h>
+#include <linux/miscdevice.h>
+#include <linux/fuse.h>
+
+#define CUSE_KERNEL_VERSION 0
+#define CUSE_KERNEL_MINOR_VERSION 1
+
+#define CUSE_KERNEL_MAJOR MISC_MAJOR
+#define CUSE_KERNEL_MINOR MISC_DYNAMIC_MINOR
+
+#define CUSE_INIT_INFO_MAX 4096
+
+/*
+ * CUSE INIT request/reply flags
+ */
+#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */
+
+enum cuse_opcode {
+ CUSE_INIT = CUSE_BASE,
+};
+
+struct cuse_init_in {
+ __u32 ver_major;
+ __u32 ver_minor;
+ __u32 flags;
+ __u32 padding;
+};
+
+struct cuse_init_out {
+ __u32 dev_major; /* chardev major */
+ __u32 dev_minor; /* chardev minor */
+ __u32 flags;
+ __u32 padding;
+};
+
+#endif /*_CUSE_H_*/
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 5650cf0..5842560 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -209,6 +209,8 @@ enum fuse_opcode {
FUSE_DESTROY = 38,
FUSE_IOCTL = 39,
FUSE_POLL = 40,
+
+ CUSE_BASE = 4096,
};

enum fuse_notify_code {
diff --git a/include/linux/magic.h b/include/linux/magic.h
index f7f3fdd..0071322 100644
--- a/include/linux/magic.h
+++ b/include/linux/magic.h
@@ -3,10 +3,11 @@

#define ADFS_SUPER_MAGIC 0xadf5
#define AFFS_SUPER_MAGIC 0xadff
-#define AFS_SUPER_MAGIC 0x5346414F
+#define AFS_SUPER_MAGIC 0x5346414F
#define AUTOFS_SUPER_MAGIC 0x0187
#define CODA_SUPER_MAGIC 0x73757245
-#define DEBUGFS_MAGIC 0x64626720
+#define CUSE_SUPER_MAGIC 0x43555345
+#define DEBUGFS_MAGIC 0x64626720
#define SYSFS_MAGIC 0x62656572
#define SECURITYFS_MAGIC 0x73636673
#define TMPFS_MAGIC 0x01021994
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index 0be9245..d4dcca8 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -28,6 +28,7 @@
#define MPT_MINOR 220
#define HPET_MINOR 228
#define FUSE_MINOR 229
+#define CUSE_MINOR 230
#define KVM_MINOR 232
#define MISC_DYNAMIC_MINOR 255

--
1.5.6

2008-11-21 18:55:44

by Miklos Szeredi

[permalink] [raw]
Subject: Re: [PATCH 5/5] CUSE: implement CUSE - Character device in Userspace

On Thu, 20 Nov 2008, Tejun Heo wrote:
> diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
> index 0be9245..d4dcca8 100644
> --- a/include/linux/miscdevice.h
> +++ b/include/linux/miscdevice.h
> @@ -28,6 +28,7 @@
> #define MPT_MINOR 220
> #define HPET_MINOR 228
> #define FUSE_MINOR 229
> +#define CUSE_MINOR 230

This doesn't work: device numbers have to be allocated by LANANA.

But maybe we can get away with a dynamic number, now that everyone
uses udev?

Miklos

2008-11-22 05:05:49

by Tejun Heo

[permalink] [raw]
Subject: Re: [PATCH 5/5] CUSE: implement CUSE - Character device in Userspace

Miklos Szeredi wrote:
> On Thu, 20 Nov 2008, Tejun Heo wrote:
>> diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
>> index 0be9245..d4dcca8 100644
>> --- a/include/linux/miscdevice.h
>> +++ b/include/linux/miscdevice.h
>> @@ -28,6 +28,7 @@
>> #define MPT_MINOR 220
>> #define HPET_MINOR 228
>> #define FUSE_MINOR 229
>> +#define CUSE_MINOR 230
>
> This doesn't work: device numbers have to be allocated by LANANA.

My last attempt at LANANA for extended devt didn't get me anywhere and
I'm not sure LANANA is handling miscdevice minors at all. If you go
through the tree, there are quite a few duplicate allocations including
FUSE_MINOR. At any rate, we can definitely try LANANA.

> But maybe we can get away with a dynamic number, now that everyone
> uses udev?

It would still be nice to have static /dev/cuse and trying to open it
can trigger "modprobe cuse". It's not like we're short of device number
space anyway.

Thanks.

--
tejun

2008-11-22 05:16:48

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 5/5] CUSE: implement CUSE - Character device in Userspace

On Thu, 20 Nov 2008 23:23:03 +0900 Tejun Heo <[email protected]> wrote:

> CUSE enables implementing character devices in userspace. With recent
> additions of nonblock, lseek, ioctl and poll support, FUSE already has
> most of what's necessary to implement character devices. All CUSE has
> to do is bonding all those components - FUSE, chardev and the driver
> model - nicely.
>
> Due to the number of different objects involved and many ways an
> instance can fail, object lifetime rules are a tad bit complex.
> Please take a look at the comment on top of fs/fuse/cuse.c for
> details.
>
> Other than that, it's mostly straight forward. Client opens
> /dev/cuse, kernel starts conversation with CUSE_INIT. The client
> tells CUSE which device it wants to create. CUSE creates the device
> for the client and the rest works the same way as in a direct IO FUSE
> session.
>
> Each CUSE device has a corresponding directory /sys/class/cuse/DEVNAME
> (which is symlink to /sys/devices/virtual/class/DEVNAME if
> SYSFS_DEPRECATED is turned off) which hosts "waiting" and "abort"
> among other things. Those two files have the same meaning as the FUSE
> control files.
>
> The only notable lacking feature compared to in-kernel implementation
> is mmap support.
>
> ...
>
> +struct cuse_conn {
> + struct fuse_conn fc;
> + struct cdev cdev;
> + struct vfsmount *mnt;
> + struct device *dev;
> +
> + /* init parameters */
> + bool unrestricted_ioctl:1;

I'd suggest removal of the :1 here. If someone later comes along and
adds another bitfield next to it, locking will be needed to prevent
racess accessing the bitfields, and I seeno appropriate lock here, nor
any comment explaining the locking..

> +static int cuse_init_worker(void *data)
> +{
> + struct cuse_init_in iin = { };
> + struct cuse_init_out iout = { };
> + struct cuse_devinfo devinfo = { };

You might want to check the generated code here. gcc has a habit of
assembling a temp structure on the stack then memcpying it over, which
is just junk. This will be gcc version dependent. Fixable by using an
old-fashioned memset instead.

2008-11-22 05:18:29

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 5/5] CUSE: implement CUSE - Character device in Userspace

On Sat, 22 Nov 2008 14:05:40 +0900 Tejun Heo <[email protected]> wrote:

> Miklos Szeredi wrote:
> > On Thu, 20 Nov 2008, Tejun Heo wrote:
> >> diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
> >> index 0be9245..d4dcca8 100644
> >> --- a/include/linux/miscdevice.h
> >> +++ b/include/linux/miscdevice.h
> >> @@ -28,6 +28,7 @@
> >> #define MPT_MINOR 220
> >> #define HPET_MINOR 228
> >> #define FUSE_MINOR 229
> >> +#define CUSE_MINOR 230
> >
> > This doesn't work: device numbers have to be allocated by LANANA.
>
> My last attempt at LANANA for extended devt didn't get me anywhere and
> I'm not sure LANANA is handling miscdevice minors at all.

No, I don't believe that miscdev minors are handled by lanana.

otoh, we've added only one new miscdev minor since 2005. All the new
code is using dynamic allocation. Readers of your changelog and code
comments will be wondering why you didn't do so ;)

2008-11-22 07:30:01

by Tejun Heo

[permalink] [raw]
Subject: Re: [PATCH 5/5] CUSE: implement CUSE - Character device in Userspace

Andrew Morton wrote:
> On Sat, 22 Nov 2008 14:05:40 +0900 Tejun Heo <[email protected]> wrote:
>
>> Miklos Szeredi wrote:
>>> On Thu, 20 Nov 2008, Tejun Heo wrote:
>>>> diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
>>>> index 0be9245..d4dcca8 100644
>>>> --- a/include/linux/miscdevice.h
>>>> +++ b/include/linux/miscdevice.h
>>>> @@ -28,6 +28,7 @@
>>>> #define MPT_MINOR 220
>>>> #define HPET_MINOR 228
>>>> #define FUSE_MINOR 229
>>>> +#define CUSE_MINOR 230
>>> This doesn't work: device numbers have to be allocated by LANANA.
>> My last attempt at LANANA for extended devt didn't get me anywhere and
>> I'm not sure LANANA is handling miscdevice minors at all.
>
> No, I don't believe that miscdev minors are handled by lanana.
>
> otoh, we've added only one new miscdev minor since 2005. All the new
> code is using dynamic allocation. Readers of your changelog and code
> comments will be wondering why you didn't do so ;)

Heh.. I suppose I was too attached to the module alias thing. I'll
switch back to dynamic minor.

--
tejun

2008-11-22 08:03:00

by Tejun Heo

[permalink] [raw]
Subject: Re: [PATCH 5/5] CUSE: implement CUSE - Character device in Userspace

Andrew Morton wrote:
>> The only notable lacking feature compared to in-kernel implementation
>> is mmap support.
>>
>> ...
>>
>> +struct cuse_conn {
>> + struct fuse_conn fc;
>> + struct cdev cdev;
>> + struct vfsmount *mnt;
>> + struct device *dev;
>> +
>> + /* init parameters */
>> + bool unrestricted_ioctl:1;
>
> I'd suggest removal of the :1 here. If someone later comes along and
> adds another bitfield next to it, locking will be needed to prevent
> racess accessing the bitfields, and I seeno appropriate lock here, nor
> any comment explaining the locking..

/* init parameters */ pretty much indicates that they're set once during
initialization (no locking concerns). Anyways, at this point, it
doesn't matter anyway, I'll drop the :1.

>> +static int cuse_init_worker(void *data)
>> +{
>> + struct cuse_init_in iin = { };
>> + struct cuse_init_out iout = { };
>> + struct cuse_devinfo devinfo = { };
>
> You might want to check the generated code here. gcc has a habit of
> assembling a temp structure on the stack then memcpying it over, which
> is just junk. This will be gcc version dependent. Fixable by using an
> old-fashioned memset instead.

My gcc-4.3.1 20080507 does four movq's to initialize iin and iout on
stack during function preamble and one movq for devinfo later in the
function body.

At any rate, this code path is run only once per CUSE device
initialization and those structures are small enough to be on stack. I
would go for simpler code anyday here.

Thanks.

--
tejun

2008-11-22 08:06:54

by Tejun Heo

[permalink] [raw]
Subject: [PATCH 2/5 UPDATED] FUSE: export symbols to be used by CUSE

Export the following symbols for CUSE.

fuse_conn_put()
fuse_conn_get()
fuse_get_root_inode()
fuse_super_operations
fuse_send_init()
fuse_flush()
fuse_fsync()
fuse_direct_io()
fuse_file_lock()
fuse_file_flock()
fuse_file_llseek()
fuse_file_ioctl()
fuse_file_compat_ioctl()
fuse_file_poll()

Signed-off-by: Tejun Heo <[email protected]>
---
The original posting contained a snippet fixing ioctl data direction
bug which should have been in fuse-implement-ioctl patch. The
original patch is fixed now. Drop the contamination.

Thanks.

fs/fuse/dev.c | 9 ++++++++-
fs/fuse/file.c | 29 +++++++++++++++++++----------
fs/fuse/fuse_i.h | 28 ++++++++++++++++++++++++++++
fs/fuse/inode.c | 11 ++++++++---
4 files changed, 63 insertions(+), 14 deletions(-)

Index: work/fs/fuse/fuse_i.h
===================================================================
--- work.orig/fs/fuse/fuse_i.h
+++ work/fs/fuse/fuse_i.h
@@ -488,10 +488,14 @@ static inline u64 get_node_id(struct ino
}

/** Device operations */
+extern const struct super_operations fuse_super_operations;
+
extern const struct file_operations fuse_dev_operations;

extern struct dentry_operations fuse_dentry_operations;

+struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode);
+
/**
* Get a filled in inode
*/
@@ -503,6 +507,11 @@ int fuse_lookup_name(struct super_block
struct fuse_entry_out *outarg, struct inode **inode);

/**
+ * Send INIT command
+ */
+void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req);
+
+/**
* Send FORGET command
*/
void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
@@ -539,6 +548,20 @@ int fuse_fsync_common(struct file *file,
int isdir);

/**
+ * Exported file operations
+ */
+loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin);
+unsigned fuse_file_poll(struct file *file, poll_table *wait);
+ssize_t fuse_direct_io(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos, int write);
+int fuse_flush(struct file *file, fl_owner_t id);
+int fuse_fsync(struct file *file, struct dentry *de, int datasync);
+int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl);
+int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl);
+long fuse_file_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
+ unsigned int flags);
+
+/**
* Notify poll wakeup
*/
int fuse_notify_poll_wakeup(struct fuse_conn *fc,
@@ -581,6 +604,11 @@ void fuse_truncate(struct address_space
int fuse_dev_init(void);

/**
+ * Release the client device
+ */
+int fuse_dev_release(struct inode *inode, struct file *file);
+
+/**
* Cleanup the client device
*/
void fuse_dev_cleanup(void);
Index: work/fs/fuse/file.c
===================================================================
--- work.orig/fs/fuse/file.c
+++ work/fs/fuse/file.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/module.h>

static const struct file_operations fuse_direct_io_file_operations;

@@ -265,7 +266,7 @@ static int fuse_wait_on_page_writeback(s
return 0;
}

-static int fuse_flush(struct file *file, fl_owner_t id)
+int fuse_flush(struct file *file, fl_owner_t id)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -299,6 +300,7 @@ static int fuse_flush(struct file *file,
}
return err;
}
+EXPORT_SYMBOL_GPL(fuse_flush);

/*
* Wait for all pending writepages on the inode to finish.
@@ -367,10 +369,11 @@ int fuse_fsync_common(struct file *file,
return err;
}

-static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
+int fuse_fsync(struct file *file, struct dentry *de, int datasync)
{
return fuse_fsync_common(file, de, datasync, 0);
}
+EXPORT_SYMBOL_GPL(fuse_fsync);

void fuse_read_fill(struct fuse_req *req, struct file *file,
struct inode *inode, loff_t pos, size_t count, int opcode)
@@ -961,8 +964,8 @@ static int fuse_get_user_pages(struct fu
return 0;
}

-static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos, int write)
+ssize_t fuse_direct_io(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos, int write)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1027,6 +1030,7 @@ static ssize_t fuse_direct_io(struct fil

return res;
}
+EXPORT_SYMBOL_GPL(fuse_direct_io);

static ssize_t fuse_direct_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
@@ -1376,7 +1380,7 @@ static int fuse_setlk(struct file *file,
return err;
}

-static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
+int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1398,8 +1402,9 @@ static int fuse_file_lock(struct file *f
}
return err;
}
+EXPORT_SYMBOL_GPL(fuse_file_lock);

-static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
+int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1415,6 +1420,7 @@ static int fuse_file_flock(struct file *

return err;
}
+EXPORT_SYMBOL_GPL(fuse_file_flock);

static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
{
@@ -1452,7 +1458,7 @@ static sector_t fuse_bmap(struct address
return err ? 0 : outarg.block;
}

-static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
+loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
{
loff_t retval;
struct inode *inode = file->f_path.dentry->d_inode;
@@ -1479,6 +1485,7 @@ static loff_t fuse_file_llseek(struct fi
mutex_unlock(&inode->i_mutex);
return retval;
}
+EXPORT_SYMBOL_GPL(fuse_file_llseek);

static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
unsigned int nr_segs, size_t bytes, bool to_user)
@@ -1569,8 +1576,8 @@ static int fuse_ioctl_copy_user(struct p
* limits ioctl data transfers to well-formed ioctls and is the forced
* behavior for all FUSE servers.
*/
-static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg, unsigned int flags)
+long fuse_file_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
+ unsigned int flags)
{
struct inode *inode = file->f_dentry->d_inode;
struct fuse_file *ff = file->private_data;
@@ -1736,6 +1743,7 @@ static long fuse_file_do_ioctl(struct fi

return err ? err : outarg.result;
}
+EXPORT_SYMBOL_GPL(fuse_file_do_ioctl);

static long fuse_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
@@ -1804,7 +1812,7 @@ static void fuse_register_polled_file(st
spin_unlock(&fc->lock);
}

-static unsigned fuse_file_poll(struct file *file, poll_table *wait)
+unsigned fuse_file_poll(struct file *file, poll_table *wait)
{
struct inode *inode = file->f_dentry->d_inode;
struct fuse_file *ff = file->private_data;
@@ -1852,6 +1860,7 @@ static unsigned fuse_file_poll(struct fi
}
return POLLERR;
}
+EXPORT_SYMBOL_GPL(fuse_file_poll);

/*
* This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and
Index: work/fs/fuse/inode.c
===================================================================
--- work.orig/fs/fuse/inode.c
+++ work/fs/fuse/inode.c
@@ -537,14 +537,16 @@ void fuse_conn_put(struct fuse_conn *fc)
kfree(fc);
}
}
+EXPORT_SYMBOL_GPL(fuse_conn_put);

struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)
{
atomic_inc(&fc->count);
return fc;
}
+EXPORT_SYMBOL_GPL(fuse_conn_get);

-static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
+struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
{
struct fuse_attr attr;
memset(&attr, 0, sizeof(attr));
@@ -554,6 +556,7 @@ static struct inode *fuse_get_root_inode
attr.nlink = 1;
return fuse_iget(sb, 1, 0, &attr, 0, 0);
}
+EXPORT_SYMBOL_GPL(fuse_get_root_inode);

struct fuse_inode_handle
{
@@ -716,7 +719,7 @@ static const struct export_operations fu
.get_parent = fuse_get_parent,
};

-static const struct super_operations fuse_super_operations = {
+const struct super_operations fuse_super_operations = {
.alloc_inode = fuse_alloc_inode,
.destroy_inode = fuse_destroy_inode,
.clear_inode = fuse_clear_inode,
@@ -727,6 +730,7 @@ static const struct super_operations fus
.statfs = fuse_statfs,
.show_options = fuse_show_options,
};
+EXPORT_SYMBOL_GPL(fuse_super_operations);

static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
{
@@ -768,7 +772,7 @@ static void process_init_reply(struct fu
wake_up_all(&fc->blocked_waitq);
}

-static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
{
struct fuse_init_in *arg = &req->misc.init_in;

@@ -791,6 +795,7 @@ static void fuse_send_init(struct fuse_c
req->end = process_init_reply;
fuse_request_send_background(fc, req);
}
+EXPORT_SYMBOL_GPL(fuse_send_init);

static int fuse_fill_super(struct super_block *sb, void *data, int silent)
{
Index: work/fs/fuse/dev.c
===================================================================
--- work.orig/fs/fuse/dev.c
+++ work/fs/fuse/dev.c
@@ -46,6 +46,7 @@ struct fuse_req *fuse_request_alloc(void
fuse_request_init(req);
return req;
}
+EXPORT_SYMBOL_GPL(fuse_request_alloc);

struct fuse_req *fuse_request_alloc_nofs(void)
{
@@ -124,6 +125,7 @@ struct fuse_req *fuse_get_req(struct fus
atomic_dec(&fc->num_waiting);
return ERR_PTR(err);
}
+EXPORT_SYMBOL_GPL(fuse_get_req);

/*
* Return request in fuse_file->reserved_req. However that may
@@ -208,6 +210,7 @@ void fuse_put_request(struct fuse_conn *
fuse_request_free(req);
}
}
+EXPORT_SYMBOL_GPL(fuse_put_request);

static unsigned len_args(unsigned numargs, struct fuse_arg *args)
{
@@ -398,6 +401,7 @@ void fuse_request_send(struct fuse_conn
}
spin_unlock(&fc->lock);
}
+EXPORT_SYMBOL_GPL(fuse_request_send);

static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
struct fuse_req *req)
@@ -1092,8 +1096,9 @@ void fuse_abort_conn(struct fuse_conn *f
}
spin_unlock(&fc->lock);
}
+EXPORT_SYMBOL_GPL(fuse_abort_conn);

-static int fuse_dev_release(struct inode *inode, struct file *file)
+int fuse_dev_release(struct inode *inode, struct file *file)
{
struct fuse_conn *fc = fuse_get_conn(file);
if (fc) {
@@ -1107,6 +1112,7 @@ static int fuse_dev_release(struct inode

return 0;
}
+EXPORT_SYMBOL_GPL(fuse_dev_release);

static int fuse_dev_fasync(int fd, struct file *file, int on)
{
@@ -1129,6 +1135,7 @@ const struct file_operations fuse_dev_op
.release = fuse_dev_release,
.fasync = fuse_dev_fasync,
};
+EXPORT_SYMBOL_GPL(fuse_dev_operations);

static struct miscdevice fuse_miscdevice = {
.minor = FUSE_MINOR,

2008-11-22 08:08:33

by Tejun Heo

[permalink] [raw]
Subject: [PATCH 5/5 UPDATED] CUSE: implement CUSE - Character device in Userspace

CUSE enables implementing character devices in userspace. With recent
additions of nonblock, lseek, ioctl and poll support, FUSE already has
most of what's necessary to implement character devices. All CUSE has
to do is bonding all those components - FUSE, chardev and the driver
model - nicely.

Due to the number of different objects involved and many ways an
instance can fail, object lifetime rules are a tad bit complex.
Please take a look at the comment on top of fs/fuse/cuse.c for
details.

Other than that, it's mostly straight forward. Client opens
/dev/cuse, kernel starts conversation with CUSE_INIT. The client
tells CUSE which device it wants to create. CUSE creates the device
for the client and the rest works the same way as in a direct IO FUSE
session.

Each CUSE device has a corresponding directory /sys/class/cuse/DEVNAME
(which is symlink to /sys/devices/virtual/class/DEVNAME if
SYSFS_DEPRECATED is turned off) which hosts "waiting" and "abort"
among other things. Those two files have the same meaning as the FUSE
control files.

The only notable lacking feature compared to in-kernel implementation
is mmap support.

Signed-off-by: Tejun Heo <[email protected]>
---
Bitfield dropped from struct cuse_conn as suggested by Andrew.
Switched back to MISC_DYNAMIC_MINOR as Miklos and Andrew suggested.

Thanks.

fs/Kconfig | 10
fs/fuse/Makefile | 1
fs/fuse/cuse.c | 719 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/cuse.h | 47 +++
include/linux/fuse.h | 2
include/linux/magic.h | 5
6 files changed, 782 insertions(+), 2 deletions(-)

Index: work/fs/fuse/cuse.c
===================================================================
--- /dev/null
+++ work/fs/fuse/cuse.c
@@ -0,0 +1,719 @@
+/*
+ * CUSE: Character device in Userspace
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ * Copyright (C) 2008 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPLv2.
+ *
+ * CUSE enables character devices to be implemented from userland much
+ * like FUSE allows filesystems. On initialization /dev/cuse is
+ * created. By opening the file and replying to the CUSE_INIT request
+ * userland CUSE server can create a character device. After that the
+ * operation is very similar to FUSE.
+ *
+ * CUSE bridges several objects to implement a character device using
+ * userland backend. The lifetime rules of the involved objects are a
+ * bit complex.
+ *
+ * cuse_conn : contains fuse_conn and serves as bonding structure
+ * channel : file handle connected to the userland CUSE server
+ * cdev : the implemented character device
+ * mnt : vfsmount which serves dentry and inode for cdev
+ * dev : generic device for cdev
+ *
+ * Note that 'channel' is what 'dev' is in FUSE. As CUSE deals with
+ * devices, it's called 'channel' to reduce confusion.
+ *
+ * channel determines when the character device dies. When channel is
+ * closed, everything should begin to destruct. As cuse_conn and mnt
+ * dereference each other unlike FUSE where mnt can outlive fuse_conn,
+ * both should be destructed at the same time. This is achieved by
+ * giving the base reference of cuse_conn to mnt and never referencing
+ * cuse_conn directly, so both channel and cdev have reference to mnt
+ * which in turn has single reference to cuse_conn.
+ *
+ * On CUSE server disconnect, cuse_channel_release() unregisters dev,
+ * deletes cdev and puts mnt. When the cdev is released, it puts mnt
+ * which in turn puts the cuse_conn on release.
+ *
+ * cuse_conn_get/put() takes cuse_conn and manipulates the reference
+ * count of mnt for convenience.
+ */
+
+#include <linux/cuse.h>
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/kthread.h>
+#include <linux/magic.h>
+#include <linux/miscdevice.h>
+#include <linux/stat.h>
+
+#include "fuse_i.h"
+
+struct cuse_conn {
+ struct fuse_conn fc;
+ struct cdev cdev;
+ struct vfsmount *mnt;
+ struct device *dev;
+
+ /* init parameters, set once during initialization */
+ bool unrestricted_ioctl;
+
+ /* the following two bools are protected by cuse_disconnect_lock */
+ bool cdev_added;
+ bool disconnected; /* channel disconnected */
+};
+
+static struct class *cuse_class;
+static DEFINE_SPINLOCK(cuse_disconnect_lock);
+
+static inline struct cuse_conn *fc_to_cc(struct fuse_conn *fc)
+{
+ return container_of(fc, struct cuse_conn, fc);
+}
+
+static inline struct cuse_conn *cdev_to_cc(struct cdev *cdev)
+{
+ return container_of(cdev, struct cuse_conn, cdev);
+}
+
+/*
+ * Connection reference count helpers. Note that instead of directly
+ * referencing cc->fc, its mnt, which holds single reference to
+ * cc->fc, is referenced.
+ */
+static inline struct cuse_conn *cuse_conn_get(struct cuse_conn *cc)
+{
+ mntget(cc->mnt);
+ return cc;
+}
+
+static inline void cuse_conn_put(struct cuse_conn *cc)
+{
+ mntput(cc->mnt);
+}
+
+
+/**************************************************************************
+ * CUSE frontend operations
+ *
+ * These are file operations for the character device.
+ *
+ * On open, CUSE opens a file from the FUSE mnt and stores it to
+ * private_data of the open file. All other ops call FUSE ops on the
+ * FUSE file.
+ */
+
+static ssize_t cuse_direct_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return fuse_direct_io(file->private_data, buf, count, ppos, 0);
+}
+
+static ssize_t cuse_direct_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /*
+ * No locking or generic_write_checks(), the server is
+ * responsible for locking and sanity checks.
+ */
+ return fuse_direct_io(file->private_data, buf, count, ppos, 1);
+}
+
+static int cuse_open(struct inode *inode, struct file *file)
+{
+ struct cuse_conn *cc = cdev_to_cc(inode->i_cdev);
+ struct file *cfile;
+
+ cfile = dentry_open(dget(cc->mnt->mnt_root), mntget(cc->mnt),
+ file->f_flags);
+ if (IS_ERR(cfile))
+ return PTR_ERR(cfile);
+
+ file->private_data = cfile;
+ return 0;
+}
+
+static int cuse_flush(struct file *file, fl_owner_t id)
+{
+ return fuse_flush(file->private_data, id);
+}
+
+static int cuse_release(struct inode *inode, struct file *file)
+{
+ return filp_close(file->private_data, NULL);
+}
+
+static int cuse_fsync(struct file *file, struct dentry *de, int datasync)
+{
+ return fuse_fsync(file->private_data, de, datasync);
+}
+
+static unsigned cuse_file_poll(struct file *file, poll_table *wait)
+{
+ return fuse_file_poll(file->private_data, wait);
+}
+
+static long cuse_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cuse_conn *cc = cdev_to_cc(file->f_dentry->d_inode->i_cdev);
+ unsigned int flags = 0;
+
+ if (cc->unrestricted_ioctl)
+ flags |= FUSE_IOCTL_UNRESTRICTED;
+
+ return fuse_file_do_ioctl(file->private_data, cmd, arg, flags);
+}
+
+static long cuse_file_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cuse_conn *cc = cdev_to_cc(file->f_dentry->d_inode->i_cdev);
+ unsigned int flags = FUSE_IOCTL_COMPAT;
+
+ if (cc->unrestricted_ioctl)
+ flags |= FUSE_IOCTL_UNRESTRICTED;
+
+ return fuse_file_do_ioctl(file->private_data, cmd, arg, flags);
+}
+
+static const struct file_operations cuse_frontend_fops = {
+ .read = cuse_direct_read,
+ .write = cuse_direct_write,
+ .open = cuse_open,
+ .flush = cuse_flush,
+ .release = cuse_release,
+ .fsync = cuse_fsync,
+ .poll = cuse_file_poll,
+ .unlocked_ioctl = cuse_file_ioctl,
+ .compat_ioctl = cuse_file_compat_ioctl,
+};
+
+
+/**************************************************************************
+ * CUSE filesystem operations
+ *
+ * CUSE filesystem type implementation. Each CUSE device instance is
+ * served by a separate superblock of cuse_fs type.
+ */
+
+static void cuse_fc_release(struct fuse_conn *fc)
+{
+ struct cuse_conn *cc = fc_to_cc(fc);
+
+ kfree(cc);
+}
+
+static int cuse_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct cuse_conn *cc = NULL;
+ struct dentry *root_dentry = NULL;
+ struct inode *root = NULL;
+ int rc;
+
+ sb->s_magic = CUSE_SUPER_MAGIC;
+ sb->s_op = &fuse_super_operations;
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+ cc = kzalloc(sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ goto err_nomem;
+ rc = fuse_conn_init(&cc->fc, sb);
+ if (rc)
+ goto err;
+
+ /* cuse isn't accessible to mortal users, give it some latitude */
+ cc->fc.flags = FUSE_ALLOW_OTHER;
+ cc->fc.user_id = current->euid;
+ cc->fc.group_id = current->egid;
+ cc->fc.max_read = FUSE_MAX_PAGES_PER_REQ * PAGE_SIZE;
+ cc->fc.release = cuse_fc_release;
+
+ /* transfer the initial cc refcnt to sb */
+ sb->s_fs_info = &cc->fc;
+ cc = NULL;
+
+ root = fuse_get_root_inode(sb, S_IFREG);
+ if (!root)
+ goto err_nomem;
+
+ root_dentry = d_alloc_root(root);
+ if (!root_dentry)
+ goto err_nomem;
+
+ sb->s_root = root_dentry;
+
+ return 0;
+
+err_nomem:
+ rc = -ENOMEM;
+err:
+ if (root_dentry)
+ dput(root_dentry);
+ else if (root)
+ iput(root);
+ kfree(cc);
+ return rc;
+}
+
+static int cuse_get_sb(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data, struct vfsmount *mnt)
+{
+ return get_sb_nodev(fs_type, flags, data, cuse_fill_super, mnt);
+}
+
+static struct file_system_type cuse_fs = {
+ .name = "cuse",
+ .get_sb = cuse_get_sb,
+ .kill_sb = kill_anon_super,
+};
+
+
+/**************************************************************************
+ * CUSE channel initialization and destruction
+ */
+
+struct cuse_devinfo {
+ const char *name;
+};
+
+/**
+ * cuse_parse_one - parse one key=value pair
+ * @pp: i/o parameter for the current position
+ * @end: points to one past the end of the packed string
+ * @keyp: out parameter for key
+ * @valp: out parameter for value
+ *
+ * *@pp points to packed strings - "key0=val0\0key1=val1\0" which ends
+ * at @end - 1. This function parses one pair and set *@keyp to the
+ * start of the key and *@valp to the start of the value. Note that
+ * the original string is modified such that the key string is
+ * terminated with '\0'. *@pp is updated to point to the next string.
+ *
+ * RETURNS:
+ * 1 on successful parse, 0 on EOF, -errno on failure.
+ */
+static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp)
+{
+ char *p = *pp;
+ char *key, *val;
+
+ while (p < end && *p == '\0')
+ p++;
+ if (p == end)
+ return 0;
+
+ if (end[-1] != '\0') {
+ printk(KERN_ERR "CUSE: info not properly terminated\n");
+ return -EINVAL;
+ }
+
+ key = val = p;
+ p += strlen(p);
+
+ if (valp) {
+ strsep(&val, "=");
+ if (!val)
+ val = key + strlen(key);
+ key = strstrip(key);
+ val = strstrip(val);
+ } else
+ key = strstrip(key);
+
+ if (!strlen(key)) {
+ printk(KERN_ERR "CUSE: zero length info key specified\n");
+ return -EINVAL;
+ }
+
+ *pp = p;
+ *keyp = key;
+ if (valp)
+ *valp = val;
+
+ return 1;
+}
+
+/**
+ * cuse_parse_dev_info - parse device info
+ * @p: device info string
+ * @len: length of device info string
+ * @devinfo: out parameter for parsed device info
+ *
+ * Parse @p to extract device info and store it into @devinfo. String
+ * pointed to by @p is modified by parsing and @devinfo points into
+ * them, so @p shouldn't be freed while @devinfo is in use.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo)
+{
+ char *end = p + len;
+ char *key, *val;
+ int rc;
+
+ while (true) {
+ rc = cuse_parse_one(&p, end, &key, &val);
+ if (rc < 0)
+ return rc;
+ if (!rc)
+ break;
+ if (strcmp(key, "DEVNAME") == 0)
+ devinfo->name = val;
+ else
+ printk(KERN_WARNING "CUSE: unknown device info \"%s\"\n",
+ key);
+ }
+
+ if (!devinfo->name || !strlen(devinfo->name)) {
+ printk(KERN_ERR "CUSE: DEVNAME unspecified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void cuse_gendev_release(struct device *dev)
+{
+ kfree(dev);
+}
+
+static void cuse_cdev_release(struct cdev *cdev)
+{
+ cuse_conn_put(cdev_to_cc(cdev));
+}
+
+/**
+ * cuse_init_worker - worker to finish initializing CUSE channel
+ * @data: cuse_conn to initialize
+ *
+ * CUSE channel is created by opening /dev/cuse but the open itself
+ * can't do much of initialization as it must return to the userland
+ * so that CUSE server and kernel can talk via the open file, so
+ * cuse_channel_open() performs minimal initialization and schedules
+ * this function on a kthread to finish up initialization.
+ *
+ * This function queries userland CUSE server which character device
+ * it wants to create and creates the character device and sets up all
+ * the required data structures for it. As there are quite a few data
+ * structures involved, object lifetime rule is a bit complex. Please
+ * read the comment at the top of this file for high level overview.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_init_worker(void *data)
+{
+ struct cuse_conn *cc = data;
+ struct cuse_init_in iin = { };
+ struct cuse_init_out iout = { };
+ struct cuse_devinfo devinfo = { };
+ struct fuse_req *req;
+ struct page *page = NULL;
+ struct device *dev;
+ bool disconnected;
+ dev_t devt;
+ int rc;
+
+ BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE);
+
+ /* identify ourself and query what the CUSE server wants */
+ req = fuse_get_req(&cc->fc);
+ if (IS_ERR(req)) {
+ rc = PTR_ERR(req);
+ goto out;
+ }
+
+ rc = -ENOMEM;
+ page = alloc_pages(GFP_KERNEL | __GFP_ZERO, 1);
+ if (!page)
+ goto out;
+
+ req->pages[0] = nth_page(page, 0);
+ req->pages[1] = nth_page(page, 1);
+ req->num_pages = 2;
+
+ req->in.h.opcode = CUSE_INIT;
+ req->in.h.nodeid = get_node_id(cc->mnt->mnt_sb->s_root->d_inode);
+ req->in.numargs = 1;
+ req->in.args[0].size = sizeof(iin);
+ req->in.args[0].value = &iin;
+
+ iin.ver_major = CUSE_KERNEL_VERSION;
+ iin.ver_minor = CUSE_KERNEL_MINOR_VERSION;
+
+ req->out.numargs = 2;
+ req->out.args[0].size = sizeof(iout);
+ req->out.args[0].value = &iout;
+ req->out.args[1].size = CUSE_INIT_INFO_MAX;
+ req->out.argpages = 1;
+ req->out.argvar = 1;
+
+ fuse_request_send(&cc->fc, req);
+ rc = req->out.h.error;
+ if (rc)
+ goto out;
+
+ /* parse init reply */
+ cc->unrestricted_ioctl = iout.flags & CUSE_UNRESTRICTED_IOCTL;
+
+ rc = cuse_parse_devinfo(page_address(page), req->out.args[1].size,
+ &devinfo);
+ if (rc)
+ goto out;
+
+ /* MAJ, MIN and name available, let's create the device */
+ devt = MKDEV(iout.dev_major, iout.dev_minor);
+ if (!MAJOR(devt))
+ rc = alloc_chrdev_region(&devt, MINOR(devt), 1, devinfo.name);
+ else
+ rc = register_chrdev_region(devt, 1, devinfo.name);
+ if (rc) {
+ printk(KERN_ERR "CUSE: failed to register chrdev region\n");
+ goto out;
+ }
+
+ rc = -ENOMEM;
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ goto out_unregister_chrdev_region;
+ device_initialize(dev);
+ dev->class = cuse_class;
+ dev->devt = devt;
+ dev->release = cuse_gendev_release;
+ dev_set_drvdata(dev, cc);
+ dev_set_name(dev, "%s", devinfo.name);
+
+ rc = device_add(dev);
+ if (rc)
+ goto out_put_device;
+
+ /* register cdev */
+ cdev_init(&cc->cdev, &cuse_frontend_fops);
+ cc->cdev.owner = THIS_MODULE;
+ cc->cdev.release = cuse_cdev_release;
+
+ rc = cdev_add(&cc->cdev, devt, 1);
+ if (rc)
+ goto out_put_device;
+ cuse_conn_get(cc); /* will be released on cdev final put */
+
+ /* transfer dev and cdev ownership to channel */
+ spin_lock(&cuse_disconnect_lock);
+ disconnected = cc->disconnected;
+ if (!disconnected) {
+ cc->dev = dev;
+ cc->cdev_added = true;
+ }
+ spin_unlock(&cuse_disconnect_lock);
+
+ if (disconnected)
+ goto out_cdev_del;
+
+ rc = 0;
+ goto out;
+
+out_cdev_del:
+ cdev_del(&cc->cdev);
+out_put_device:
+ put_device(dev);
+out_unregister_chrdev_region:
+ unregister_chrdev_region(devt, 1);
+out:
+ if (!IS_ERR(req))
+ fuse_put_request(&cc->fc, req);
+ if (page)
+ __free_pages(page, 1);
+
+ if (rc)
+ fuse_abort_conn(&cc->fc);
+
+ cuse_conn_put(cc);
+ return rc;
+}
+
+/**
+ * cuse_channel_open - open method for /dev/cuse
+ * @inode: inode for /dev/cuse
+ * @file: file struct being opened
+ *
+ * Userland CUSE server can create a CUSE device by opening /dev/cuse
+ * and replying to initilaization request the kernel sends. This
+ * function is responsible for handling CUSE device initialization.
+ * Because the fd opened by this function is used during
+ * initialization, only mnt and cuse_conn are created here and the
+ * reset of initialization is delegated to a kthread.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_channel_open(struct inode *inode, struct file *file)
+{
+ struct cuse_conn *cc;
+ struct vfsmount *mnt;
+ struct fuse_req *init_req;
+ struct task_struct *worker;
+ int rc;
+
+ /* Set up cuse_conn. cuse_conn will be created when filling
+ * in superblock for the following kern_mount().
+ */
+ mnt = kern_mount(&cuse_fs);
+ if (IS_ERR(mnt))
+ return PTR_ERR(mnt);
+
+ cc = fc_to_cc(get_fuse_conn_super(mnt->mnt_sb));
+ cc->mnt = mnt;
+
+ /* let's send fuse init request */
+ rc = -ENOMEM;
+ init_req = fuse_request_alloc();
+ if (!init_req)
+ goto err_cc_put;
+
+ cc->fc.connected = 1;
+ file->private_data = fuse_conn_get(&cc->fc);
+ fuse_send_init(&cc->fc, init_req);
+
+ /* Okay, FUSE part of initialization is complete. The rest of
+ * the initialization is a bit more involved and requires
+ * conversing with userland. Start a kthread.
+ */
+ worker = kthread_run(cuse_init_worker, cuse_conn_get(cc),
+ "cuse-init-pid%d", current->pid);
+ if (IS_ERR(worker)) {
+ fput(file);
+ rc = PTR_ERR(worker);
+ goto err_cc_put;
+ }
+
+ return 0;
+
+err_cc_put:
+ cuse_conn_put(cc);
+ return rc;
+}
+
+/**
+ * cuse_channel_release - release method for /dev/cuse
+ * @inode: inode for /dev/cuse
+ * @file: file struct being closed
+ *
+ * Disconnect the channel, deregister CUSE device and initiate
+ * destruction by putting the default reference.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_channel_release(struct inode *inode, struct file *file)
+{
+ struct cuse_conn *cc = fc_to_cc(file->private_data);
+ int rc;
+
+ spin_lock(&cuse_disconnect_lock);
+ cc->disconnected = true;
+ spin_unlock(&cuse_disconnect_lock);
+
+ rc = fuse_dev_release(inode, file);
+ if (rc)
+ return rc;
+
+ if (cc->dev)
+ device_unregister(cc->dev);
+ if (cc->cdev_added) {
+ unregister_chrdev_region(cc->cdev.dev, 1);
+ cdev_del(&cc->cdev);
+ }
+ cuse_conn_put(cc);
+
+ return 0;
+}
+
+static struct file_operations cuse_channel_fops; /* initialized during init */
+
+
+/**************************************************************************
+ * Misc stuff and module initializatiion
+ *
+ * CUSE exports the same set of attributes to sysfs as fusectl.
+ */
+
+ssize_t cuse_class_waiting_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cuse_conn *cc = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", atomic_read(&cc->fc.num_waiting));
+}
+
+ssize_t cuse_class_abort_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cuse_conn *cc = dev_get_drvdata(dev);
+
+ fuse_abort_conn(&cc->fc);
+ return count;
+}
+
+static struct device_attribute cuse_class_dev_attrs[] = {
+ __ATTR(waiting, S_IFREG | 0400, cuse_class_waiting_show, NULL),
+ __ATTR(abort, S_IFREG | 0200, NULL, cuse_class_abort_store),
+ { }
+};
+
+static struct miscdevice cuse_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "cuse",
+ .fops = &cuse_channel_fops,
+};
+
+static int __init cuse_init(void)
+{
+ int rc;
+
+ /* inherit and extend fuse_dev_operations */
+ cuse_channel_fops = fuse_dev_operations;
+ cuse_channel_fops.owner = THIS_MODULE;
+ cuse_channel_fops.open = cuse_channel_open;
+ cuse_channel_fops.release = cuse_channel_release;
+
+ cuse_class = class_create(THIS_MODULE, "cuse");
+ if (IS_ERR(cuse_class))
+ return PTR_ERR(cuse_class);
+ cuse_class->dev_attrs = cuse_class_dev_attrs;
+
+ rc = misc_register(&cuse_miscdev);
+ if (rc)
+ goto destroy_class;
+ rc = register_filesystem(&cuse_fs);
+ if (rc)
+ goto misc_deregister;
+ return 0;
+
+misc_deregister:
+ misc_deregister(&cuse_miscdev);
+destroy_class:
+ class_destroy(cuse_class);
+ return rc;
+}
+
+static void __exit cuse_exit(void)
+{
+ unregister_filesystem(&cuse_fs);
+ misc_deregister(&cuse_miscdev);
+ class_destroy(cuse_class);
+}
+
+module_init(cuse_init);
+module_exit(cuse_exit);
+
+MODULE_AUTHOR("Tejun Heo <[email protected]>");
+MODULE_DESCRIPTION("Character device in Userspace");
+MODULE_LICENSE("GPL");
Index: work/include/linux/cuse.h
===================================================================
--- /dev/null
+++ work/include/linux/cuse.h
@@ -0,0 +1,47 @@
+/*
+ * CUSE: Character device in Userspace
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ * Copyright (C) 2008 Tejun Heo <[email protected]>
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _CUSE_H_
+#define _CUSE_H_
+
+#include <linux/major.h>
+#include <linux/miscdevice.h>
+#include <linux/fuse.h>
+
+#define CUSE_KERNEL_VERSION 0
+#define CUSE_KERNEL_MINOR_VERSION 1
+
+#define CUSE_KERNEL_MAJOR MISC_MAJOR
+#define CUSE_KERNEL_MINOR MISC_DYNAMIC_MINOR
+
+#define CUSE_INIT_INFO_MAX 4096
+
+/*
+ * CUSE INIT request/reply flags
+ */
+#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */
+
+enum cuse_opcode {
+ CUSE_INIT = CUSE_BASE,
+};
+
+struct cuse_init_in {
+ __u32 ver_major;
+ __u32 ver_minor;
+ __u32 flags;
+ __u32 padding;
+};
+
+struct cuse_init_out {
+ __u32 dev_major; /* chardev major */
+ __u32 dev_minor; /* chardev minor */
+ __u32 flags;
+ __u32 padding;
+};
+
+#endif /*_CUSE_H_*/
Index: work/include/linux/fuse.h
===================================================================
--- work.orig/include/linux/fuse.h
+++ work/include/linux/fuse.h
@@ -209,6 +209,8 @@ enum fuse_opcode {
FUSE_DESTROY = 38,
FUSE_IOCTL = 39,
FUSE_POLL = 40,
+
+ CUSE_BASE = 4096,
};

enum fuse_notify_code {
Index: work/fs/Kconfig
===================================================================
--- work.orig/fs/Kconfig
+++ work/fs/Kconfig
@@ -419,6 +419,16 @@ config FUSE_FS
If you want to develop a userspace FS, or if you want to use
a filesystem based on FUSE, answer Y or M.

+config CUSE
+ tristate "Character device in Userpace support"
+ depends on FUSE_FS
+ help
+ This FUSE extension allows character devices to be
+ implemented in userspace.
+
+ If you want to develop or use userspace character device
+ based on CUSE, answer Y or M.
+
config GENERIC_ACL
bool
select FS_POSIX_ACL
Index: work/fs/fuse/Makefile
===================================================================
--- work.orig/fs/fuse/Makefile
+++ work/fs/fuse/Makefile
@@ -3,5 +3,6 @@
#

obj-$(CONFIG_FUSE_FS) += fuse.o
+obj-$(CONFIG_CUSE) += cuse.o

fuse-objs := dev.o dir.o file.o inode.o control.o
Index: work/include/linux/magic.h
===================================================================
--- work.orig/include/linux/magic.h
+++ work/include/linux/magic.h
@@ -3,10 +3,11 @@

#define ADFS_SUPER_MAGIC 0xadf5
#define AFFS_SUPER_MAGIC 0xadff
-#define AFS_SUPER_MAGIC 0x5346414F
+#define AFS_SUPER_MAGIC 0x5346414F
#define AUTOFS_SUPER_MAGIC 0x0187
#define CODA_SUPER_MAGIC 0x73757245
-#define DEBUGFS_MAGIC 0x64626720
+#define CUSE_SUPER_MAGIC 0x43555345
+#define DEBUGFS_MAGIC 0x64626720
#define SYSFS_MAGIC 0x62656572
#define SECURITYFS_MAGIC 0x73636673
#define TMPFS_MAGIC 0x01021994

2008-11-22 08:41:15

by Miklos Szeredi

[permalink] [raw]
Subject: Re: [PATCH 5/5] CUSE: implement CUSE - Character device in Userspace

On Sat, 22 Nov 2008, Tejun Heo wrote:
> Miklos Szeredi wrote:
> > On Thu, 20 Nov 2008, Tejun Heo wrote:
> >> diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
> >> index 0be9245..d4dcca8 100644
> >> --- a/include/linux/miscdevice.h
> >> +++ b/include/linux/miscdevice.h
> >> @@ -28,6 +28,7 @@
> >> #define MPT_MINOR 220
> >> #define HPET_MINOR 228
> >> #define FUSE_MINOR 229
> >> +#define CUSE_MINOR 230
> >
> > This doesn't work: device numbers have to be allocated by LANANA.
>
> My last attempt at LANANA for extended devt didn't get me anywhere and
> I'm not sure LANANA is handling miscdevice minors at all.

Back then I did get the fuse device registered in LANANA database
(http://www.lanana.org/docs/device-list/devices-2.6+.txt)

> If you go
> through the tree, there are quite a few duplicate allocations including
> FUSE_MINOR. At any rate, we can definitely try LANANA.

Ugh, I suppose that's my fault for not putting the definition in
miscdevice.h (though apparently the duplicates didn't do so either).

Thanks,
Miklos

2008-11-22 08:48:54

by Tejun Heo

[permalink] [raw]
Subject: Re: [PATCH 5/5] CUSE: implement CUSE - Character device in Userspace

Miklos Szeredi wrote:
> On Sat, 22 Nov 2008, Tejun Heo wrote:
>> Miklos Szeredi wrote:
>>> On Thu, 20 Nov 2008, Tejun Heo wrote:
>>>> diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
>>>> index 0be9245..d4dcca8 100644
>>>> --- a/include/linux/miscdevice.h
>>>> +++ b/include/linux/miscdevice.h
>>>> @@ -28,6 +28,7 @@
>>>> #define MPT_MINOR 220
>>>> #define HPET_MINOR 228
>>>> #define FUSE_MINOR 229
>>>> +#define CUSE_MINOR 230
>>> This doesn't work: device numbers have to be allocated by LANANA.
>> My last attempt at LANANA for extended devt didn't get me anywhere and
>> I'm not sure LANANA is handling miscdevice minors at all.
>
> Back then I did get the fuse device registered in LANANA database
> (http://www.lanana.org/docs/device-list/devices-2.6+.txt)

I switched it back to dynamic minor, so it's a non-issue now.

>> If you go
>> through the tree, there are quite a few duplicate allocations including
>> FUSE_MINOR. At any rate, we can definitely try LANANA.
>
> Ugh, I suppose that's my fault for not putting the definition in
> miscdevice.h (though apparently the duplicates didn't do so either).

Miscdevice minors are spread all over the place especially under archs.
I tried to move all of them into miscdevice.h but I got scared by the
dups. :-)

Thanks.

--
tejun

2008-11-26 10:02:32

by Miklos Szeredi

[permalink] [raw]
Subject: Re: [PATCH 5/5 UPDATED] CUSE: implement CUSE - Character device in Userspace

Hi Tejun,

Apologies for the late review.

On Sat, 22 Nov 2008, Tejun Heo wrote:
> + * cuse_conn : contains fuse_conn and serves as bonding structure
> + * channel : file handle connected to the userland CUSE server
> + * cdev : the implemented character device
> + * mnt : vfsmount which serves dentry and inode for cdev

Hmm, this mount thing seems rather pointless to me. I understand the
motivation: to let the unmodified fuse functions use
get_fuse_conn(inode) and get_node_id(inode). But I think we should
really bite the bullet and reimplement these functions so that they
take fuse_conn and node_id as arguments instead of the inode (and
separate out those parts which do really need the inode for the
memory mappings).

It's a bit more work on the fuse side, but overall it results in a
much cleaner architecture, don't you think?

> + worker = kthread_run(cuse_init_worker, cuse_conn_get(cc),
> + "cuse-init-pid%d", current->pid);

This could be done without a kthread, by setting the ->end() callback
for the INIT request, no?

Thanks,
Miklos

2008-11-26 10:17:11

by Tejun Heo

[permalink] [raw]
Subject: Re: [PATCH 5/5 UPDATED] CUSE: implement CUSE - Character device in Userspace

Hello,

Miklos Szeredi wrote:
> Hi Tejun,
>
> Apologies for the late review.

:-)

> On Sat, 22 Nov 2008, Tejun Heo wrote:
>> + * cuse_conn : contains fuse_conn and serves as bonding structure
>> + * channel : file handle connected to the userland CUSE server
>> + * cdev : the implemented character device
>> + * mnt : vfsmount which serves dentry and inode for cdev
>
> Hmm, this mount thing seems rather pointless to me. I understand the
> motivation: to let the unmodified fuse functions use
> get_fuse_conn(inode) and get_node_id(inode). But I think we should
> really bite the bullet and reimplement these functions so that they
> take fuse_conn and node_id as arguments instead of the inode (and
> separate out those parts which do really need the inode for the
> memory mappings).
>
> It's a bit more work on the fuse side, but overall it results in a
> much cleaner architecture, don't you think?

Yes, I agree. I was mostly being lazy. I'll try to rip off mnt
assumption from FUSE and make CUSE bypass the mnt thing.

>> + worker = kthread_run(cuse_init_worker, cuse_conn_get(cc),
>> + "cuse-init-pid%d", current->pid);
>
> This could be done without a kthread, by setting the ->end() callback
> for the INIT request, no?

Indeed. Will switch to ->end().

Thanks.

--
tejun

2008-11-26 11:34:31

by Miklos Szeredi

[permalink] [raw]
Subject: Re: [PATCH 5/5 UPDATED] CUSE: implement CUSE - Character device in Userspace

On Wed, 26 Nov 2008, Tejun Heo wrote:
> Hello,
>
> Miklos Szeredi wrote:
> > Hi Tejun,
> >
> > Apologies for the late review.
>
> :-)

I meant late compared to three months ago, when you first posted the
CUSE patches :)

> Yes, I agree. I was mostly being lazy. I'll try to rip off mnt
> assumption from FUSE and make CUSE bypass the mnt thing.
>
> >> + worker = kthread_run(cuse_init_worker, cuse_conn_get(cc),
> >> + "cuse-init-pid%d", current->pid);
> >
> > This could be done without a kthread, by setting the ->end() callback
> > for the INIT request, no?
>
> Indeed. Will switch to ->end().

Cool, thanks.

Miklos