diff options
author | Joel Becker <joel.becker@oracle.com> | 2008-02-19 03:23:28 (GMT) |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2008-04-18 15:56:06 (GMT) |
commit | 6427a727557d9c964b7b162ae11bb156e2c501d5 (patch) | |
tree | 767767f88890db68718d474e6f4502c6f580e26b /fs/ocfs2/stack_user.c | |
parent | 8adf0536c9fb578a8542dcf81104d3438a5287e4 (diff) | |
download | linux-6427a727557d9c964b7b162ae11bb156e2c501d5.tar.xz |
ocfs2: Add the ocfs2_control misc device.
The ocfs2_control misc device is how a userspace control daemon (controld)
talks to the filesystem. Introduce the bare-bones filesystem ops.
Signed-off-by: Joel Becker <joel.becker@oracle.com>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Diffstat (limited to 'fs/ocfs2/stack_user.c')
-rw-r--r-- | fs/ocfs2/stack_user.c | 184 |
1 files changed, 183 insertions, 1 deletions
diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index 920eb11..fdca5d3 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -18,17 +18,199 @@ */ #include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/mutex.h> +#include <linux/reboot.h> #include "stackglue.h" -static int __init user_stack_init(void) +/* + * The control protocol starts with a handshake. Until the handshake + * is complete, the control device will fail all write(2)s. + * + * The handshake is simple. First, the client reads until EOF. Each line + * of output is a supported protocol tag. All protocol tags are a single + * character followed by a two hex digit version number. Currently the + * only things supported is T01, for "Text-base version 0x01". Next, the + * client writes the version they would like to use. If the version tag + * written is unknown, -EINVAL is returned. Once the negotiation is + * complete, the client can start sending messages. + */ + +/* + * ocfs2_live_connection is refcounted because the filesystem and + * miscdevice sides can detach in different order. Let's just be safe. + */ +struct ocfs2_live_connection { + struct list_head oc_list; + struct ocfs2_cluster_connection *oc_conn; +}; + +static atomic_t ocfs2_control_opened; + +static LIST_HEAD(ocfs2_live_connection_list); +static DEFINE_MUTEX(ocfs2_control_lock); + +static struct ocfs2_live_connection *ocfs2_connection_find(const char *name) +{ + size_t len = strlen(name); + struct ocfs2_live_connection *c; + + BUG_ON(!mutex_is_locked(&ocfs2_control_lock)); + + list_for_each_entry(c, &ocfs2_live_connection_list, oc_list) { + if ((c->oc_conn->cc_namelen == len) && + !strncmp(c->oc_conn->cc_name, name, len)) + return c; + } + + return c; +} + +/* + * ocfs2_live_connection structures are created underneath the ocfs2 + * mount path. Since the VFS prevents multiple calls to + * fill_super(), we can't get dupes here. + */ +static int ocfs2_live_connection_new(struct ocfs2_cluster_connection *conn, + struct ocfs2_live_connection **c_ret) +{ + int rc = 0; + struct ocfs2_live_connection *c; + + c = kzalloc(sizeof(struct ocfs2_live_connection), GFP_KERNEL); + if (!c) + return -ENOMEM; + + mutex_lock(&ocfs2_control_lock); + c->oc_conn = conn; + + if (atomic_read(&ocfs2_control_opened)) + list_add(&c->oc_list, &ocfs2_live_connection_list); + else { + printk(KERN_ERR + "ocfs2: Userspace control daemon is not present\n"); + rc = -ESRCH; + } + + mutex_unlock(&ocfs2_control_lock); + + if (!rc) + *c_ret = c; + else + kfree(c); + + return rc; +} + +/* + * This function disconnects the cluster connection from ocfs2_control. + * Afterwards, userspace can't affect the cluster connection. + */ +static void ocfs2_live_connection_drop(struct ocfs2_live_connection *c) +{ + mutex_lock(&ocfs2_control_lock); + list_del_init(&c->oc_list); + c->oc_conn = NULL; + mutex_unlock(&ocfs2_control_lock); + + kfree(c); +} + + +static ssize_t ocfs2_control_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) { return 0; } +static ssize_t ocfs2_control_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + return 0; +} + +static int ocfs2_control_release(struct inode *inode, struct file *file) +{ + if (atomic_dec_and_test(&ocfs2_control_opened)) { + mutex_lock(&ocfs2_control_lock); + if (!list_empty(&ocfs2_live_connection_list)) { + /* XXX: Do bad things! */ + printk(KERN_ERR + "ocfs2: Unexpected release of ocfs2_control!\n" + " Loss of cluster connection requires " + "an emergency restart!\n"); + emergency_restart(); + } + mutex_unlock(&ocfs2_control_lock); + } + + return 0; +} + +static int ocfs2_control_open(struct inode *inode, struct file *file) +{ + atomic_inc(&ocfs2_control_opened); + + return 0; +} + +static const struct file_operations ocfs2_control_fops = { + .open = ocfs2_control_open, + .release = ocfs2_control_release, + .read = ocfs2_control_read, + .write = ocfs2_control_write, + .owner = THIS_MODULE, +}; + +struct miscdevice ocfs2_control_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ocfs2_control", + .fops = &ocfs2_control_fops, +}; + +static int ocfs2_control_init(void) +{ + int rc; + + atomic_set(&ocfs2_control_opened, 0); + + rc = misc_register(&ocfs2_control_device); + if (rc) + printk(KERN_ERR + "ocfs2: Unable to register ocfs2_control device " + "(errno %d)\n", + -rc); + + return rc; +} + +static void ocfs2_control_exit(void) +{ + int rc; + + rc = misc_deregister(&ocfs2_control_device); + if (rc) + printk(KERN_ERR + "ocfs2: Unable to deregister ocfs2_control device " + "(errno %d)\n", + -rc); +} + +static int __init user_stack_init(void) +{ + return ocfs2_control_init(); +} + static void __exit user_stack_exit(void) { + ocfs2_control_exit(); } MODULE_AUTHOR("Oracle"); |