162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * stack_user.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Code which interfaces ocfs2 with fs/dlm and a userspace stack.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2007 Oracle.  All rights reserved.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/filelock.h>
1362306a36Sopenharmony_ci#include <linux/miscdevice.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/reboot.h>
1762306a36Sopenharmony_ci#include <linux/sched.h>
1862306a36Sopenharmony_ci#include <linux/uaccess.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "stackglue.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/dlm_plock.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * The control protocol starts with a handshake.  Until the handshake
2662306a36Sopenharmony_ci * is complete, the control device will fail all write(2)s.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * The handshake is simple.  First, the client reads until EOF.  Each line
2962306a36Sopenharmony_ci * of output is a supported protocol tag.  All protocol tags are a single
3062306a36Sopenharmony_ci * character followed by a two hex digit version number.  Currently the
3162306a36Sopenharmony_ci * only things supported is T01, for "Text-base version 0x01".  Next, the
3262306a36Sopenharmony_ci * client writes the version they would like to use, including the newline.
3362306a36Sopenharmony_ci * Thus, the protocol tag is 'T01\n'.  If the version tag written is
3462306a36Sopenharmony_ci * unknown, -EINVAL is returned.  Once the negotiation is complete, the
3562306a36Sopenharmony_ci * client can start sending messages.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * The T01 protocol has three messages.  First is the "SETN" message.
3862306a36Sopenharmony_ci * It has the following syntax:
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci *  SETN<space><8-char-hex-nodenum><newline>
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * This is 14 characters.
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * The "SETN" message must be the first message following the protocol.
4562306a36Sopenharmony_ci * It tells ocfs2_control the local node number.
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * Next comes the "SETV" message.  It has the following syntax:
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci *  SETV<space><2-char-hex-major><space><2-char-hex-minor><newline>
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * This is 11 characters.
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * The "SETV" message sets the filesystem locking protocol version as
5462306a36Sopenharmony_ci * negotiated by the client.  The client negotiates based on the maximum
5562306a36Sopenharmony_ci * version advertised in /sys/fs/ocfs2/max_locking_protocol.  The major
5662306a36Sopenharmony_ci * number from the "SETV" message must match
5762306a36Sopenharmony_ci * ocfs2_user_plugin.sp_max_proto.pv_major, and the minor number
5862306a36Sopenharmony_ci * must be less than or equal to ...sp_max_version.pv_minor.
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci * Once this information has been set, mounts will be allowed.  From this
6162306a36Sopenharmony_ci * point on, the "DOWN" message can be sent for node down notification.
6262306a36Sopenharmony_ci * It has the following syntax:
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci *  DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline>
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci * eg:
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci *  DOWN 632A924FDD844190BDA93C0DF6B94899 00000001\n
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * This is 47 characters.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/*
7462306a36Sopenharmony_ci * Whether or not the client has done the handshake.
7562306a36Sopenharmony_ci * For now, we have just one protocol version.
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_ci#define OCFS2_CONTROL_PROTO			"T01\n"
7862306a36Sopenharmony_ci#define OCFS2_CONTROL_PROTO_LEN			4
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* Handshake states */
8162306a36Sopenharmony_ci#define OCFS2_CONTROL_HANDSHAKE_INVALID		(0)
8262306a36Sopenharmony_ci#define OCFS2_CONTROL_HANDSHAKE_READ		(1)
8362306a36Sopenharmony_ci#define OCFS2_CONTROL_HANDSHAKE_PROTOCOL	(2)
8462306a36Sopenharmony_ci#define OCFS2_CONTROL_HANDSHAKE_VALID		(3)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* Messages */
8762306a36Sopenharmony_ci#define OCFS2_CONTROL_MESSAGE_OP_LEN		4
8862306a36Sopenharmony_ci#define OCFS2_CONTROL_MESSAGE_SETNODE_OP	"SETN"
8962306a36Sopenharmony_ci#define OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN	14
9062306a36Sopenharmony_ci#define OCFS2_CONTROL_MESSAGE_SETVERSION_OP	"SETV"
9162306a36Sopenharmony_ci#define OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN	11
9262306a36Sopenharmony_ci#define OCFS2_CONTROL_MESSAGE_DOWN_OP		"DOWN"
9362306a36Sopenharmony_ci#define OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN	47
9462306a36Sopenharmony_ci#define OCFS2_TEXT_UUID_LEN			32
9562306a36Sopenharmony_ci#define OCFS2_CONTROL_MESSAGE_VERNUM_LEN	2
9662306a36Sopenharmony_ci#define OCFS2_CONTROL_MESSAGE_NODENUM_LEN	8
9762306a36Sopenharmony_ci#define VERSION_LOCK				"version_lock"
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cienum ocfs2_connection_type {
10062306a36Sopenharmony_ci	WITH_CONTROLD,
10162306a36Sopenharmony_ci	NO_CONTROLD
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * ocfs2_live_connection is refcounted because the filesystem and
10662306a36Sopenharmony_ci * miscdevice sides can detach in different order.  Let's just be safe.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cistruct ocfs2_live_connection {
10962306a36Sopenharmony_ci	struct list_head		oc_list;
11062306a36Sopenharmony_ci	struct ocfs2_cluster_connection	*oc_conn;
11162306a36Sopenharmony_ci	enum ocfs2_connection_type	oc_type;
11262306a36Sopenharmony_ci	atomic_t                        oc_this_node;
11362306a36Sopenharmony_ci	int                             oc_our_slot;
11462306a36Sopenharmony_ci	struct dlm_lksb                 oc_version_lksb;
11562306a36Sopenharmony_ci	char                            oc_lvb[DLM_LVB_LEN];
11662306a36Sopenharmony_ci	struct completion               oc_sync_wait;
11762306a36Sopenharmony_ci	wait_queue_head_t		oc_wait;
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistruct ocfs2_control_private {
12162306a36Sopenharmony_ci	struct list_head op_list;
12262306a36Sopenharmony_ci	int op_state;
12362306a36Sopenharmony_ci	int op_this_node;
12462306a36Sopenharmony_ci	struct ocfs2_protocol_version op_proto;
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/* SETN<space><8-char-hex-nodenum><newline> */
12862306a36Sopenharmony_cistruct ocfs2_control_message_setn {
12962306a36Sopenharmony_ci	char	tag[OCFS2_CONTROL_MESSAGE_OP_LEN];
13062306a36Sopenharmony_ci	char	space;
13162306a36Sopenharmony_ci	char	nodestr[OCFS2_CONTROL_MESSAGE_NODENUM_LEN];
13262306a36Sopenharmony_ci	char	newline;
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/* SETV<space><2-char-hex-major><space><2-char-hex-minor><newline> */
13662306a36Sopenharmony_cistruct ocfs2_control_message_setv {
13762306a36Sopenharmony_ci	char	tag[OCFS2_CONTROL_MESSAGE_OP_LEN];
13862306a36Sopenharmony_ci	char	space1;
13962306a36Sopenharmony_ci	char	major[OCFS2_CONTROL_MESSAGE_VERNUM_LEN];
14062306a36Sopenharmony_ci	char	space2;
14162306a36Sopenharmony_ci	char	minor[OCFS2_CONTROL_MESSAGE_VERNUM_LEN];
14262306a36Sopenharmony_ci	char	newline;
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/* DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline> */
14662306a36Sopenharmony_cistruct ocfs2_control_message_down {
14762306a36Sopenharmony_ci	char	tag[OCFS2_CONTROL_MESSAGE_OP_LEN];
14862306a36Sopenharmony_ci	char	space1;
14962306a36Sopenharmony_ci	char	uuid[OCFS2_TEXT_UUID_LEN];
15062306a36Sopenharmony_ci	char	space2;
15162306a36Sopenharmony_ci	char	nodestr[OCFS2_CONTROL_MESSAGE_NODENUM_LEN];
15262306a36Sopenharmony_ci	char	newline;
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciunion ocfs2_control_message {
15662306a36Sopenharmony_ci	char					tag[OCFS2_CONTROL_MESSAGE_OP_LEN];
15762306a36Sopenharmony_ci	struct ocfs2_control_message_setn	u_setn;
15862306a36Sopenharmony_ci	struct ocfs2_control_message_setv	u_setv;
15962306a36Sopenharmony_ci	struct ocfs2_control_message_down	u_down;
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic struct ocfs2_stack_plugin ocfs2_user_plugin;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic atomic_t ocfs2_control_opened;
16562306a36Sopenharmony_cistatic int ocfs2_control_this_node = -1;
16662306a36Sopenharmony_cistatic struct ocfs2_protocol_version running_proto;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic LIST_HEAD(ocfs2_live_connection_list);
16962306a36Sopenharmony_cistatic LIST_HEAD(ocfs2_control_private_list);
17062306a36Sopenharmony_cistatic DEFINE_MUTEX(ocfs2_control_lock);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic inline void ocfs2_control_set_handshake_state(struct file *file,
17362306a36Sopenharmony_ci						     int state)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct ocfs2_control_private *p = file->private_data;
17662306a36Sopenharmony_ci	p->op_state = state;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic inline int ocfs2_control_get_handshake_state(struct file *file)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct ocfs2_control_private *p = file->private_data;
18262306a36Sopenharmony_ci	return p->op_state;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic struct ocfs2_live_connection *ocfs2_connection_find(const char *name)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	size_t len = strlen(name);
18862306a36Sopenharmony_ci	struct ocfs2_live_connection *c;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	BUG_ON(!mutex_is_locked(&ocfs2_control_lock));
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	list_for_each_entry(c, &ocfs2_live_connection_list, oc_list) {
19362306a36Sopenharmony_ci		if ((c->oc_conn->cc_namelen == len) &&
19462306a36Sopenharmony_ci		    !strncmp(c->oc_conn->cc_name, name, len))
19562306a36Sopenharmony_ci			return c;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return NULL;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/*
20262306a36Sopenharmony_ci * ocfs2_live_connection structures are created underneath the ocfs2
20362306a36Sopenharmony_ci * mount path.  Since the VFS prevents multiple calls to
20462306a36Sopenharmony_ci * fill_super(), we can't get dupes here.
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_cistatic int ocfs2_live_connection_attach(struct ocfs2_cluster_connection *conn,
20762306a36Sopenharmony_ci				     struct ocfs2_live_connection *c)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	int rc = 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	mutex_lock(&ocfs2_control_lock);
21262306a36Sopenharmony_ci	c->oc_conn = conn;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if ((c->oc_type == NO_CONTROLD) || atomic_read(&ocfs2_control_opened))
21562306a36Sopenharmony_ci		list_add(&c->oc_list, &ocfs2_live_connection_list);
21662306a36Sopenharmony_ci	else {
21762306a36Sopenharmony_ci		printk(KERN_ERR
21862306a36Sopenharmony_ci		       "ocfs2: Userspace control daemon is not present\n");
21962306a36Sopenharmony_ci		rc = -ESRCH;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	mutex_unlock(&ocfs2_control_lock);
22362306a36Sopenharmony_ci	return rc;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/*
22762306a36Sopenharmony_ci * This function disconnects the cluster connection from ocfs2_control.
22862306a36Sopenharmony_ci * Afterwards, userspace can't affect the cluster connection.
22962306a36Sopenharmony_ci */
23062306a36Sopenharmony_cistatic void ocfs2_live_connection_drop(struct ocfs2_live_connection *c)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	mutex_lock(&ocfs2_control_lock);
23362306a36Sopenharmony_ci	list_del_init(&c->oc_list);
23462306a36Sopenharmony_ci	c->oc_conn = NULL;
23562306a36Sopenharmony_ci	mutex_unlock(&ocfs2_control_lock);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	kfree(c);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int ocfs2_control_cfu(void *target, size_t target_len,
24162306a36Sopenharmony_ci			     const char __user *buf, size_t count)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	/* The T01 expects write(2) calls to have exactly one command */
24462306a36Sopenharmony_ci	if ((count != target_len) ||
24562306a36Sopenharmony_ci	    (count > sizeof(union ocfs2_control_message)))
24662306a36Sopenharmony_ci		return -EINVAL;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (copy_from_user(target, buf, target_len))
24962306a36Sopenharmony_ci		return -EFAULT;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic ssize_t ocfs2_control_validate_protocol(struct file *file,
25562306a36Sopenharmony_ci					       const char __user *buf,
25662306a36Sopenharmony_ci					       size_t count)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	ssize_t ret;
25962306a36Sopenharmony_ci	char kbuf[OCFS2_CONTROL_PROTO_LEN];
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	ret = ocfs2_control_cfu(kbuf, OCFS2_CONTROL_PROTO_LEN,
26262306a36Sopenharmony_ci				buf, count);
26362306a36Sopenharmony_ci	if (ret)
26462306a36Sopenharmony_ci		return ret;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (strncmp(kbuf, OCFS2_CONTROL_PROTO, OCFS2_CONTROL_PROTO_LEN))
26762306a36Sopenharmony_ci		return -EINVAL;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ocfs2_control_set_handshake_state(file,
27062306a36Sopenharmony_ci					  OCFS2_CONTROL_HANDSHAKE_PROTOCOL);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return count;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void ocfs2_control_send_down(const char *uuid,
27662306a36Sopenharmony_ci				    int nodenum)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct ocfs2_live_connection *c;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	mutex_lock(&ocfs2_control_lock);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	c = ocfs2_connection_find(uuid);
28362306a36Sopenharmony_ci	if (c) {
28462306a36Sopenharmony_ci		BUG_ON(c->oc_conn == NULL);
28562306a36Sopenharmony_ci		c->oc_conn->cc_recovery_handler(nodenum,
28662306a36Sopenharmony_ci						c->oc_conn->cc_recovery_data);
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	mutex_unlock(&ocfs2_control_lock);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci/*
29362306a36Sopenharmony_ci * Called whenever configuration elements are sent to /dev/ocfs2_control.
29462306a36Sopenharmony_ci * If all configuration elements are present, try to set the global
29562306a36Sopenharmony_ci * values.  If there is a problem, return an error.  Skip any missing
29662306a36Sopenharmony_ci * elements, and only bump ocfs2_control_opened when we have all elements
29762306a36Sopenharmony_ci * and are successful.
29862306a36Sopenharmony_ci */
29962306a36Sopenharmony_cistatic int ocfs2_control_install_private(struct file *file)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	int rc = 0;
30262306a36Sopenharmony_ci	int set_p = 1;
30362306a36Sopenharmony_ci	struct ocfs2_control_private *p = file->private_data;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	BUG_ON(p->op_state != OCFS2_CONTROL_HANDSHAKE_PROTOCOL);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	mutex_lock(&ocfs2_control_lock);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (p->op_this_node < 0) {
31062306a36Sopenharmony_ci		set_p = 0;
31162306a36Sopenharmony_ci	} else if ((ocfs2_control_this_node >= 0) &&
31262306a36Sopenharmony_ci		   (ocfs2_control_this_node != p->op_this_node)) {
31362306a36Sopenharmony_ci		rc = -EINVAL;
31462306a36Sopenharmony_ci		goto out_unlock;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (!p->op_proto.pv_major) {
31862306a36Sopenharmony_ci		set_p = 0;
31962306a36Sopenharmony_ci	} else if (!list_empty(&ocfs2_live_connection_list) &&
32062306a36Sopenharmony_ci		   ((running_proto.pv_major != p->op_proto.pv_major) ||
32162306a36Sopenharmony_ci		    (running_proto.pv_minor != p->op_proto.pv_minor))) {
32262306a36Sopenharmony_ci		rc = -EINVAL;
32362306a36Sopenharmony_ci		goto out_unlock;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (set_p) {
32762306a36Sopenharmony_ci		ocfs2_control_this_node = p->op_this_node;
32862306a36Sopenharmony_ci		running_proto.pv_major = p->op_proto.pv_major;
32962306a36Sopenharmony_ci		running_proto.pv_minor = p->op_proto.pv_minor;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ciout_unlock:
33362306a36Sopenharmony_ci	mutex_unlock(&ocfs2_control_lock);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (!rc && set_p) {
33662306a36Sopenharmony_ci		/* We set the global values successfully */
33762306a36Sopenharmony_ci		atomic_inc(&ocfs2_control_opened);
33862306a36Sopenharmony_ci		ocfs2_control_set_handshake_state(file,
33962306a36Sopenharmony_ci					OCFS2_CONTROL_HANDSHAKE_VALID);
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return rc;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int ocfs2_control_get_this_node(void)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	int rc;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	mutex_lock(&ocfs2_control_lock);
35062306a36Sopenharmony_ci	if (ocfs2_control_this_node < 0)
35162306a36Sopenharmony_ci		rc = -EINVAL;
35262306a36Sopenharmony_ci	else
35362306a36Sopenharmony_ci		rc = ocfs2_control_this_node;
35462306a36Sopenharmony_ci	mutex_unlock(&ocfs2_control_lock);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return rc;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int ocfs2_control_do_setnode_msg(struct file *file,
36062306a36Sopenharmony_ci					struct ocfs2_control_message_setn *msg)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	long nodenum;
36362306a36Sopenharmony_ci	char *ptr = NULL;
36462306a36Sopenharmony_ci	struct ocfs2_control_private *p = file->private_data;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (ocfs2_control_get_handshake_state(file) !=
36762306a36Sopenharmony_ci	    OCFS2_CONTROL_HANDSHAKE_PROTOCOL)
36862306a36Sopenharmony_ci		return -EINVAL;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (strncmp(msg->tag, OCFS2_CONTROL_MESSAGE_SETNODE_OP,
37162306a36Sopenharmony_ci		    OCFS2_CONTROL_MESSAGE_OP_LEN))
37262306a36Sopenharmony_ci		return -EINVAL;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if ((msg->space != ' ') || (msg->newline != '\n'))
37562306a36Sopenharmony_ci		return -EINVAL;
37662306a36Sopenharmony_ci	msg->space = msg->newline = '\0';
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	nodenum = simple_strtol(msg->nodestr, &ptr, 16);
37962306a36Sopenharmony_ci	if (!ptr || *ptr)
38062306a36Sopenharmony_ci		return -EINVAL;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if ((nodenum == LONG_MIN) || (nodenum == LONG_MAX) ||
38362306a36Sopenharmony_ci	    (nodenum > INT_MAX) || (nodenum < 0))
38462306a36Sopenharmony_ci		return -ERANGE;
38562306a36Sopenharmony_ci	p->op_this_node = nodenum;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return ocfs2_control_install_private(file);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic int ocfs2_control_do_setversion_msg(struct file *file,
39162306a36Sopenharmony_ci					   struct ocfs2_control_message_setv *msg)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	long major, minor;
39462306a36Sopenharmony_ci	char *ptr = NULL;
39562306a36Sopenharmony_ci	struct ocfs2_control_private *p = file->private_data;
39662306a36Sopenharmony_ci	struct ocfs2_protocol_version *max =
39762306a36Sopenharmony_ci		&ocfs2_user_plugin.sp_max_proto;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (ocfs2_control_get_handshake_state(file) !=
40062306a36Sopenharmony_ci	    OCFS2_CONTROL_HANDSHAKE_PROTOCOL)
40162306a36Sopenharmony_ci		return -EINVAL;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (strncmp(msg->tag, OCFS2_CONTROL_MESSAGE_SETVERSION_OP,
40462306a36Sopenharmony_ci		    OCFS2_CONTROL_MESSAGE_OP_LEN))
40562306a36Sopenharmony_ci		return -EINVAL;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if ((msg->space1 != ' ') || (msg->space2 != ' ') ||
40862306a36Sopenharmony_ci	    (msg->newline != '\n'))
40962306a36Sopenharmony_ci		return -EINVAL;
41062306a36Sopenharmony_ci	msg->space1 = msg->space2 = msg->newline = '\0';
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	major = simple_strtol(msg->major, &ptr, 16);
41362306a36Sopenharmony_ci	if (!ptr || *ptr)
41462306a36Sopenharmony_ci		return -EINVAL;
41562306a36Sopenharmony_ci	minor = simple_strtol(msg->minor, &ptr, 16);
41662306a36Sopenharmony_ci	if (!ptr || *ptr)
41762306a36Sopenharmony_ci		return -EINVAL;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/*
42062306a36Sopenharmony_ci	 * The major must be between 1 and 255, inclusive.  The minor
42162306a36Sopenharmony_ci	 * must be between 0 and 255, inclusive.  The version passed in
42262306a36Sopenharmony_ci	 * must be within the maximum version supported by the filesystem.
42362306a36Sopenharmony_ci	 */
42462306a36Sopenharmony_ci	if ((major == LONG_MIN) || (major == LONG_MAX) ||
42562306a36Sopenharmony_ci	    (major > (u8)-1) || (major < 1))
42662306a36Sopenharmony_ci		return -ERANGE;
42762306a36Sopenharmony_ci	if ((minor == LONG_MIN) || (minor == LONG_MAX) ||
42862306a36Sopenharmony_ci	    (minor > (u8)-1) || (minor < 0))
42962306a36Sopenharmony_ci		return -ERANGE;
43062306a36Sopenharmony_ci	if ((major != max->pv_major) ||
43162306a36Sopenharmony_ci	    (minor > max->pv_minor))
43262306a36Sopenharmony_ci		return -EINVAL;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	p->op_proto.pv_major = major;
43562306a36Sopenharmony_ci	p->op_proto.pv_minor = minor;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	return ocfs2_control_install_private(file);
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic int ocfs2_control_do_down_msg(struct file *file,
44162306a36Sopenharmony_ci				     struct ocfs2_control_message_down *msg)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	long nodenum;
44462306a36Sopenharmony_ci	char *p = NULL;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (ocfs2_control_get_handshake_state(file) !=
44762306a36Sopenharmony_ci	    OCFS2_CONTROL_HANDSHAKE_VALID)
44862306a36Sopenharmony_ci		return -EINVAL;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (strncmp(msg->tag, OCFS2_CONTROL_MESSAGE_DOWN_OP,
45162306a36Sopenharmony_ci		    OCFS2_CONTROL_MESSAGE_OP_LEN))
45262306a36Sopenharmony_ci		return -EINVAL;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if ((msg->space1 != ' ') || (msg->space2 != ' ') ||
45562306a36Sopenharmony_ci	    (msg->newline != '\n'))
45662306a36Sopenharmony_ci		return -EINVAL;
45762306a36Sopenharmony_ci	msg->space1 = msg->space2 = msg->newline = '\0';
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	nodenum = simple_strtol(msg->nodestr, &p, 16);
46062306a36Sopenharmony_ci	if (!p || *p)
46162306a36Sopenharmony_ci		return -EINVAL;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if ((nodenum == LONG_MIN) || (nodenum == LONG_MAX) ||
46462306a36Sopenharmony_ci	    (nodenum > INT_MAX) || (nodenum < 0))
46562306a36Sopenharmony_ci		return -ERANGE;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	ocfs2_control_send_down(msg->uuid, nodenum);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return 0;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic ssize_t ocfs2_control_message(struct file *file,
47362306a36Sopenharmony_ci				     const char __user *buf,
47462306a36Sopenharmony_ci				     size_t count)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	ssize_t ret;
47762306a36Sopenharmony_ci	union ocfs2_control_message msg;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* Try to catch padding issues */
48062306a36Sopenharmony_ci	WARN_ON(offsetof(struct ocfs2_control_message_down, uuid) !=
48162306a36Sopenharmony_ci		(sizeof(msg.u_down.tag) + sizeof(msg.u_down.space1)));
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	memset(&msg, 0, sizeof(union ocfs2_control_message));
48462306a36Sopenharmony_ci	ret = ocfs2_control_cfu(&msg, count, buf, count);
48562306a36Sopenharmony_ci	if (ret)
48662306a36Sopenharmony_ci		goto out;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if ((count == OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN) &&
48962306a36Sopenharmony_ci	    !strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_SETNODE_OP,
49062306a36Sopenharmony_ci		     OCFS2_CONTROL_MESSAGE_OP_LEN))
49162306a36Sopenharmony_ci		ret = ocfs2_control_do_setnode_msg(file, &msg.u_setn);
49262306a36Sopenharmony_ci	else if ((count == OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN) &&
49362306a36Sopenharmony_ci		 !strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_SETVERSION_OP,
49462306a36Sopenharmony_ci			  OCFS2_CONTROL_MESSAGE_OP_LEN))
49562306a36Sopenharmony_ci		ret = ocfs2_control_do_setversion_msg(file, &msg.u_setv);
49662306a36Sopenharmony_ci	else if ((count == OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN) &&
49762306a36Sopenharmony_ci		 !strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_DOWN_OP,
49862306a36Sopenharmony_ci			  OCFS2_CONTROL_MESSAGE_OP_LEN))
49962306a36Sopenharmony_ci		ret = ocfs2_control_do_down_msg(file, &msg.u_down);
50062306a36Sopenharmony_ci	else
50162306a36Sopenharmony_ci		ret = -EINVAL;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ciout:
50462306a36Sopenharmony_ci	return ret ? ret : count;
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic ssize_t ocfs2_control_write(struct file *file,
50862306a36Sopenharmony_ci				   const char __user *buf,
50962306a36Sopenharmony_ci				   size_t count,
51062306a36Sopenharmony_ci				   loff_t *ppos)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	ssize_t ret;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	switch (ocfs2_control_get_handshake_state(file)) {
51562306a36Sopenharmony_ci		case OCFS2_CONTROL_HANDSHAKE_INVALID:
51662306a36Sopenharmony_ci			ret = -EINVAL;
51762306a36Sopenharmony_ci			break;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		case OCFS2_CONTROL_HANDSHAKE_READ:
52062306a36Sopenharmony_ci			ret = ocfs2_control_validate_protocol(file, buf,
52162306a36Sopenharmony_ci							      count);
52262306a36Sopenharmony_ci			break;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci		case OCFS2_CONTROL_HANDSHAKE_PROTOCOL:
52562306a36Sopenharmony_ci		case OCFS2_CONTROL_HANDSHAKE_VALID:
52662306a36Sopenharmony_ci			ret = ocfs2_control_message(file, buf, count);
52762306a36Sopenharmony_ci			break;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		default:
53062306a36Sopenharmony_ci			BUG();
53162306a36Sopenharmony_ci			ret = -EIO;
53262306a36Sopenharmony_ci			break;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return ret;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci/*
53962306a36Sopenharmony_ci * This is a naive version.  If we ever have a new protocol, we'll expand
54062306a36Sopenharmony_ci * it.  Probably using seq_file.
54162306a36Sopenharmony_ci */
54262306a36Sopenharmony_cistatic ssize_t ocfs2_control_read(struct file *file,
54362306a36Sopenharmony_ci				  char __user *buf,
54462306a36Sopenharmony_ci				  size_t count,
54562306a36Sopenharmony_ci				  loff_t *ppos)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	ssize_t ret;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	ret = simple_read_from_buffer(buf, count, ppos,
55062306a36Sopenharmony_ci			OCFS2_CONTROL_PROTO, OCFS2_CONTROL_PROTO_LEN);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* Have we read the whole protocol list? */
55362306a36Sopenharmony_ci	if (ret > 0 && *ppos >= OCFS2_CONTROL_PROTO_LEN)
55462306a36Sopenharmony_ci		ocfs2_control_set_handshake_state(file,
55562306a36Sopenharmony_ci						  OCFS2_CONTROL_HANDSHAKE_READ);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	return ret;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic int ocfs2_control_release(struct inode *inode, struct file *file)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct ocfs2_control_private *p = file->private_data;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	mutex_lock(&ocfs2_control_lock);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (ocfs2_control_get_handshake_state(file) !=
56762306a36Sopenharmony_ci	    OCFS2_CONTROL_HANDSHAKE_VALID)
56862306a36Sopenharmony_ci		goto out;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (atomic_dec_and_test(&ocfs2_control_opened)) {
57162306a36Sopenharmony_ci		if (!list_empty(&ocfs2_live_connection_list)) {
57262306a36Sopenharmony_ci			/* XXX: Do bad things! */
57362306a36Sopenharmony_ci			printk(KERN_ERR
57462306a36Sopenharmony_ci			       "ocfs2: Unexpected release of ocfs2_control!\n"
57562306a36Sopenharmony_ci			       "       Loss of cluster connection requires "
57662306a36Sopenharmony_ci			       "an emergency restart!\n");
57762306a36Sopenharmony_ci			emergency_restart();
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci		/*
58062306a36Sopenharmony_ci		 * Last valid close clears the node number and resets
58162306a36Sopenharmony_ci		 * the locking protocol version
58262306a36Sopenharmony_ci		 */
58362306a36Sopenharmony_ci		ocfs2_control_this_node = -1;
58462306a36Sopenharmony_ci		running_proto.pv_major = 0;
58562306a36Sopenharmony_ci		running_proto.pv_minor = 0;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ciout:
58962306a36Sopenharmony_ci	list_del_init(&p->op_list);
59062306a36Sopenharmony_ci	file->private_data = NULL;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	mutex_unlock(&ocfs2_control_lock);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	kfree(p);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	return 0;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic int ocfs2_control_open(struct inode *inode, struct file *file)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct ocfs2_control_private *p;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	p = kzalloc(sizeof(struct ocfs2_control_private), GFP_KERNEL);
60462306a36Sopenharmony_ci	if (!p)
60562306a36Sopenharmony_ci		return -ENOMEM;
60662306a36Sopenharmony_ci	p->op_this_node = -1;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	mutex_lock(&ocfs2_control_lock);
60962306a36Sopenharmony_ci	file->private_data = p;
61062306a36Sopenharmony_ci	list_add(&p->op_list, &ocfs2_control_private_list);
61162306a36Sopenharmony_ci	mutex_unlock(&ocfs2_control_lock);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	return 0;
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic const struct file_operations ocfs2_control_fops = {
61762306a36Sopenharmony_ci	.open    = ocfs2_control_open,
61862306a36Sopenharmony_ci	.release = ocfs2_control_release,
61962306a36Sopenharmony_ci	.read    = ocfs2_control_read,
62062306a36Sopenharmony_ci	.write   = ocfs2_control_write,
62162306a36Sopenharmony_ci	.owner   = THIS_MODULE,
62262306a36Sopenharmony_ci	.llseek  = default_llseek,
62362306a36Sopenharmony_ci};
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic struct miscdevice ocfs2_control_device = {
62662306a36Sopenharmony_ci	.minor		= MISC_DYNAMIC_MINOR,
62762306a36Sopenharmony_ci	.name		= "ocfs2_control",
62862306a36Sopenharmony_ci	.fops		= &ocfs2_control_fops,
62962306a36Sopenharmony_ci};
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic int ocfs2_control_init(void)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	int rc;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	atomic_set(&ocfs2_control_opened, 0);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	rc = misc_register(&ocfs2_control_device);
63862306a36Sopenharmony_ci	if (rc)
63962306a36Sopenharmony_ci		printk(KERN_ERR
64062306a36Sopenharmony_ci		       "ocfs2: Unable to register ocfs2_control device "
64162306a36Sopenharmony_ci		       "(errno %d)\n",
64262306a36Sopenharmony_ci		       -rc);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	return rc;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic void ocfs2_control_exit(void)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	misc_deregister(&ocfs2_control_device);
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic void fsdlm_lock_ast_wrapper(void *astarg)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct ocfs2_dlm_lksb *lksb = astarg;
65562306a36Sopenharmony_ci	int status = lksb->lksb_fsdlm.sb_status;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/*
65862306a36Sopenharmony_ci	 * For now we're punting on the issue of other non-standard errors
65962306a36Sopenharmony_ci	 * where we can't tell if the unlock_ast or lock_ast should be called.
66062306a36Sopenharmony_ci	 * The main "other error" that's possible is EINVAL which means the
66162306a36Sopenharmony_ci	 * function was called with invalid args, which shouldn't be possible
66262306a36Sopenharmony_ci	 * since the caller here is under our control.  Other non-standard
66362306a36Sopenharmony_ci	 * errors probably fall into the same category, or otherwise are fatal
66462306a36Sopenharmony_ci	 * which means we can't carry on anyway.
66562306a36Sopenharmony_ci	 */
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (status == -DLM_EUNLOCK || status == -DLM_ECANCEL)
66862306a36Sopenharmony_ci		lksb->lksb_conn->cc_proto->lp_unlock_ast(lksb, 0);
66962306a36Sopenharmony_ci	else
67062306a36Sopenharmony_ci		lksb->lksb_conn->cc_proto->lp_lock_ast(lksb);
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic void fsdlm_blocking_ast_wrapper(void *astarg, int level)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct ocfs2_dlm_lksb *lksb = astarg;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	lksb->lksb_conn->cc_proto->lp_blocking_ast(lksb, level);
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cistatic int user_dlm_lock(struct ocfs2_cluster_connection *conn,
68162306a36Sopenharmony_ci			 int mode,
68262306a36Sopenharmony_ci			 struct ocfs2_dlm_lksb *lksb,
68362306a36Sopenharmony_ci			 u32 flags,
68462306a36Sopenharmony_ci			 void *name,
68562306a36Sopenharmony_ci			 unsigned int namelen)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	if (!lksb->lksb_fsdlm.sb_lvbptr)
68862306a36Sopenharmony_ci		lksb->lksb_fsdlm.sb_lvbptr = (char *)lksb +
68962306a36Sopenharmony_ci					     sizeof(struct dlm_lksb);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	return dlm_lock(conn->cc_lockspace, mode, &lksb->lksb_fsdlm,
69262306a36Sopenharmony_ci			flags|DLM_LKF_NODLCKWT, name, namelen, 0,
69362306a36Sopenharmony_ci			fsdlm_lock_ast_wrapper, lksb,
69462306a36Sopenharmony_ci			fsdlm_blocking_ast_wrapper);
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic int user_dlm_unlock(struct ocfs2_cluster_connection *conn,
69862306a36Sopenharmony_ci			   struct ocfs2_dlm_lksb *lksb,
69962306a36Sopenharmony_ci			   u32 flags)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	return dlm_unlock(conn->cc_lockspace, lksb->lksb_fsdlm.sb_lkid,
70262306a36Sopenharmony_ci			  flags, &lksb->lksb_fsdlm, lksb);
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic int user_dlm_lock_status(struct ocfs2_dlm_lksb *lksb)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	return lksb->lksb_fsdlm.sb_status;
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic int user_dlm_lvb_valid(struct ocfs2_dlm_lksb *lksb)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	int invalid = lksb->lksb_fsdlm.sb_flags & DLM_SBF_VALNOTVALID;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	return !invalid;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic void *user_dlm_lvb(struct ocfs2_dlm_lksb *lksb)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	if (!lksb->lksb_fsdlm.sb_lvbptr)
72062306a36Sopenharmony_ci		lksb->lksb_fsdlm.sb_lvbptr = (char *)lksb +
72162306a36Sopenharmony_ci					     sizeof(struct dlm_lksb);
72262306a36Sopenharmony_ci	return (void *)(lksb->lksb_fsdlm.sb_lvbptr);
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic void user_dlm_dump_lksb(struct ocfs2_dlm_lksb *lksb)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic int user_plock(struct ocfs2_cluster_connection *conn,
73062306a36Sopenharmony_ci		      u64 ino,
73162306a36Sopenharmony_ci		      struct file *file,
73262306a36Sopenharmony_ci		      int cmd,
73362306a36Sopenharmony_ci		      struct file_lock *fl)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	/*
73662306a36Sopenharmony_ci	 * This more or less just demuxes the plock request into any
73762306a36Sopenharmony_ci	 * one of three dlm calls.
73862306a36Sopenharmony_ci	 *
73962306a36Sopenharmony_ci	 * Internally, fs/dlm will pass these to a misc device, which
74062306a36Sopenharmony_ci	 * a userspace daemon will read and write to.
74162306a36Sopenharmony_ci	 */
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (cmd == F_CANCELLK)
74462306a36Sopenharmony_ci		return dlm_posix_cancel(conn->cc_lockspace, ino, file, fl);
74562306a36Sopenharmony_ci	else if (IS_GETLK(cmd))
74662306a36Sopenharmony_ci		return dlm_posix_get(conn->cc_lockspace, ino, file, fl);
74762306a36Sopenharmony_ci	else if (fl->fl_type == F_UNLCK)
74862306a36Sopenharmony_ci		return dlm_posix_unlock(conn->cc_lockspace, ino, file, fl);
74962306a36Sopenharmony_ci	else
75062306a36Sopenharmony_ci		return dlm_posix_lock(conn->cc_lockspace, ino, file, cmd, fl);
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci/*
75462306a36Sopenharmony_ci * Compare a requested locking protocol version against the current one.
75562306a36Sopenharmony_ci *
75662306a36Sopenharmony_ci * If the major numbers are different, they are incompatible.
75762306a36Sopenharmony_ci * If the current minor is greater than the request, they are incompatible.
75862306a36Sopenharmony_ci * If the current minor is less than or equal to the request, they are
75962306a36Sopenharmony_ci * compatible, and the requester should run at the current minor version.
76062306a36Sopenharmony_ci */
76162306a36Sopenharmony_cistatic int fs_protocol_compare(struct ocfs2_protocol_version *existing,
76262306a36Sopenharmony_ci			       struct ocfs2_protocol_version *request)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	if (existing->pv_major != request->pv_major)
76562306a36Sopenharmony_ci		return 1;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (existing->pv_minor > request->pv_minor)
76862306a36Sopenharmony_ci		return 1;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (existing->pv_minor < request->pv_minor)
77162306a36Sopenharmony_ci		request->pv_minor = existing->pv_minor;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	return 0;
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic void lvb_to_version(char *lvb, struct ocfs2_protocol_version *ver)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	struct ocfs2_protocol_version *pv =
77962306a36Sopenharmony_ci		(struct ocfs2_protocol_version *)lvb;
78062306a36Sopenharmony_ci	/*
78162306a36Sopenharmony_ci	 * ocfs2_protocol_version has two u8 variables, so we don't
78262306a36Sopenharmony_ci	 * need any endian conversion.
78362306a36Sopenharmony_ci	 */
78462306a36Sopenharmony_ci	ver->pv_major = pv->pv_major;
78562306a36Sopenharmony_ci	ver->pv_minor = pv->pv_minor;
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic void version_to_lvb(struct ocfs2_protocol_version *ver, char *lvb)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct ocfs2_protocol_version *pv =
79162306a36Sopenharmony_ci		(struct ocfs2_protocol_version *)lvb;
79262306a36Sopenharmony_ci	/*
79362306a36Sopenharmony_ci	 * ocfs2_protocol_version has two u8 variables, so we don't
79462306a36Sopenharmony_ci	 * need any endian conversion.
79562306a36Sopenharmony_ci	 */
79662306a36Sopenharmony_ci	pv->pv_major = ver->pv_major;
79762306a36Sopenharmony_ci	pv->pv_minor = ver->pv_minor;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic void sync_wait_cb(void *arg)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct ocfs2_cluster_connection *conn = arg;
80362306a36Sopenharmony_ci	struct ocfs2_live_connection *lc = conn->cc_private;
80462306a36Sopenharmony_ci	complete(&lc->oc_sync_wait);
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic int sync_unlock(struct ocfs2_cluster_connection *conn,
80862306a36Sopenharmony_ci		struct dlm_lksb *lksb, char *name)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	int error;
81162306a36Sopenharmony_ci	struct ocfs2_live_connection *lc = conn->cc_private;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	error = dlm_unlock(conn->cc_lockspace, lksb->sb_lkid, 0, lksb, conn);
81462306a36Sopenharmony_ci	if (error) {
81562306a36Sopenharmony_ci		printk(KERN_ERR "%s lkid %x error %d\n",
81662306a36Sopenharmony_ci				name, lksb->sb_lkid, error);
81762306a36Sopenharmony_ci		return error;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	wait_for_completion(&lc->oc_sync_wait);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	if (lksb->sb_status != -DLM_EUNLOCK) {
82362306a36Sopenharmony_ci		printk(KERN_ERR "%s lkid %x status %d\n",
82462306a36Sopenharmony_ci				name, lksb->sb_lkid, lksb->sb_status);
82562306a36Sopenharmony_ci		return -1;
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci	return 0;
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_cistatic int sync_lock(struct ocfs2_cluster_connection *conn,
83162306a36Sopenharmony_ci		int mode, uint32_t flags,
83262306a36Sopenharmony_ci		struct dlm_lksb *lksb, char *name)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	int error, status;
83562306a36Sopenharmony_ci	struct ocfs2_live_connection *lc = conn->cc_private;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	error = dlm_lock(conn->cc_lockspace, mode, lksb, flags,
83862306a36Sopenharmony_ci			name, strlen(name),
83962306a36Sopenharmony_ci			0, sync_wait_cb, conn, NULL);
84062306a36Sopenharmony_ci	if (error) {
84162306a36Sopenharmony_ci		printk(KERN_ERR "%s lkid %x flags %x mode %d error %d\n",
84262306a36Sopenharmony_ci				name, lksb->sb_lkid, flags, mode, error);
84362306a36Sopenharmony_ci		return error;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	wait_for_completion(&lc->oc_sync_wait);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	status = lksb->sb_status;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (status && status != -EAGAIN) {
85162306a36Sopenharmony_ci		printk(KERN_ERR "%s lkid %x flags %x mode %d status %d\n",
85262306a36Sopenharmony_ci				name, lksb->sb_lkid, flags, mode, status);
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	return status;
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic int version_lock(struct ocfs2_cluster_connection *conn, int mode,
86062306a36Sopenharmony_ci		int flags)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	struct ocfs2_live_connection *lc = conn->cc_private;
86362306a36Sopenharmony_ci	return sync_lock(conn, mode, flags,
86462306a36Sopenharmony_ci			&lc->oc_version_lksb, VERSION_LOCK);
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic int version_unlock(struct ocfs2_cluster_connection *conn)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	struct ocfs2_live_connection *lc = conn->cc_private;
87062306a36Sopenharmony_ci	return sync_unlock(conn, &lc->oc_version_lksb, VERSION_LOCK);
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci/* get_protocol_version()
87462306a36Sopenharmony_ci *
87562306a36Sopenharmony_ci * To exchange ocfs2 versioning, we use the LVB of the version dlm lock.
87662306a36Sopenharmony_ci * The algorithm is:
87762306a36Sopenharmony_ci * 1. Attempt to take the lock in EX mode (non-blocking).
87862306a36Sopenharmony_ci * 2. If successful (which means it is the first mount), write the
87962306a36Sopenharmony_ci *    version number and downconvert to PR lock.
88062306a36Sopenharmony_ci * 3. If unsuccessful (returns -EAGAIN), read the version from the LVB after
88162306a36Sopenharmony_ci *    taking the PR lock.
88262306a36Sopenharmony_ci */
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic int get_protocol_version(struct ocfs2_cluster_connection *conn)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	int ret;
88762306a36Sopenharmony_ci	struct ocfs2_live_connection *lc = conn->cc_private;
88862306a36Sopenharmony_ci	struct ocfs2_protocol_version pv;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	running_proto.pv_major =
89162306a36Sopenharmony_ci		ocfs2_user_plugin.sp_max_proto.pv_major;
89262306a36Sopenharmony_ci	running_proto.pv_minor =
89362306a36Sopenharmony_ci		ocfs2_user_plugin.sp_max_proto.pv_minor;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	lc->oc_version_lksb.sb_lvbptr = lc->oc_lvb;
89662306a36Sopenharmony_ci	ret = version_lock(conn, DLM_LOCK_EX,
89762306a36Sopenharmony_ci			DLM_LKF_VALBLK|DLM_LKF_NOQUEUE);
89862306a36Sopenharmony_ci	if (!ret) {
89962306a36Sopenharmony_ci		conn->cc_version.pv_major = running_proto.pv_major;
90062306a36Sopenharmony_ci		conn->cc_version.pv_minor = running_proto.pv_minor;
90162306a36Sopenharmony_ci		version_to_lvb(&running_proto, lc->oc_lvb);
90262306a36Sopenharmony_ci		version_lock(conn, DLM_LOCK_PR, DLM_LKF_CONVERT|DLM_LKF_VALBLK);
90362306a36Sopenharmony_ci	} else if (ret == -EAGAIN) {
90462306a36Sopenharmony_ci		ret = version_lock(conn, DLM_LOCK_PR, DLM_LKF_VALBLK);
90562306a36Sopenharmony_ci		if (ret)
90662306a36Sopenharmony_ci			goto out;
90762306a36Sopenharmony_ci		lvb_to_version(lc->oc_lvb, &pv);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci		if ((pv.pv_major != running_proto.pv_major) ||
91062306a36Sopenharmony_ci				(pv.pv_minor > running_proto.pv_minor)) {
91162306a36Sopenharmony_ci			ret = -EINVAL;
91262306a36Sopenharmony_ci			goto out;
91362306a36Sopenharmony_ci		}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		conn->cc_version.pv_major = pv.pv_major;
91662306a36Sopenharmony_ci		conn->cc_version.pv_minor = pv.pv_minor;
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ciout:
91962306a36Sopenharmony_ci	return ret;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic void user_recover_prep(void *arg)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cistatic void user_recover_slot(void *arg, struct dlm_slot *slot)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct ocfs2_cluster_connection *conn = arg;
92962306a36Sopenharmony_ci	printk(KERN_INFO "ocfs2: Node %d/%d down. Initiating recovery.\n",
93062306a36Sopenharmony_ci			slot->nodeid, slot->slot);
93162306a36Sopenharmony_ci	conn->cc_recovery_handler(slot->nodeid, conn->cc_recovery_data);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic void user_recover_done(void *arg, struct dlm_slot *slots,
93662306a36Sopenharmony_ci		int num_slots, int our_slot,
93762306a36Sopenharmony_ci		uint32_t generation)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	struct ocfs2_cluster_connection *conn = arg;
94062306a36Sopenharmony_ci	struct ocfs2_live_connection *lc = conn->cc_private;
94162306a36Sopenharmony_ci	int i;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	for (i = 0; i < num_slots; i++)
94462306a36Sopenharmony_ci		if (slots[i].slot == our_slot) {
94562306a36Sopenharmony_ci			atomic_set(&lc->oc_this_node, slots[i].nodeid);
94662306a36Sopenharmony_ci			break;
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	lc->oc_our_slot = our_slot;
95062306a36Sopenharmony_ci	wake_up(&lc->oc_wait);
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_cistatic const struct dlm_lockspace_ops ocfs2_ls_ops = {
95462306a36Sopenharmony_ci	.recover_prep = user_recover_prep,
95562306a36Sopenharmony_ci	.recover_slot = user_recover_slot,
95662306a36Sopenharmony_ci	.recover_done = user_recover_done,
95762306a36Sopenharmony_ci};
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic int user_cluster_disconnect(struct ocfs2_cluster_connection *conn)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	version_unlock(conn);
96262306a36Sopenharmony_ci	dlm_release_lockspace(conn->cc_lockspace, 2);
96362306a36Sopenharmony_ci	conn->cc_lockspace = NULL;
96462306a36Sopenharmony_ci	ocfs2_live_connection_drop(conn->cc_private);
96562306a36Sopenharmony_ci	conn->cc_private = NULL;
96662306a36Sopenharmony_ci	return 0;
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic int user_cluster_connect(struct ocfs2_cluster_connection *conn)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	dlm_lockspace_t *fsdlm;
97262306a36Sopenharmony_ci	struct ocfs2_live_connection *lc;
97362306a36Sopenharmony_ci	int rc, ops_rv;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	BUG_ON(conn == NULL);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	lc = kzalloc(sizeof(struct ocfs2_live_connection), GFP_KERNEL);
97862306a36Sopenharmony_ci	if (!lc)
97962306a36Sopenharmony_ci		return -ENOMEM;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	init_waitqueue_head(&lc->oc_wait);
98262306a36Sopenharmony_ci	init_completion(&lc->oc_sync_wait);
98362306a36Sopenharmony_ci	atomic_set(&lc->oc_this_node, 0);
98462306a36Sopenharmony_ci	conn->cc_private = lc;
98562306a36Sopenharmony_ci	lc->oc_type = NO_CONTROLD;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	rc = dlm_new_lockspace(conn->cc_name, conn->cc_cluster_name,
98862306a36Sopenharmony_ci			       DLM_LSFL_NEWEXCL, DLM_LVB_LEN,
98962306a36Sopenharmony_ci			       &ocfs2_ls_ops, conn, &ops_rv, &fsdlm);
99062306a36Sopenharmony_ci	if (rc) {
99162306a36Sopenharmony_ci		if (rc == -EEXIST || rc == -EPROTO)
99262306a36Sopenharmony_ci			printk(KERN_ERR "ocfs2: Unable to create the "
99362306a36Sopenharmony_ci				"lockspace %s (%d), because a ocfs2-tools "
99462306a36Sopenharmony_ci				"program is running on this file system "
99562306a36Sopenharmony_ci				"with the same name lockspace\n",
99662306a36Sopenharmony_ci				conn->cc_name, rc);
99762306a36Sopenharmony_ci		goto out;
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	if (ops_rv == -EOPNOTSUPP) {
100162306a36Sopenharmony_ci		lc->oc_type = WITH_CONTROLD;
100262306a36Sopenharmony_ci		printk(KERN_NOTICE "ocfs2: You seem to be using an older "
100362306a36Sopenharmony_ci				"version of dlm_controld and/or ocfs2-tools."
100462306a36Sopenharmony_ci				" Please consider upgrading.\n");
100562306a36Sopenharmony_ci	} else if (ops_rv) {
100662306a36Sopenharmony_ci		rc = ops_rv;
100762306a36Sopenharmony_ci		goto out;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci	conn->cc_lockspace = fsdlm;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	rc = ocfs2_live_connection_attach(conn, lc);
101262306a36Sopenharmony_ci	if (rc)
101362306a36Sopenharmony_ci		goto out;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (lc->oc_type == NO_CONTROLD) {
101662306a36Sopenharmony_ci		rc = get_protocol_version(conn);
101762306a36Sopenharmony_ci		if (rc) {
101862306a36Sopenharmony_ci			printk(KERN_ERR "ocfs2: Could not determine"
101962306a36Sopenharmony_ci					" locking version\n");
102062306a36Sopenharmony_ci			user_cluster_disconnect(conn);
102162306a36Sopenharmony_ci			goto out;
102262306a36Sopenharmony_ci		}
102362306a36Sopenharmony_ci		wait_event(lc->oc_wait, (atomic_read(&lc->oc_this_node) > 0));
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	/*
102762306a36Sopenharmony_ci	 * running_proto must have been set before we allowed any mounts
102862306a36Sopenharmony_ci	 * to proceed.
102962306a36Sopenharmony_ci	 */
103062306a36Sopenharmony_ci	if (fs_protocol_compare(&running_proto, &conn->cc_version)) {
103162306a36Sopenharmony_ci		printk(KERN_ERR
103262306a36Sopenharmony_ci		       "Unable to mount with fs locking protocol version "
103362306a36Sopenharmony_ci		       "%u.%u because negotiated protocol is %u.%u\n",
103462306a36Sopenharmony_ci		       conn->cc_version.pv_major, conn->cc_version.pv_minor,
103562306a36Sopenharmony_ci		       running_proto.pv_major, running_proto.pv_minor);
103662306a36Sopenharmony_ci		rc = -EPROTO;
103762306a36Sopenharmony_ci		ocfs2_live_connection_drop(lc);
103862306a36Sopenharmony_ci		lc = NULL;
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ciout:
104262306a36Sopenharmony_ci	if (rc)
104362306a36Sopenharmony_ci		kfree(lc);
104462306a36Sopenharmony_ci	return rc;
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_cistatic int user_cluster_this_node(struct ocfs2_cluster_connection *conn,
104962306a36Sopenharmony_ci				  unsigned int *this_node)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	int rc;
105262306a36Sopenharmony_ci	struct ocfs2_live_connection *lc = conn->cc_private;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (lc->oc_type == WITH_CONTROLD)
105562306a36Sopenharmony_ci		rc = ocfs2_control_get_this_node();
105662306a36Sopenharmony_ci	else if (lc->oc_type == NO_CONTROLD)
105762306a36Sopenharmony_ci		rc = atomic_read(&lc->oc_this_node);
105862306a36Sopenharmony_ci	else
105962306a36Sopenharmony_ci		rc = -EINVAL;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (rc < 0)
106262306a36Sopenharmony_ci		return rc;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	*this_node = rc;
106562306a36Sopenharmony_ci	return 0;
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_cistatic struct ocfs2_stack_operations ocfs2_user_plugin_ops = {
106962306a36Sopenharmony_ci	.connect	= user_cluster_connect,
107062306a36Sopenharmony_ci	.disconnect	= user_cluster_disconnect,
107162306a36Sopenharmony_ci	.this_node	= user_cluster_this_node,
107262306a36Sopenharmony_ci	.dlm_lock	= user_dlm_lock,
107362306a36Sopenharmony_ci	.dlm_unlock	= user_dlm_unlock,
107462306a36Sopenharmony_ci	.lock_status	= user_dlm_lock_status,
107562306a36Sopenharmony_ci	.lvb_valid	= user_dlm_lvb_valid,
107662306a36Sopenharmony_ci	.lock_lvb	= user_dlm_lvb,
107762306a36Sopenharmony_ci	.plock		= user_plock,
107862306a36Sopenharmony_ci	.dump_lksb	= user_dlm_dump_lksb,
107962306a36Sopenharmony_ci};
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic struct ocfs2_stack_plugin ocfs2_user_plugin = {
108262306a36Sopenharmony_ci	.sp_name	= "user",
108362306a36Sopenharmony_ci	.sp_ops		= &ocfs2_user_plugin_ops,
108462306a36Sopenharmony_ci	.sp_owner	= THIS_MODULE,
108562306a36Sopenharmony_ci};
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cistatic int __init ocfs2_user_plugin_init(void)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci	int rc;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	rc = ocfs2_control_init();
109362306a36Sopenharmony_ci	if (!rc) {
109462306a36Sopenharmony_ci		rc = ocfs2_stack_glue_register(&ocfs2_user_plugin);
109562306a36Sopenharmony_ci		if (rc)
109662306a36Sopenharmony_ci			ocfs2_control_exit();
109762306a36Sopenharmony_ci	}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	return rc;
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic void __exit ocfs2_user_plugin_exit(void)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	ocfs2_stack_glue_unregister(&ocfs2_user_plugin);
110562306a36Sopenharmony_ci	ocfs2_control_exit();
110662306a36Sopenharmony_ci}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ciMODULE_AUTHOR("Oracle");
110962306a36Sopenharmony_ciMODULE_DESCRIPTION("ocfs2 driver for userspace cluster stacks");
111062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
111162306a36Sopenharmony_cimodule_init(ocfs2_user_plugin_init);
111262306a36Sopenharmony_cimodule_exit(ocfs2_user_plugin_exit);
1113