1/*
2 * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
3 * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
4 *
5 * This copyrighted material is made available to anyone wishing to use,
6 * modify, copy, or redistribute it subject to the terms and conditions
7 * of the GNU General Public License version 2.
8 */
9
10#include "lock_dlm.h"
11
12const struct lm_lockops gdlm_ops;
13
14
15static struct gdlm_ls *init_gdlm(lm_callback_t cb, struct gfs2_sbd *sdp,
16				 int flags, char *table_name)
17{
18	struct gdlm_ls *ls;
19	char buf[256], *p;
20
21	ls = kzalloc(sizeof(struct gdlm_ls), GFP_KERNEL);
22	if (!ls)
23		return NULL;
24
25	ls->drop_locks_count = GDLM_DROP_COUNT;
26	ls->drop_locks_period = GDLM_DROP_PERIOD;
27	ls->fscb = cb;
28	ls->sdp = sdp;
29	ls->fsflags = flags;
30	spin_lock_init(&ls->async_lock);
31	INIT_LIST_HEAD(&ls->complete);
32	INIT_LIST_HEAD(&ls->blocking);
33	INIT_LIST_HEAD(&ls->delayed);
34	INIT_LIST_HEAD(&ls->submit);
35	INIT_LIST_HEAD(&ls->all_locks);
36	init_waitqueue_head(&ls->thread_wait);
37	init_waitqueue_head(&ls->wait_control);
38	ls->thread1 = NULL;
39	ls->thread2 = NULL;
40	ls->drop_time = jiffies;
41	ls->jid = -1;
42
43	strncpy(buf, table_name, 256);
44	buf[255] = '\0';
45
46	p = strchr(buf, ':');
47	if (!p) {
48		log_info("invalid table_name \"%s\"", table_name);
49		kfree(ls);
50		return NULL;
51	}
52	*p = '\0';
53	p++;
54
55	strncpy(ls->clustername, buf, GDLM_NAME_LEN);
56	strncpy(ls->fsname, p, GDLM_NAME_LEN);
57
58	return ls;
59}
60
61static int make_args(struct gdlm_ls *ls, char *data_arg, int *nodir)
62{
63	char data[256];
64	char *options, *x, *y;
65	int error = 0;
66
67	memset(data, 0, 256);
68	strncpy(data, data_arg, 255);
69
70	for (options = data; (x = strsep(&options, ":")); ) {
71		if (!*x)
72			continue;
73
74		y = strchr(x, '=');
75		if (y)
76			*y++ = 0;
77
78		if (!strcmp(x, "jid")) {
79			if (!y) {
80				log_error("need argument to jid");
81				error = -EINVAL;
82				break;
83			}
84			sscanf(y, "%u", &ls->jid);
85
86		} else if (!strcmp(x, "first")) {
87			if (!y) {
88				log_error("need argument to first");
89				error = -EINVAL;
90				break;
91			}
92			sscanf(y, "%u", &ls->first);
93
94		} else if (!strcmp(x, "id")) {
95			if (!y) {
96				log_error("need argument to id");
97				error = -EINVAL;
98				break;
99			}
100			sscanf(y, "%u", &ls->id);
101
102		} else if (!strcmp(x, "nodir")) {
103			if (!y) {
104				log_error("need argument to nodir");
105				error = -EINVAL;
106				break;
107			}
108			sscanf(y, "%u", nodir);
109
110		} else {
111			log_error("unkonwn option: %s", x);
112			error = -EINVAL;
113			break;
114		}
115	}
116
117	return error;
118}
119
120static int gdlm_mount(char *table_name, char *host_data,
121			lm_callback_t cb, void *cb_data,
122			unsigned int min_lvb_size, int flags,
123			struct lm_lockstruct *lockstruct,
124			struct kobject *fskobj)
125{
126	struct gdlm_ls *ls;
127	int error = -ENOMEM, nodir = 0;
128
129	if (min_lvb_size > GDLM_LVB_SIZE)
130		goto out;
131
132	ls = init_gdlm(cb, cb_data, flags, table_name);
133	if (!ls)
134		goto out;
135
136	error = make_args(ls, host_data, &nodir);
137	if (error)
138		goto out;
139
140	error = gdlm_init_threads(ls);
141	if (error)
142		goto out_free;
143
144	error = gdlm_kobject_setup(ls, fskobj);
145	if (error)
146		goto out_thread;
147
148	error = dlm_new_lockspace(ls->fsname, strlen(ls->fsname),
149				  &ls->dlm_lockspace,
150				  nodir ? DLM_LSFL_NODIR : 0,
151				  GDLM_LVB_SIZE);
152	if (error) {
153		log_error("dlm_new_lockspace error %d", error);
154		goto out_kobj;
155	}
156
157	lockstruct->ls_jid = ls->jid;
158	lockstruct->ls_first = ls->first;
159	lockstruct->ls_lockspace = ls;
160	lockstruct->ls_ops = &gdlm_ops;
161	lockstruct->ls_flags = 0;
162	lockstruct->ls_lvb_size = GDLM_LVB_SIZE;
163	return 0;
164
165out_kobj:
166	gdlm_kobject_release(ls);
167out_thread:
168	gdlm_release_threads(ls);
169out_free:
170	kfree(ls);
171out:
172	return error;
173}
174
175static void gdlm_unmount(void *lockspace)
176{
177	struct gdlm_ls *ls = lockspace;
178	int rv;
179
180	log_debug("unmount flags %lx", ls->flags);
181
182
183	if (test_bit(DFL_WITHDRAW, &ls->flags))
184		goto out;
185
186	gdlm_kobject_release(ls);
187	dlm_release_lockspace(ls->dlm_lockspace, 2);
188	gdlm_release_threads(ls);
189	rv = gdlm_release_all_locks(ls);
190	if (rv)
191		log_info("gdlm_unmount: %d stray locks freed", rv);
192out:
193	kfree(ls);
194}
195
196static void gdlm_recovery_done(void *lockspace, unsigned int jid,
197                               unsigned int message)
198{
199	struct gdlm_ls *ls = lockspace;
200	ls->recover_jid_done = jid;
201	ls->recover_jid_status = message;
202	kobject_uevent(&ls->kobj, KOBJ_CHANGE);
203}
204
205static void gdlm_others_may_mount(void *lockspace)
206{
207	struct gdlm_ls *ls = lockspace;
208	ls->first_done = 1;
209	kobject_uevent(&ls->kobj, KOBJ_CHANGE);
210}
211
212/* Userspace gets the offline uevent, blocks new gfs locks on
213   other mounters, and lets us know (sets WITHDRAW flag).  Then,
214   userspace leaves the mount group while we leave the lockspace. */
215
216static void gdlm_withdraw(void *lockspace)
217{
218	struct gdlm_ls *ls = lockspace;
219
220	kobject_uevent(&ls->kobj, KOBJ_OFFLINE);
221
222	wait_event_interruptible(ls->wait_control,
223				 test_bit(DFL_WITHDRAW, &ls->flags));
224
225	dlm_release_lockspace(ls->dlm_lockspace, 2);
226	gdlm_release_threads(ls);
227	gdlm_release_all_locks(ls);
228	gdlm_kobject_release(ls);
229}
230
231const struct lm_lockops gdlm_ops = {
232	.lm_proto_name = "lock_dlm",
233	.lm_mount = gdlm_mount,
234	.lm_others_may_mount = gdlm_others_may_mount,
235	.lm_unmount = gdlm_unmount,
236	.lm_withdraw = gdlm_withdraw,
237	.lm_get_lock = gdlm_get_lock,
238	.lm_put_lock = gdlm_put_lock,
239	.lm_lock = gdlm_lock,
240	.lm_unlock = gdlm_unlock,
241	.lm_plock = gdlm_plock,
242	.lm_punlock = gdlm_punlock,
243	.lm_plock_get = gdlm_plock_get,
244	.lm_cancel = gdlm_cancel,
245	.lm_hold_lvb = gdlm_hold_lvb,
246	.lm_unhold_lvb = gdlm_unhold_lvb,
247	.lm_recovery_done = gdlm_recovery_done,
248	.lm_owner = THIS_MODULE,
249};
250