1/*
2 * Copyright (C) 2005 Red Hat, Inc.  All rights reserved.
3 *
4 * This copyrighted material is made available to anyone wishing to use,
5 * modify, copy, or redistribute it subject to the terms and conditions
6 * of the GNU General Public License version 2.
7 */
8
9#include <linux/miscdevice.h>
10#include <linux/lock_dlm_plock.h>
11#include <linux/poll.h>
12
13#include "lock_dlm.h"
14
15
16static spinlock_t ops_lock;
17static struct list_head send_list;
18static struct list_head recv_list;
19static wait_queue_head_t send_wq;
20static wait_queue_head_t recv_wq;
21
22struct plock_op {
23	struct list_head list;
24	int done;
25	struct gdlm_plock_info info;
26};
27
28struct plock_xop {
29	struct plock_op xop;
30	void *callback;
31	void *fl;
32	void *file;
33	struct file_lock flc;
34};
35
36
37static inline void set_version(struct gdlm_plock_info *info)
38{
39	info->version[0] = GDLM_PLOCK_VERSION_MAJOR;
40	info->version[1] = GDLM_PLOCK_VERSION_MINOR;
41	info->version[2] = GDLM_PLOCK_VERSION_PATCH;
42}
43
44static int check_version(struct gdlm_plock_info *info)
45{
46	if ((GDLM_PLOCK_VERSION_MAJOR != info->version[0]) ||
47	    (GDLM_PLOCK_VERSION_MINOR < info->version[1])) {
48		log_error("plock device version mismatch: "
49			  "kernel (%u.%u.%u), user (%u.%u.%u)",
50			  GDLM_PLOCK_VERSION_MAJOR,
51			  GDLM_PLOCK_VERSION_MINOR,
52			  GDLM_PLOCK_VERSION_PATCH,
53			  info->version[0],
54			  info->version[1],
55			  info->version[2]);
56		return -EINVAL;
57	}
58	return 0;
59}
60
61static void send_op(struct plock_op *op)
62{
63	set_version(&op->info);
64	INIT_LIST_HEAD(&op->list);
65	spin_lock(&ops_lock);
66	list_add_tail(&op->list, &send_list);
67	spin_unlock(&ops_lock);
68	wake_up(&send_wq);
69}
70
71int gdlm_plock(void *lockspace, struct lm_lockname *name,
72	       struct file *file, int cmd, struct file_lock *fl)
73{
74	struct gdlm_ls *ls = lockspace;
75	struct plock_op *op;
76	struct plock_xop *xop;
77	int rv;
78
79	xop = kzalloc(sizeof(*xop), GFP_KERNEL);
80	if (!xop)
81		return -ENOMEM;
82
83	op = &xop->xop;
84	op->info.optype		= GDLM_PLOCK_OP_LOCK;
85	op->info.pid		= fl->fl_pid;
86	op->info.ex		= (fl->fl_type == F_WRLCK);
87	op->info.wait		= IS_SETLKW(cmd);
88	op->info.fsid		= ls->id;
89	op->info.number		= name->ln_number;
90	op->info.start		= fl->fl_start;
91	op->info.end		= fl->fl_end;
92	op->info.owner		= (__u64)(long) fl->fl_owner;
93	if (fl->fl_lmops && fl->fl_lmops->fl_grant) {
94		xop->callback	= fl->fl_lmops->fl_grant;
95		locks_init_lock(&xop->flc);
96		locks_copy_lock(&xop->flc, fl);
97		xop->fl		= fl;
98		xop->file	= file;
99	} else
100		xop->callback	= NULL;
101
102	send_op(op);
103
104	if (xop->callback == NULL)
105		wait_event(recv_wq, (op->done != 0));
106	else
107		return -EINPROGRESS;
108
109	spin_lock(&ops_lock);
110	if (!list_empty(&op->list)) {
111		printk(KERN_INFO "plock op on list\n");
112		list_del(&op->list);
113	}
114	spin_unlock(&ops_lock);
115
116	rv = op->info.rv;
117
118	if (!rv) {
119		if (posix_lock_file_wait(file, fl) < 0)
120			log_error("gdlm_plock: vfs lock error %x,%llx",
121				  name->ln_type,
122				  (unsigned long long)name->ln_number);
123	}
124
125	kfree(xop);
126	return rv;
127}
128
129/* Returns failure iff a succesful lock operation should be canceled */
130static int gdlm_plock_callback(struct plock_op *op)
131{
132	struct file *file;
133	struct file_lock *fl;
134	struct file_lock *flc;
135	int (*notify)(void *, void *, int) = NULL;
136	struct plock_xop *xop = (struct plock_xop *)op;
137	int rv = 0;
138
139	spin_lock(&ops_lock);
140	if (!list_empty(&op->list)) {
141		printk(KERN_INFO "plock op on list\n");
142		list_del(&op->list);
143	}
144	spin_unlock(&ops_lock);
145
146	/* check if the following 2 are still valid or make a copy */
147	file = xop->file;
148	flc = &xop->flc;
149	fl = xop->fl;
150	notify = xop->callback;
151
152	if (op->info.rv) {
153		notify(flc, NULL, op->info.rv);
154		goto out;
155	}
156
157	/* got fs lock; bookkeep locally as well: */
158	flc->fl_flags &= ~FL_SLEEP;
159	if (posix_lock_file(file, flc, NULL)) {
160		/*
161		 * This can only happen in the case of kmalloc() failure.
162		 * The filesystem's own lock is the authoritative lock,
163		 * so a failure to get the lock locally is not a disaster.
164		 * As long as GFS cannot reliably cancel locks (especially
165		 * in a low-memory situation), we're better off ignoring
166		 * this failure than trying to recover.
167		 */
168		log_error("gdlm_plock: vfs lock error file %p fl %p",
169				file, fl);
170	}
171
172	rv = notify(flc, NULL, 0);
173	if (rv) {
174		printk("gfs2 lock granted after lock request failed;"
175						" dangling lock!\n");
176		goto out;
177	}
178
179out:
180	kfree(xop);
181	return rv;
182}
183
184int gdlm_punlock(void *lockspace, struct lm_lockname *name,
185		 struct file *file, struct file_lock *fl)
186{
187	struct gdlm_ls *ls = lockspace;
188	struct plock_op *op;
189	int rv;
190
191	op = kzalloc(sizeof(*op), GFP_KERNEL);
192	if (!op)
193		return -ENOMEM;
194
195	if (posix_lock_file_wait(file, fl) < 0)
196		log_error("gdlm_punlock: vfs unlock error %x,%llx",
197			  name->ln_type, (unsigned long long)name->ln_number);
198
199	op->info.optype		= GDLM_PLOCK_OP_UNLOCK;
200	op->info.pid		= fl->fl_pid;
201	op->info.fsid		= ls->id;
202	op->info.number		= name->ln_number;
203	op->info.start		= fl->fl_start;
204	op->info.end		= fl->fl_end;
205	op->info.owner		= (__u64)(long) fl->fl_owner;
206
207	send_op(op);
208	wait_event(recv_wq, (op->done != 0));
209
210	spin_lock(&ops_lock);
211	if (!list_empty(&op->list)) {
212		printk(KERN_INFO "punlock op on list\n");
213		list_del(&op->list);
214	}
215	spin_unlock(&ops_lock);
216
217	rv = op->info.rv;
218
219	if (rv == -ENOENT)
220		rv = 0;
221
222	kfree(op);
223	return rv;
224}
225
226int gdlm_plock_get(void *lockspace, struct lm_lockname *name,
227		   struct file *file, struct file_lock *fl)
228{
229	struct gdlm_ls *ls = lockspace;
230	struct plock_op *op;
231	int rv;
232
233	op = kzalloc(sizeof(*op), GFP_KERNEL);
234	if (!op)
235		return -ENOMEM;
236
237	op->info.optype		= GDLM_PLOCK_OP_GET;
238	op->info.pid		= fl->fl_pid;
239	op->info.ex		= (fl->fl_type == F_WRLCK);
240	op->info.fsid		= ls->id;
241	op->info.number		= name->ln_number;
242	op->info.start		= fl->fl_start;
243	op->info.end		= fl->fl_end;
244
245
246	send_op(op);
247	wait_event(recv_wq, (op->done != 0));
248
249	spin_lock(&ops_lock);
250	if (!list_empty(&op->list)) {
251		printk(KERN_INFO "plock_get op on list\n");
252		list_del(&op->list);
253	}
254	spin_unlock(&ops_lock);
255
256	rv = op->info.rv;
257
258	fl->fl_type = F_UNLCK;
259	if (rv == -ENOENT)
260		rv = 0;
261	else if (rv == 0 && op->info.pid != fl->fl_pid) {
262		fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK;
263		fl->fl_pid = op->info.pid;
264		fl->fl_start = op->info.start;
265		fl->fl_end = op->info.end;
266	}
267
268	kfree(op);
269	return rv;
270}
271
272/* a read copies out one plock request from the send list */
273static ssize_t dev_read(struct file *file, char __user *u, size_t count,
274			loff_t *ppos)
275{
276	struct gdlm_plock_info info;
277	struct plock_op *op = NULL;
278
279	if (count < sizeof(info))
280		return -EINVAL;
281
282	spin_lock(&ops_lock);
283	if (!list_empty(&send_list)) {
284		op = list_entry(send_list.next, struct plock_op, list);
285		list_move(&op->list, &recv_list);
286		memcpy(&info, &op->info, sizeof(info));
287	}
288	spin_unlock(&ops_lock);
289
290	if (!op)
291		return -EAGAIN;
292
293	if (copy_to_user(u, &info, sizeof(info)))
294		return -EFAULT;
295	return sizeof(info);
296}
297
298/* a write copies in one plock result that should match a plock_op
299   on the recv list */
300static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
301			 loff_t *ppos)
302{
303	struct gdlm_plock_info info;
304	struct plock_op *op;
305	int found = 0;
306
307	if (count != sizeof(info))
308		return -EINVAL;
309
310	if (copy_from_user(&info, u, sizeof(info)))
311		return -EFAULT;
312
313	if (check_version(&info))
314		return -EINVAL;
315
316	spin_lock(&ops_lock);
317	list_for_each_entry(op, &recv_list, list) {
318		if (op->info.fsid == info.fsid && op->info.number == info.number &&
319		    op->info.owner == info.owner) {
320			list_del_init(&op->list);
321			found = 1;
322			op->done = 1;
323			memcpy(&op->info, &info, sizeof(info));
324			break;
325		}
326	}
327	spin_unlock(&ops_lock);
328
329	if (found) {
330		struct plock_xop *xop;
331		xop = (struct plock_xop *)op;
332		if (xop->callback)
333			count = gdlm_plock_callback(op);
334		else
335			wake_up(&recv_wq);
336	} else
337		printk(KERN_INFO "gdlm dev_write no op %x %llx\n", info.fsid,
338			(unsigned long long)info.number);
339	return count;
340}
341
342static unsigned int dev_poll(struct file *file, poll_table *wait)
343{
344	poll_wait(file, &send_wq, wait);
345
346	spin_lock(&ops_lock);
347	if (!list_empty(&send_list)) {
348		spin_unlock(&ops_lock);
349		return POLLIN | POLLRDNORM;
350	}
351	spin_unlock(&ops_lock);
352	return 0;
353}
354
355static const struct file_operations dev_fops = {
356	.read    = dev_read,
357	.write   = dev_write,
358	.poll    = dev_poll,
359	.owner   = THIS_MODULE
360};
361
362static struct miscdevice plock_dev_misc = {
363	.minor = MISC_DYNAMIC_MINOR,
364	.name = GDLM_PLOCK_MISC_NAME,
365	.fops = &dev_fops
366};
367
368int gdlm_plock_init(void)
369{
370	int rv;
371
372	spin_lock_init(&ops_lock);
373	INIT_LIST_HEAD(&send_list);
374	INIT_LIST_HEAD(&recv_list);
375	init_waitqueue_head(&send_wq);
376	init_waitqueue_head(&recv_wq);
377
378	rv = misc_register(&plock_dev_misc);
379	if (rv)
380		printk(KERN_INFO "gdlm_plock_init: misc_register failed %d",
381		       rv);
382	return rv;
383}
384
385void gdlm_plock_exit(void)
386{
387	if (misc_deregister(&plock_dev_misc) < 0)
388		printk(KERN_INFO "gdlm_plock_exit: misc_deregister failed");
389}
390