xref: /linux/fs/dlm/user.c (revision 16e5c1fc)
1597d0caeSDavid Teigland /*
27fe2b319SDavid Teigland  * Copyright (C) 2006-2010 Red Hat, Inc.  All rights reserved.
3597d0caeSDavid Teigland  *
4597d0caeSDavid Teigland  * This copyrighted material is made available to anyone wishing to use,
5597d0caeSDavid Teigland  * modify, copy, or redistribute it subject to the terms and conditions
6597d0caeSDavid Teigland  * of the GNU General Public License v.2.
7597d0caeSDavid Teigland  */
8597d0caeSDavid Teigland 
9597d0caeSDavid Teigland #include <linux/miscdevice.h>
10597d0caeSDavid Teigland #include <linux/init.h>
11597d0caeSDavid Teigland #include <linux/wait.h>
12597d0caeSDavid Teigland #include <linux/module.h>
13597d0caeSDavid Teigland #include <linux/file.h>
14597d0caeSDavid Teigland #include <linux/fs.h>
15597d0caeSDavid Teigland #include <linux/poll.h>
16597d0caeSDavid Teigland #include <linux/signal.h>
17597d0caeSDavid Teigland #include <linux/spinlock.h>
18597d0caeSDavid Teigland #include <linux/dlm.h>
19597d0caeSDavid Teigland #include <linux/dlm_device.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
21597d0caeSDavid Teigland 
22597d0caeSDavid Teigland #include "dlm_internal.h"
23597d0caeSDavid Teigland #include "lockspace.h"
24597d0caeSDavid Teigland #include "lock.h"
25597d0caeSDavid Teigland #include "lvb_table.h"
2684c6e8cdSAdrian Bunk #include "user.h"
278304d6f2SDavid Teigland #include "ast.h"
28597d0caeSDavid Teigland 
290fe410d3SDenis Cheng static const char name_prefix[] = "dlm";
3000977a59SArjan van de Ven static const struct file_operations device_fops;
31dc68c7edSDavid Teigland static atomic_t dlm_monitor_opened;
32dc68c7edSDavid Teigland static int dlm_monitor_unused = 1;
33597d0caeSDavid Teigland 
34597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
35597d0caeSDavid Teigland 
36597d0caeSDavid Teigland struct dlm_lock_params32 {
37597d0caeSDavid Teigland 	__u8 mode;
38597d0caeSDavid Teigland 	__u8 namelen;
39d7db923eSDavid Teigland 	__u16 unused;
40d7db923eSDavid Teigland 	__u32 flags;
41597d0caeSDavid Teigland 	__u32 lkid;
42597d0caeSDavid Teigland 	__u32 parent;
43d7db923eSDavid Teigland 	__u64 xid;
44d7db923eSDavid Teigland 	__u64 timeout;
45597d0caeSDavid Teigland 	__u32 castparam;
46597d0caeSDavid Teigland 	__u32 castaddr;
47597d0caeSDavid Teigland 	__u32 bastparam;
48597d0caeSDavid Teigland 	__u32 bastaddr;
49597d0caeSDavid Teigland 	__u32 lksb;
50597d0caeSDavid Teigland 	char lvb[DLM_USER_LVB_LEN];
51597d0caeSDavid Teigland 	char name[0];
52597d0caeSDavid Teigland };
53597d0caeSDavid Teigland 
54597d0caeSDavid Teigland struct dlm_write_request32 {
55597d0caeSDavid Teigland 	__u32 version[3];
56597d0caeSDavid Teigland 	__u8 cmd;
57597d0caeSDavid Teigland 	__u8 is64bit;
58597d0caeSDavid Teigland 	__u8 unused[2];
59597d0caeSDavid Teigland 
60597d0caeSDavid Teigland 	union  {
61597d0caeSDavid Teigland 		struct dlm_lock_params32 lock;
62597d0caeSDavid Teigland 		struct dlm_lspace_params lspace;
6372c2be77SDavid Teigland 		struct dlm_purge_params purge;
64597d0caeSDavid Teigland 	} i;
65597d0caeSDavid Teigland };
66597d0caeSDavid Teigland 
67597d0caeSDavid Teigland struct dlm_lksb32 {
68597d0caeSDavid Teigland 	__u32 sb_status;
69597d0caeSDavid Teigland 	__u32 sb_lkid;
70597d0caeSDavid Teigland 	__u8 sb_flags;
71597d0caeSDavid Teigland 	__u32 sb_lvbptr;
72597d0caeSDavid Teigland };
73597d0caeSDavid Teigland 
74597d0caeSDavid Teigland struct dlm_lock_result32 {
75d7db923eSDavid Teigland 	__u32 version[3];
76597d0caeSDavid Teigland 	__u32 length;
77597d0caeSDavid Teigland 	__u32 user_astaddr;
78597d0caeSDavid Teigland 	__u32 user_astparam;
79597d0caeSDavid Teigland 	__u32 user_lksb;
80597d0caeSDavid Teigland 	struct dlm_lksb32 lksb;
81597d0caeSDavid Teigland 	__u8 bast_mode;
82597d0caeSDavid Teigland 	__u8 unused[3];
83597d0caeSDavid Teigland 	/* Offsets may be zero if no data is present */
84597d0caeSDavid Teigland 	__u32 lvb_offset;
85597d0caeSDavid Teigland };
86597d0caeSDavid Teigland 
87597d0caeSDavid Teigland static void compat_input(struct dlm_write_request *kb,
882a79289eSPatrick Caulfeld 			 struct dlm_write_request32 *kb32,
891fecb1c4SDavid Teigland 			 int namelen)
90597d0caeSDavid Teigland {
91597d0caeSDavid Teigland 	kb->version[0] = kb32->version[0];
92597d0caeSDavid Teigland 	kb->version[1] = kb32->version[1];
93597d0caeSDavid Teigland 	kb->version[2] = kb32->version[2];
94597d0caeSDavid Teigland 
95597d0caeSDavid Teigland 	kb->cmd = kb32->cmd;
96597d0caeSDavid Teigland 	kb->is64bit = kb32->is64bit;
97597d0caeSDavid Teigland 	if (kb->cmd == DLM_USER_CREATE_LOCKSPACE ||
98597d0caeSDavid Teigland 	    kb->cmd == DLM_USER_REMOVE_LOCKSPACE) {
99597d0caeSDavid Teigland 		kb->i.lspace.flags = kb32->i.lspace.flags;
100597d0caeSDavid Teigland 		kb->i.lspace.minor = kb32->i.lspace.minor;
1011fecb1c4SDavid Teigland 		memcpy(kb->i.lspace.name, kb32->i.lspace.name, namelen);
10272c2be77SDavid Teigland 	} else if (kb->cmd == DLM_USER_PURGE) {
10372c2be77SDavid Teigland 		kb->i.purge.nodeid = kb32->i.purge.nodeid;
10472c2be77SDavid Teigland 		kb->i.purge.pid = kb32->i.purge.pid;
105597d0caeSDavid Teigland 	} else {
106597d0caeSDavid Teigland 		kb->i.lock.mode = kb32->i.lock.mode;
107597d0caeSDavid Teigland 		kb->i.lock.namelen = kb32->i.lock.namelen;
108597d0caeSDavid Teigland 		kb->i.lock.flags = kb32->i.lock.flags;
109597d0caeSDavid Teigland 		kb->i.lock.lkid = kb32->i.lock.lkid;
110597d0caeSDavid Teigland 		kb->i.lock.parent = kb32->i.lock.parent;
111d7db923eSDavid Teigland 		kb->i.lock.xid = kb32->i.lock.xid;
112d7db923eSDavid Teigland 		kb->i.lock.timeout = kb32->i.lock.timeout;
113597d0caeSDavid Teigland 		kb->i.lock.castparam = (void *)(long)kb32->i.lock.castparam;
114597d0caeSDavid Teigland 		kb->i.lock.castaddr = (void *)(long)kb32->i.lock.castaddr;
115597d0caeSDavid Teigland 		kb->i.lock.bastparam = (void *)(long)kb32->i.lock.bastparam;
116597d0caeSDavid Teigland 		kb->i.lock.bastaddr = (void *)(long)kb32->i.lock.bastaddr;
117597d0caeSDavid Teigland 		kb->i.lock.lksb = (void *)(long)kb32->i.lock.lksb;
118597d0caeSDavid Teigland 		memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN);
1191fecb1c4SDavid Teigland 		memcpy(kb->i.lock.name, kb32->i.lock.name, namelen);
120597d0caeSDavid Teigland 	}
121597d0caeSDavid Teigland }
122597d0caeSDavid Teigland 
123597d0caeSDavid Teigland static void compat_output(struct dlm_lock_result *res,
124597d0caeSDavid Teigland 			  struct dlm_lock_result32 *res32)
125597d0caeSDavid Teigland {
126d7db923eSDavid Teigland 	res32->version[0] = res->version[0];
127d7db923eSDavid Teigland 	res32->version[1] = res->version[1];
128d7db923eSDavid Teigland 	res32->version[2] = res->version[2];
129d7db923eSDavid Teigland 
130597d0caeSDavid Teigland 	res32->user_astaddr = (__u32)(long)res->user_astaddr;
131597d0caeSDavid Teigland 	res32->user_astparam = (__u32)(long)res->user_astparam;
132597d0caeSDavid Teigland 	res32->user_lksb = (__u32)(long)res->user_lksb;
133597d0caeSDavid Teigland 	res32->bast_mode = res->bast_mode;
134597d0caeSDavid Teigland 
135597d0caeSDavid Teigland 	res32->lvb_offset = res->lvb_offset;
136597d0caeSDavid Teigland 	res32->length = res->length;
137597d0caeSDavid Teigland 
138597d0caeSDavid Teigland 	res32->lksb.sb_status = res->lksb.sb_status;
139597d0caeSDavid Teigland 	res32->lksb.sb_flags = res->lksb.sb_flags;
140597d0caeSDavid Teigland 	res32->lksb.sb_lkid = res->lksb.sb_lkid;
141597d0caeSDavid Teigland 	res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr;
142597d0caeSDavid Teigland }
143597d0caeSDavid Teigland #endif
144597d0caeSDavid Teigland 
14584d8cd69SDavid Teigland /* Figure out if this lock is at the end of its life and no longer
14684d8cd69SDavid Teigland    available for the application to use.  The lkb still exists until
14784d8cd69SDavid Teigland    the final ast is read.  A lock becomes EOL in three situations:
14884d8cd69SDavid Teigland      1. a noqueue request fails with EAGAIN
14984d8cd69SDavid Teigland      2. an unlock completes with EUNLOCK
15084d8cd69SDavid Teigland      3. a cancel of a waiting request completes with ECANCEL/EDEADLK
15184d8cd69SDavid Teigland    An EOL lock needs to be removed from the process's list of locks.
15284d8cd69SDavid Teigland    And we can't allow any new operation on an EOL lock.  This is
15384d8cd69SDavid Teigland    not related to the lifetime of the lkb struct which is managed
15484d8cd69SDavid Teigland    entirely by refcount. */
15584d8cd69SDavid Teigland 
1568304d6f2SDavid Teigland static int lkb_is_endoflife(int mode, int status)
15784d8cd69SDavid Teigland {
1588304d6f2SDavid Teigland 	switch (status) {
15984d8cd69SDavid Teigland 	case -DLM_EUNLOCK:
16084d8cd69SDavid Teigland 		return 1;
16184d8cd69SDavid Teigland 	case -DLM_ECANCEL:
16284d8cd69SDavid Teigland 	case -ETIMEDOUT:
1638b4021faSDavid Teigland 	case -EDEADLK:
16484d8cd69SDavid Teigland 	case -EAGAIN:
1658304d6f2SDavid Teigland 		if (mode == DLM_LOCK_IV)
16684d8cd69SDavid Teigland 			return 1;
16784d8cd69SDavid Teigland 		break;
16884d8cd69SDavid Teigland 	}
16984d8cd69SDavid Teigland 	return 0;
17084d8cd69SDavid Teigland }
17184d8cd69SDavid Teigland 
172ef0c2bb0SDavid Teigland /* we could possibly check if the cancel of an orphan has resulted in the lkb
173ef0c2bb0SDavid Teigland    being removed and then remove that lkb from the orphans list and free it */
174597d0caeSDavid Teigland 
1758304d6f2SDavid Teigland void dlm_user_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode,
1768304d6f2SDavid Teigland 		      int status, uint32_t sbflags, uint64_t seq)
177597d0caeSDavid Teigland {
178597d0caeSDavid Teigland 	struct dlm_ls *ls;
179597d0caeSDavid Teigland 	struct dlm_user_args *ua;
180597d0caeSDavid Teigland 	struct dlm_user_proc *proc;
1818304d6f2SDavid Teigland 	int rv;
182597d0caeSDavid Teigland 
183ef0c2bb0SDavid Teigland 	if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD))
184597d0caeSDavid Teigland 		return;
185597d0caeSDavid Teigland 
186597d0caeSDavid Teigland 	ls = lkb->lkb_resource->res_ls;
187597d0caeSDavid Teigland 	mutex_lock(&ls->ls_clear_proc_locks);
188597d0caeSDavid Teigland 
189597d0caeSDavid Teigland 	/* If ORPHAN/DEAD flag is set, it means the process is dead so an ast
190597d0caeSDavid Teigland 	   can't be delivered.  For ORPHAN's, dlm_clear_proc_locks() freed
191ef0c2bb0SDavid Teigland 	   lkb->ua so we can't try to use it.  This second check is necessary
192ef0c2bb0SDavid Teigland 	   for cases where a completion ast is received for an operation that
193ef0c2bb0SDavid Teigland 	   began before clear_proc_locks did its cancel/unlock. */
194597d0caeSDavid Teigland 
195ef0c2bb0SDavid Teigland 	if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD))
196597d0caeSDavid Teigland 		goto out;
197597d0caeSDavid Teigland 
198d292c0ccSDavid Teigland 	DLM_ASSERT(lkb->lkb_ua, dlm_print_lkb(lkb););
199d292c0ccSDavid Teigland 	ua = lkb->lkb_ua;
200597d0caeSDavid Teigland 	proc = ua->proc;
201597d0caeSDavid Teigland 
2028304d6f2SDavid Teigland 	if ((flags & DLM_CB_BAST) && ua->bastaddr == NULL)
203597d0caeSDavid Teigland 		goto out;
204597d0caeSDavid Teigland 
2058304d6f2SDavid Teigland 	if ((flags & DLM_CB_CAST) && lkb_is_endoflife(mode, status))
2068304d6f2SDavid Teigland 		lkb->lkb_flags |= DLM_IFL_ENDOFLIFE;
2078304d6f2SDavid Teigland 
208597d0caeSDavid Teigland 	spin_lock(&proc->asts_spin);
209ef0c2bb0SDavid Teigland 
2108304d6f2SDavid Teigland 	rv = dlm_add_lkb_callback(lkb, flags, mode, status, sbflags, seq);
2118304d6f2SDavid Teigland 	if (rv < 0) {
2128304d6f2SDavid Teigland 		spin_unlock(&proc->asts_spin);
2138304d6f2SDavid Teigland 		goto out;
2148304d6f2SDavid Teigland 	}
215ef0c2bb0SDavid Teigland 
21623e8e1aaSDavid Teigland 	if (list_empty(&lkb->lkb_cb_list)) {
217597d0caeSDavid Teigland 		kref_get(&lkb->lkb_ref);
21823e8e1aaSDavid Teigland 		list_add_tail(&lkb->lkb_cb_list, &proc->asts);
219597d0caeSDavid Teigland 		wake_up_interruptible(&proc->wait);
220597d0caeSDavid Teigland 	}
221597d0caeSDavid Teigland 	spin_unlock(&proc->asts_spin);
22234e22bedSDavid Teigland 
2238304d6f2SDavid Teigland 	if (lkb->lkb_flags & DLM_IFL_ENDOFLIFE) {
2248304d6f2SDavid Teigland 		/* N.B. spin_lock locks_spin, not asts_spin */
225ce5246b9SDavid Teigland 		spin_lock(&proc->locks_spin);
226ef0c2bb0SDavid Teigland 		if (!list_empty(&lkb->lkb_ownqueue)) {
22734e22bedSDavid Teigland 			list_del_init(&lkb->lkb_ownqueue);
22834e22bedSDavid Teigland 			dlm_put_lkb(lkb);
22934e22bedSDavid Teigland 		}
230ce5246b9SDavid Teigland 		spin_unlock(&proc->locks_spin);
231ef0c2bb0SDavid Teigland 	}
232597d0caeSDavid Teigland  out:
233597d0caeSDavid Teigland 	mutex_unlock(&ls->ls_clear_proc_locks);
234597d0caeSDavid Teigland }
235597d0caeSDavid Teigland 
236597d0caeSDavid Teigland static int device_user_lock(struct dlm_user_proc *proc,
237597d0caeSDavid Teigland 			    struct dlm_lock_params *params)
238597d0caeSDavid Teigland {
239597d0caeSDavid Teigland 	struct dlm_ls *ls;
240597d0caeSDavid Teigland 	struct dlm_user_args *ua;
2412ab4bd8eSDavid Teigland 	uint32_t lkid;
242597d0caeSDavid Teigland 	int error = -ENOMEM;
243597d0caeSDavid Teigland 
244597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
245597d0caeSDavid Teigland 	if (!ls)
246597d0caeSDavid Teigland 		return -ENOENT;
247597d0caeSDavid Teigland 
248597d0caeSDavid Teigland 	if (!params->castaddr || !params->lksb) {
249597d0caeSDavid Teigland 		error = -EINVAL;
250597d0caeSDavid Teigland 		goto out;
251597d0caeSDavid Teigland 	}
252597d0caeSDavid Teigland 
253573c24c4SDavid Teigland 	ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS);
254597d0caeSDavid Teigland 	if (!ua)
255597d0caeSDavid Teigland 		goto out;
256597d0caeSDavid Teigland 	ua->proc = proc;
257597d0caeSDavid Teigland 	ua->user_lksb = params->lksb;
258597d0caeSDavid Teigland 	ua->castparam = params->castparam;
259597d0caeSDavid Teigland 	ua->castaddr = params->castaddr;
260597d0caeSDavid Teigland 	ua->bastparam = params->bastparam;
261597d0caeSDavid Teigland 	ua->bastaddr = params->bastaddr;
262d7db923eSDavid Teigland 	ua->xid = params->xid;
263597d0caeSDavid Teigland 
2642ab4bd8eSDavid Teigland 	if (params->flags & DLM_LKF_CONVERT) {
265597d0caeSDavid Teigland 		error = dlm_user_convert(ls, ua,
266597d0caeSDavid Teigland 				         params->mode, params->flags,
267d7db923eSDavid Teigland 				         params->lkid, params->lvb,
268d7db923eSDavid Teigland 					 (unsigned long) params->timeout);
2692ab4bd8eSDavid Teigland 	} else if (params->flags & DLM_LKF_ORPHAN) {
2702ab4bd8eSDavid Teigland 		error = dlm_user_adopt_orphan(ls, ua,
2712ab4bd8eSDavid Teigland 					 params->mode, params->flags,
2722ab4bd8eSDavid Teigland 					 params->name, params->namelen,
2732ab4bd8eSDavid Teigland 					 (unsigned long) params->timeout,
2742ab4bd8eSDavid Teigland 					 &lkid);
2752ab4bd8eSDavid Teigland 		if (!error)
2762ab4bd8eSDavid Teigland 			error = lkid;
2772ab4bd8eSDavid Teigland 	} else {
278597d0caeSDavid Teigland 		error = dlm_user_request(ls, ua,
279597d0caeSDavid Teigland 					 params->mode, params->flags,
280597d0caeSDavid Teigland 					 params->name, params->namelen,
281d7db923eSDavid Teigland 					 (unsigned long) params->timeout);
282597d0caeSDavid Teigland 		if (!error)
283597d0caeSDavid Teigland 			error = ua->lksb.sb_lkid;
284597d0caeSDavid Teigland 	}
285597d0caeSDavid Teigland  out:
286597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
287597d0caeSDavid Teigland 	return error;
288597d0caeSDavid Teigland }
289597d0caeSDavid Teigland 
290597d0caeSDavid Teigland static int device_user_unlock(struct dlm_user_proc *proc,
291597d0caeSDavid Teigland 			      struct dlm_lock_params *params)
292597d0caeSDavid Teigland {
293597d0caeSDavid Teigland 	struct dlm_ls *ls;
294597d0caeSDavid Teigland 	struct dlm_user_args *ua;
295597d0caeSDavid Teigland 	int error = -ENOMEM;
296597d0caeSDavid Teigland 
297597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
298597d0caeSDavid Teigland 	if (!ls)
299597d0caeSDavid Teigland 		return -ENOENT;
300597d0caeSDavid Teigland 
301573c24c4SDavid Teigland 	ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS);
302597d0caeSDavid Teigland 	if (!ua)
303597d0caeSDavid Teigland 		goto out;
304597d0caeSDavid Teigland 	ua->proc = proc;
305597d0caeSDavid Teigland 	ua->user_lksb = params->lksb;
306597d0caeSDavid Teigland 	ua->castparam = params->castparam;
307597d0caeSDavid Teigland 	ua->castaddr = params->castaddr;
308597d0caeSDavid Teigland 
309597d0caeSDavid Teigland 	if (params->flags & DLM_LKF_CANCEL)
310597d0caeSDavid Teigland 		error = dlm_user_cancel(ls, ua, params->flags, params->lkid);
311597d0caeSDavid Teigland 	else
312597d0caeSDavid Teigland 		error = dlm_user_unlock(ls, ua, params->flags, params->lkid,
313597d0caeSDavid Teigland 					params->lvb);
314597d0caeSDavid Teigland  out:
315597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
316597d0caeSDavid Teigland 	return error;
317597d0caeSDavid Teigland }
318597d0caeSDavid Teigland 
3198b4021faSDavid Teigland static int device_user_deadlock(struct dlm_user_proc *proc,
3208b4021faSDavid Teigland 				struct dlm_lock_params *params)
3218b4021faSDavid Teigland {
3228b4021faSDavid Teigland 	struct dlm_ls *ls;
3238b4021faSDavid Teigland 	int error;
3248b4021faSDavid Teigland 
3258b4021faSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
3268b4021faSDavid Teigland 	if (!ls)
3278b4021faSDavid Teigland 		return -ENOENT;
3288b4021faSDavid Teigland 
3298b4021faSDavid Teigland 	error = dlm_user_deadlock(ls, params->flags, params->lkid);
3308b4021faSDavid Teigland 
3318b4021faSDavid Teigland 	dlm_put_lockspace(ls);
3328b4021faSDavid Teigland 	return error;
3338b4021faSDavid Teigland }
3348b4021faSDavid Teigland 
3350f8e0d9aSDavid Teigland static int dlm_device_register(struct dlm_ls *ls, char *name)
336254da030SPatrick Caulfield {
337254da030SPatrick Caulfield 	int error, len;
338254da030SPatrick Caulfield 
3390f8e0d9aSDavid Teigland 	/* The device is already registered.  This happens when the
3400f8e0d9aSDavid Teigland 	   lockspace is created multiple times from userspace. */
3410f8e0d9aSDavid Teigland 	if (ls->ls_device.name)
3420f8e0d9aSDavid Teigland 		return 0;
3430f8e0d9aSDavid Teigland 
344254da030SPatrick Caulfield 	error = -ENOMEM;
345254da030SPatrick Caulfield 	len = strlen(name) + strlen(name_prefix) + 2;
346573c24c4SDavid Teigland 	ls->ls_device.name = kzalloc(len, GFP_NOFS);
347254da030SPatrick Caulfield 	if (!ls->ls_device.name)
348254da030SPatrick Caulfield 		goto fail;
349254da030SPatrick Caulfield 
350254da030SPatrick Caulfield 	snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix,
351254da030SPatrick Caulfield 		 name);
352254da030SPatrick Caulfield 	ls->ls_device.fops = &device_fops;
353254da030SPatrick Caulfield 	ls->ls_device.minor = MISC_DYNAMIC_MINOR;
354254da030SPatrick Caulfield 
355254da030SPatrick Caulfield 	error = misc_register(&ls->ls_device);
356254da030SPatrick Caulfield 	if (error) {
357254da030SPatrick Caulfield 		kfree(ls->ls_device.name);
358254da030SPatrick Caulfield 	}
359254da030SPatrick Caulfield fail:
360254da030SPatrick Caulfield 	return error;
361254da030SPatrick Caulfield }
362254da030SPatrick Caulfield 
3630f8e0d9aSDavid Teigland int dlm_device_deregister(struct dlm_ls *ls)
3640f8e0d9aSDavid Teigland {
3650f8e0d9aSDavid Teigland 	/* The device is not registered.  This happens when the lockspace
3660f8e0d9aSDavid Teigland 	   was never used from userspace, or when device_create_lockspace()
3670f8e0d9aSDavid Teigland 	   calls dlm_release_lockspace() after the register fails. */
3680f8e0d9aSDavid Teigland 	if (!ls->ls_device.name)
3690f8e0d9aSDavid Teigland 		return 0;
3700f8e0d9aSDavid Teigland 
371f368ed60SGreg Kroah-Hartman 	misc_deregister(&ls->ls_device);
3720f8e0d9aSDavid Teigland 	kfree(ls->ls_device.name);
373f368ed60SGreg Kroah-Hartman 	return 0;
3740f8e0d9aSDavid Teigland }
3750f8e0d9aSDavid Teigland 
37672c2be77SDavid Teigland static int device_user_purge(struct dlm_user_proc *proc,
37772c2be77SDavid Teigland 			     struct dlm_purge_params *params)
37872c2be77SDavid Teigland {
37972c2be77SDavid Teigland 	struct dlm_ls *ls;
38072c2be77SDavid Teigland 	int error;
38172c2be77SDavid Teigland 
38272c2be77SDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
38372c2be77SDavid Teigland 	if (!ls)
38472c2be77SDavid Teigland 		return -ENOENT;
38572c2be77SDavid Teigland 
38672c2be77SDavid Teigland 	error = dlm_user_purge(ls, proc, params->nodeid, params->pid);
38772c2be77SDavid Teigland 
38872c2be77SDavid Teigland 	dlm_put_lockspace(ls);
38972c2be77SDavid Teigland 	return error;
39072c2be77SDavid Teigland }
39172c2be77SDavid Teigland 
392597d0caeSDavid Teigland static int device_create_lockspace(struct dlm_lspace_params *params)
393597d0caeSDavid Teigland {
394597d0caeSDavid Teigland 	dlm_lockspace_t *lockspace;
395597d0caeSDavid Teigland 	struct dlm_ls *ls;
396254da030SPatrick Caulfield 	int error;
397597d0caeSDavid Teigland 
398597d0caeSDavid Teigland 	if (!capable(CAP_SYS_ADMIN))
399597d0caeSDavid Teigland 		return -EPERM;
400597d0caeSDavid Teigland 
40160f98d18SDavid Teigland 	error = dlm_new_lockspace(params->name, NULL, params->flags,
40260f98d18SDavid Teigland 				  DLM_USER_LVB_LEN, NULL, NULL, NULL,
40360f98d18SDavid Teigland 				  &lockspace);
404597d0caeSDavid Teigland 	if (error)
405597d0caeSDavid Teigland 		return error;
406597d0caeSDavid Teigland 
407597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(lockspace);
408597d0caeSDavid Teigland 	if (!ls)
409597d0caeSDavid Teigland 		return -ENOENT;
410597d0caeSDavid Teigland 
4110f8e0d9aSDavid Teigland 	error = dlm_device_register(ls, params->name);
412597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
413597d0caeSDavid Teigland 
414254da030SPatrick Caulfield 	if (error)
415597d0caeSDavid Teigland 		dlm_release_lockspace(lockspace, 0);
416254da030SPatrick Caulfield 	else
417254da030SPatrick Caulfield 		error = ls->ls_device.minor;
418254da030SPatrick Caulfield 
419597d0caeSDavid Teigland 	return error;
420597d0caeSDavid Teigland }
421597d0caeSDavid Teigland 
422597d0caeSDavid Teigland static int device_remove_lockspace(struct dlm_lspace_params *params)
423597d0caeSDavid Teigland {
424597d0caeSDavid Teigland 	dlm_lockspace_t *lockspace;
425597d0caeSDavid Teigland 	struct dlm_ls *ls;
426c6e6f0baSDavid Teigland 	int error, force = 0;
427597d0caeSDavid Teigland 
428597d0caeSDavid Teigland 	if (!capable(CAP_SYS_ADMIN))
429597d0caeSDavid Teigland 		return -EPERM;
430597d0caeSDavid Teigland 
431597d0caeSDavid Teigland 	ls = dlm_find_lockspace_device(params->minor);
432597d0caeSDavid Teigland 	if (!ls)
433597d0caeSDavid Teigland 		return -ENOENT;
434597d0caeSDavid Teigland 
435c6e6f0baSDavid Teigland 	if (params->flags & DLM_USER_LSFLG_FORCEFREE)
436c6e6f0baSDavid Teigland 		force = 2;
437c6e6f0baSDavid Teigland 
438597d0caeSDavid Teigland 	lockspace = ls->ls_local_handle;
439597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
4400f8e0d9aSDavid Teigland 
4410f8e0d9aSDavid Teigland 	/* The final dlm_release_lockspace waits for references to go to
4420f8e0d9aSDavid Teigland 	   zero, so all processes will need to close their device for the
4430f8e0d9aSDavid Teigland 	   ls before the release will proceed.  release also calls the
4440f8e0d9aSDavid Teigland 	   device_deregister above.  Converting a positive return value
4450f8e0d9aSDavid Teigland 	   from release to zero means that userspace won't know when its
4460f8e0d9aSDavid Teigland 	   release was the final one, but it shouldn't need to know. */
4470f8e0d9aSDavid Teigland 
448c6e6f0baSDavid Teigland 	error = dlm_release_lockspace(lockspace, force);
4490f8e0d9aSDavid Teigland 	if (error > 0)
4500f8e0d9aSDavid Teigland 		error = 0;
451597d0caeSDavid Teigland 	return error;
452597d0caeSDavid Teigland }
453597d0caeSDavid Teigland 
454597d0caeSDavid Teigland /* Check the user's version matches ours */
455597d0caeSDavid Teigland static int check_version(struct dlm_write_request *req)
456597d0caeSDavid Teigland {
457597d0caeSDavid Teigland 	if (req->version[0] != DLM_DEVICE_VERSION_MAJOR ||
458597d0caeSDavid Teigland 	    (req->version[0] == DLM_DEVICE_VERSION_MAJOR &&
459597d0caeSDavid Teigland 	     req->version[1] > DLM_DEVICE_VERSION_MINOR)) {
460597d0caeSDavid Teigland 
461597d0caeSDavid Teigland 		printk(KERN_DEBUG "dlm: process %s (%d) version mismatch "
462597d0caeSDavid Teigland 		       "user (%d.%d.%d) kernel (%d.%d.%d)\n",
463597d0caeSDavid Teigland 		       current->comm,
464ba25f9dcSPavel Emelyanov 		       task_pid_nr(current),
465597d0caeSDavid Teigland 		       req->version[0],
466597d0caeSDavid Teigland 		       req->version[1],
467597d0caeSDavid Teigland 		       req->version[2],
468597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_MAJOR,
469597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_MINOR,
470597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_PATCH);
471597d0caeSDavid Teigland 		return -EINVAL;
472597d0caeSDavid Teigland 	}
473597d0caeSDavid Teigland 	return 0;
474597d0caeSDavid Teigland }
475597d0caeSDavid Teigland 
476597d0caeSDavid Teigland /*
477597d0caeSDavid Teigland  * device_write
478597d0caeSDavid Teigland  *
479597d0caeSDavid Teigland  *   device_user_lock
480597d0caeSDavid Teigland  *     dlm_user_request -> request_lock
481597d0caeSDavid Teigland  *     dlm_user_convert -> convert_lock
482597d0caeSDavid Teigland  *
483597d0caeSDavid Teigland  *   device_user_unlock
484597d0caeSDavid Teigland  *     dlm_user_unlock -> unlock_lock
485597d0caeSDavid Teigland  *     dlm_user_cancel -> cancel_lock
486597d0caeSDavid Teigland  *
487597d0caeSDavid Teigland  *   device_create_lockspace
488597d0caeSDavid Teigland  *     dlm_new_lockspace
489597d0caeSDavid Teigland  *
490597d0caeSDavid Teigland  *   device_remove_lockspace
491597d0caeSDavid Teigland  *     dlm_release_lockspace
492597d0caeSDavid Teigland  */
493597d0caeSDavid Teigland 
494597d0caeSDavid Teigland /* a write to a lockspace device is a lock or unlock request, a write
495597d0caeSDavid Teigland    to the control device is to create/remove a lockspace */
496597d0caeSDavid Teigland 
497597d0caeSDavid Teigland static ssize_t device_write(struct file *file, const char __user *buf,
498597d0caeSDavid Teigland 			    size_t count, loff_t *ppos)
499597d0caeSDavid Teigland {
500597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
501597d0caeSDavid Teigland 	struct dlm_write_request *kbuf;
502597d0caeSDavid Teigland 	int error;
503597d0caeSDavid Teigland 
504597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
505597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_write_request32))
506597d0caeSDavid Teigland #else
507597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_write_request))
508597d0caeSDavid Teigland #endif
509597d0caeSDavid Teigland 		return -EINVAL;
510597d0caeSDavid Teigland 
511d4b0bcf3SDavid Teigland 	/*
512d4b0bcf3SDavid Teigland 	 * can't compare against COMPAT/dlm_write_request32 because
513d4b0bcf3SDavid Teigland 	 * we don't yet know if is64bit is zero
514d4b0bcf3SDavid Teigland 	 */
5152b75bc91SSasha Levin 	if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN)
5162b75bc91SSasha Levin 		return -EINVAL;
5172b75bc91SSasha Levin 
518*16e5c1fcSAl Viro 	kbuf = memdup_user_nul(buf, count);
519*16e5c1fcSAl Viro 	if (!IS_ERR(kbuf))
520*16e5c1fcSAl Viro 		return PTR_ERR(kbuf);
521597d0caeSDavid Teigland 
522597d0caeSDavid Teigland 	if (check_version(kbuf)) {
523597d0caeSDavid Teigland 		error = -EBADE;
524597d0caeSDavid Teigland 		goto out_free;
525597d0caeSDavid Teigland 	}
526597d0caeSDavid Teigland 
527597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
528597d0caeSDavid Teigland 	if (!kbuf->is64bit) {
529597d0caeSDavid Teigland 		struct dlm_write_request32 *k32buf;
5301fecb1c4SDavid Teigland 		int namelen = 0;
5311fecb1c4SDavid Teigland 
5321fecb1c4SDavid Teigland 		if (count > sizeof(struct dlm_write_request32))
5331fecb1c4SDavid Teigland 			namelen = count - sizeof(struct dlm_write_request32);
5341fecb1c4SDavid Teigland 
535597d0caeSDavid Teigland 		k32buf = (struct dlm_write_request32 *)kbuf;
5361fecb1c4SDavid Teigland 
5371fecb1c4SDavid Teigland 		/* add 1 after namelen so that the name string is terminated */
5381fecb1c4SDavid Teigland 		kbuf = kzalloc(sizeof(struct dlm_write_request) + namelen + 1,
539573c24c4SDavid Teigland 			       GFP_NOFS);
540cb980d9aSDavid Teigland 		if (!kbuf) {
541cb980d9aSDavid Teigland 			kfree(k32buf);
542597d0caeSDavid Teigland 			return -ENOMEM;
543cb980d9aSDavid Teigland 		}
544597d0caeSDavid Teigland 
545597d0caeSDavid Teigland 		if (proc)
546597d0caeSDavid Teigland 			set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags);
5471fecb1c4SDavid Teigland 
5481fecb1c4SDavid Teigland 		compat_input(kbuf, k32buf, namelen);
549597d0caeSDavid Teigland 		kfree(k32buf);
550597d0caeSDavid Teigland 	}
551597d0caeSDavid Teigland #endif
552597d0caeSDavid Teigland 
553597d0caeSDavid Teigland 	/* do we really need this? can a write happen after a close? */
554597d0caeSDavid Teigland 	if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) &&
555cb980d9aSDavid Teigland 	    (proc && test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))) {
556cb980d9aSDavid Teigland 		error = -EINVAL;
557cb980d9aSDavid Teigland 		goto out_free;
558cb980d9aSDavid Teigland 	}
559597d0caeSDavid Teigland 
560597d0caeSDavid Teigland 	error = -EINVAL;
561597d0caeSDavid Teigland 
562597d0caeSDavid Teigland 	switch (kbuf->cmd)
563597d0caeSDavid Teigland 	{
564597d0caeSDavid Teigland 	case DLM_USER_LOCK:
565597d0caeSDavid Teigland 		if (!proc) {
566597d0caeSDavid Teigland 			log_print("no locking on control device");
567c6ca7bc9SDavid Teigland 			goto out_free;
568597d0caeSDavid Teigland 		}
569597d0caeSDavid Teigland 		error = device_user_lock(proc, &kbuf->i.lock);
570597d0caeSDavid Teigland 		break;
571597d0caeSDavid Teigland 
572597d0caeSDavid Teigland 	case DLM_USER_UNLOCK:
573597d0caeSDavid Teigland 		if (!proc) {
574597d0caeSDavid Teigland 			log_print("no locking on control device");
575c6ca7bc9SDavid Teigland 			goto out_free;
576597d0caeSDavid Teigland 		}
577597d0caeSDavid Teigland 		error = device_user_unlock(proc, &kbuf->i.lock);
578597d0caeSDavid Teigland 		break;
579597d0caeSDavid Teigland 
5808b4021faSDavid Teigland 	case DLM_USER_DEADLOCK:
5818b4021faSDavid Teigland 		if (!proc) {
5828b4021faSDavid Teigland 			log_print("no locking on control device");
583c6ca7bc9SDavid Teigland 			goto out_free;
5848b4021faSDavid Teigland 		}
5858b4021faSDavid Teigland 		error = device_user_deadlock(proc, &kbuf->i.lock);
5868b4021faSDavid Teigland 		break;
5878b4021faSDavid Teigland 
588597d0caeSDavid Teigland 	case DLM_USER_CREATE_LOCKSPACE:
589597d0caeSDavid Teigland 		if (proc) {
590597d0caeSDavid Teigland 			log_print("create/remove only on control device");
591c6ca7bc9SDavid Teigland 			goto out_free;
592597d0caeSDavid Teigland 		}
593597d0caeSDavid Teigland 		error = device_create_lockspace(&kbuf->i.lspace);
594597d0caeSDavid Teigland 		break;
595597d0caeSDavid Teigland 
596597d0caeSDavid Teigland 	case DLM_USER_REMOVE_LOCKSPACE:
597597d0caeSDavid Teigland 		if (proc) {
598597d0caeSDavid Teigland 			log_print("create/remove only on control device");
599c6ca7bc9SDavid Teigland 			goto out_free;
600597d0caeSDavid Teigland 		}
601597d0caeSDavid Teigland 		error = device_remove_lockspace(&kbuf->i.lspace);
602597d0caeSDavid Teigland 		break;
603597d0caeSDavid Teigland 
60472c2be77SDavid Teigland 	case DLM_USER_PURGE:
60572c2be77SDavid Teigland 		if (!proc) {
60672c2be77SDavid Teigland 			log_print("no locking on control device");
607c6ca7bc9SDavid Teigland 			goto out_free;
60872c2be77SDavid Teigland 		}
60972c2be77SDavid Teigland 		error = device_user_purge(proc, &kbuf->i.purge);
61072c2be77SDavid Teigland 		break;
61172c2be77SDavid Teigland 
612597d0caeSDavid Teigland 	default:
613597d0caeSDavid Teigland 		log_print("Unknown command passed to DLM device : %d\n",
614597d0caeSDavid Teigland 			  kbuf->cmd);
615597d0caeSDavid Teigland 	}
616597d0caeSDavid Teigland 
617597d0caeSDavid Teigland  out_free:
618597d0caeSDavid Teigland 	kfree(kbuf);
619597d0caeSDavid Teigland 	return error;
620597d0caeSDavid Teigland }
621597d0caeSDavid Teigland 
622597d0caeSDavid Teigland /* Every process that opens the lockspace device has its own "proc" structure
623597d0caeSDavid Teigland    hanging off the open file that's used to keep track of locks owned by the
624597d0caeSDavid Teigland    process and asts that need to be delivered to the process. */
625597d0caeSDavid Teigland 
626597d0caeSDavid Teigland static int device_open(struct inode *inode, struct file *file)
627597d0caeSDavid Teigland {
628597d0caeSDavid Teigland 	struct dlm_user_proc *proc;
629597d0caeSDavid Teigland 	struct dlm_ls *ls;
630597d0caeSDavid Teigland 
631597d0caeSDavid Teigland 	ls = dlm_find_lockspace_device(iminor(inode));
632f9f2ed48SDavid Teigland 	if (!ls)
633597d0caeSDavid Teigland 		return -ENOENT;
634597d0caeSDavid Teigland 
635573c24c4SDavid Teigland 	proc = kzalloc(sizeof(struct dlm_user_proc), GFP_NOFS);
636597d0caeSDavid Teigland 	if (!proc) {
637597d0caeSDavid Teigland 		dlm_put_lockspace(ls);
638597d0caeSDavid Teigland 		return -ENOMEM;
639597d0caeSDavid Teigland 	}
640597d0caeSDavid Teigland 
641597d0caeSDavid Teigland 	proc->lockspace = ls->ls_local_handle;
642597d0caeSDavid Teigland 	INIT_LIST_HEAD(&proc->asts);
643597d0caeSDavid Teigland 	INIT_LIST_HEAD(&proc->locks);
644a1bc86e6SDavid Teigland 	INIT_LIST_HEAD(&proc->unlocking);
645597d0caeSDavid Teigland 	spin_lock_init(&proc->asts_spin);
646597d0caeSDavid Teigland 	spin_lock_init(&proc->locks_spin);
647597d0caeSDavid Teigland 	init_waitqueue_head(&proc->wait);
648597d0caeSDavid Teigland 	file->private_data = proc;
649597d0caeSDavid Teigland 
650597d0caeSDavid Teigland 	return 0;
651597d0caeSDavid Teigland }
652597d0caeSDavid Teigland 
653597d0caeSDavid Teigland static int device_close(struct inode *inode, struct file *file)
654597d0caeSDavid Teigland {
655597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
656597d0caeSDavid Teigland 	struct dlm_ls *ls;
657597d0caeSDavid Teigland 
658597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
659597d0caeSDavid Teigland 	if (!ls)
660597d0caeSDavid Teigland 		return -ENOENT;
661597d0caeSDavid Teigland 
662597d0caeSDavid Teigland 	set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags);
663597d0caeSDavid Teigland 
664597d0caeSDavid Teigland 	dlm_clear_proc_locks(ls, proc);
665597d0caeSDavid Teigland 
666597d0caeSDavid Teigland 	/* at this point no more lkb's should exist for this lockspace,
667597d0caeSDavid Teigland 	   so there's no chance of dlm_user_add_ast() being called and
668597d0caeSDavid Teigland 	   looking for lkb->ua->proc */
669597d0caeSDavid Teigland 
670597d0caeSDavid Teigland 	kfree(proc);
671597d0caeSDavid Teigland 	file->private_data = NULL;
672597d0caeSDavid Teigland 
673597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
674597d0caeSDavid Teigland 	dlm_put_lockspace(ls);  /* for the find in device_open() */
675597d0caeSDavid Teigland 
676597d0caeSDavid Teigland 	/* FIXME: AUTOFREE: if this ls is no longer used do
677597d0caeSDavid Teigland 	   device_remove_lockspace() */
678597d0caeSDavid Teigland 
679597d0caeSDavid Teigland 	return 0;
680597d0caeSDavid Teigland }
681597d0caeSDavid Teigland 
6828304d6f2SDavid Teigland static int copy_result_to_user(struct dlm_user_args *ua, int compat,
6838304d6f2SDavid Teigland 			       uint32_t flags, int mode, int copy_lvb,
6848304d6f2SDavid Teigland 			       char __user *buf, size_t count)
685597d0caeSDavid Teigland {
686597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
687597d0caeSDavid Teigland 	struct dlm_lock_result32 result32;
688597d0caeSDavid Teigland #endif
689597d0caeSDavid Teigland 	struct dlm_lock_result result;
690597d0caeSDavid Teigland 	void *resultptr;
691597d0caeSDavid Teigland 	int error=0;
692597d0caeSDavid Teigland 	int len;
693597d0caeSDavid Teigland 	int struct_len;
694597d0caeSDavid Teigland 
695597d0caeSDavid Teigland 	memset(&result, 0, sizeof(struct dlm_lock_result));
696d7db923eSDavid Teigland 	result.version[0] = DLM_DEVICE_VERSION_MAJOR;
697d7db923eSDavid Teigland 	result.version[1] = DLM_DEVICE_VERSION_MINOR;
698d7db923eSDavid Teigland 	result.version[2] = DLM_DEVICE_VERSION_PATCH;
699597d0caeSDavid Teigland 	memcpy(&result.lksb, &ua->lksb, sizeof(struct dlm_lksb));
700597d0caeSDavid Teigland 	result.user_lksb = ua->user_lksb;
701597d0caeSDavid Teigland 
702597d0caeSDavid Teigland 	/* FIXME: dlm1 provides for the user's bastparam/addr to not be updated
703597d0caeSDavid Teigland 	   in a conversion unless the conversion is successful.  See code
704597d0caeSDavid Teigland 	   in dlm_user_convert() for updating ua from ua_tmp.  OpenVMS, though,
705597d0caeSDavid Teigland 	   notes that a new blocking AST address and parameter are set even if
706597d0caeSDavid Teigland 	   the conversion fails, so maybe we should just do that. */
707597d0caeSDavid Teigland 
7088304d6f2SDavid Teigland 	if (flags & DLM_CB_BAST) {
709597d0caeSDavid Teigland 		result.user_astaddr = ua->bastaddr;
710597d0caeSDavid Teigland 		result.user_astparam = ua->bastparam;
71189d799d0SDavid Teigland 		result.bast_mode = mode;
712597d0caeSDavid Teigland 	} else {
713597d0caeSDavid Teigland 		result.user_astaddr = ua->castaddr;
714597d0caeSDavid Teigland 		result.user_astparam = ua->castparam;
715597d0caeSDavid Teigland 	}
716597d0caeSDavid Teigland 
717597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
718597d0caeSDavid Teigland 	if (compat)
719597d0caeSDavid Teigland 		len = sizeof(struct dlm_lock_result32);
720597d0caeSDavid Teigland 	else
721597d0caeSDavid Teigland #endif
722597d0caeSDavid Teigland 		len = sizeof(struct dlm_lock_result);
723597d0caeSDavid Teigland 	struct_len = len;
724597d0caeSDavid Teigland 
725597d0caeSDavid Teigland 	/* copy lvb to userspace if there is one, it's been updated, and
726597d0caeSDavid Teigland 	   the user buffer has space for it */
727597d0caeSDavid Teigland 
7288304d6f2SDavid Teigland 	if (copy_lvb && ua->lksb.sb_lvbptr && count >= len + DLM_USER_LVB_LEN) {
729597d0caeSDavid Teigland 		if (copy_to_user(buf+len, ua->lksb.sb_lvbptr,
730597d0caeSDavid Teigland 				 DLM_USER_LVB_LEN)) {
731597d0caeSDavid Teigland 			error = -EFAULT;
732597d0caeSDavid Teigland 			goto out;
733597d0caeSDavid Teigland 		}
734597d0caeSDavid Teigland 
735597d0caeSDavid Teigland 		result.lvb_offset = len;
736597d0caeSDavid Teigland 		len += DLM_USER_LVB_LEN;
737597d0caeSDavid Teigland 	}
738597d0caeSDavid Teigland 
739597d0caeSDavid Teigland 	result.length = len;
740597d0caeSDavid Teigland 	resultptr = &result;
741597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
742597d0caeSDavid Teigland 	if (compat) {
743597d0caeSDavid Teigland 		compat_output(&result, &result32);
744597d0caeSDavid Teigland 		resultptr = &result32;
745597d0caeSDavid Teigland 	}
746597d0caeSDavid Teigland #endif
747597d0caeSDavid Teigland 
748597d0caeSDavid Teigland 	if (copy_to_user(buf, resultptr, struct_len))
749597d0caeSDavid Teigland 		error = -EFAULT;
750597d0caeSDavid Teigland 	else
751597d0caeSDavid Teigland 		error = len;
752597d0caeSDavid Teigland  out:
753597d0caeSDavid Teigland 	return error;
754597d0caeSDavid Teigland }
755597d0caeSDavid Teigland 
756d7db923eSDavid Teigland static int copy_version_to_user(char __user *buf, size_t count)
757d7db923eSDavid Teigland {
758d7db923eSDavid Teigland 	struct dlm_device_version ver;
759d7db923eSDavid Teigland 
760d7db923eSDavid Teigland 	memset(&ver, 0, sizeof(struct dlm_device_version));
761d7db923eSDavid Teigland 	ver.version[0] = DLM_DEVICE_VERSION_MAJOR;
762d7db923eSDavid Teigland 	ver.version[1] = DLM_DEVICE_VERSION_MINOR;
763d7db923eSDavid Teigland 	ver.version[2] = DLM_DEVICE_VERSION_PATCH;
764d7db923eSDavid Teigland 
765d7db923eSDavid Teigland 	if (copy_to_user(buf, &ver, sizeof(struct dlm_device_version)))
766d7db923eSDavid Teigland 		return -EFAULT;
767d7db923eSDavid Teigland 	return sizeof(struct dlm_device_version);
768d7db923eSDavid Teigland }
769d7db923eSDavid Teigland 
770597d0caeSDavid Teigland /* a read returns a single ast described in a struct dlm_lock_result */
771597d0caeSDavid Teigland 
772597d0caeSDavid Teigland static ssize_t device_read(struct file *file, char __user *buf, size_t count,
773597d0caeSDavid Teigland 			   loff_t *ppos)
774597d0caeSDavid Teigland {
775597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
776597d0caeSDavid Teigland 	struct dlm_lkb *lkb;
777597d0caeSDavid Teigland 	DECLARE_WAITQUEUE(wait, current);
7788304d6f2SDavid Teigland 	struct dlm_callback cb;
7798304d6f2SDavid Teigland 	int rv, resid, copy_lvb = 0;
780b96f4650SDavid Teigland 	int old_mode, new_mode;
781597d0caeSDavid Teigland 
782d7db923eSDavid Teigland 	if (count == sizeof(struct dlm_device_version)) {
7838304d6f2SDavid Teigland 		rv = copy_version_to_user(buf, count);
7848304d6f2SDavid Teigland 		return rv;
785d7db923eSDavid Teigland 	}
786d7db923eSDavid Teigland 
787d7db923eSDavid Teigland 	if (!proc) {
788d7db923eSDavid Teigland 		log_print("non-version read from control device %zu", count);
789d7db923eSDavid Teigland 		return -EINVAL;
790d7db923eSDavid Teigland 	}
791d7db923eSDavid Teigland 
792597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
793597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_lock_result32))
794597d0caeSDavid Teigland #else
795597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_lock_result))
796597d0caeSDavid Teigland #endif
797597d0caeSDavid Teigland 		return -EINVAL;
798597d0caeSDavid Teigland 
79989d799d0SDavid Teigland  try_another:
80089d799d0SDavid Teigland 
801597d0caeSDavid Teigland 	/* do we really need this? can a read happen after a close? */
802597d0caeSDavid Teigland 	if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
803597d0caeSDavid Teigland 		return -EINVAL;
804597d0caeSDavid Teigland 
805597d0caeSDavid Teigland 	spin_lock(&proc->asts_spin);
806597d0caeSDavid Teigland 	if (list_empty(&proc->asts)) {
807597d0caeSDavid Teigland 		if (file->f_flags & O_NONBLOCK) {
808597d0caeSDavid Teigland 			spin_unlock(&proc->asts_spin);
809597d0caeSDavid Teigland 			return -EAGAIN;
810597d0caeSDavid Teigland 		}
811597d0caeSDavid Teigland 
812597d0caeSDavid Teigland 		add_wait_queue(&proc->wait, &wait);
813597d0caeSDavid Teigland 
814597d0caeSDavid Teigland 	repeat:
815597d0caeSDavid Teigland 		set_current_state(TASK_INTERRUPTIBLE);
816597d0caeSDavid Teigland 		if (list_empty(&proc->asts) && !signal_pending(current)) {
817597d0caeSDavid Teigland 			spin_unlock(&proc->asts_spin);
818597d0caeSDavid Teigland 			schedule();
819597d0caeSDavid Teigland 			spin_lock(&proc->asts_spin);
820597d0caeSDavid Teigland 			goto repeat;
821597d0caeSDavid Teigland 		}
822597d0caeSDavid Teigland 		set_current_state(TASK_RUNNING);
823597d0caeSDavid Teigland 		remove_wait_queue(&proc->wait, &wait);
824597d0caeSDavid Teigland 
825597d0caeSDavid Teigland 		if (signal_pending(current)) {
826597d0caeSDavid Teigland 			spin_unlock(&proc->asts_spin);
827597d0caeSDavid Teigland 			return -ERESTARTSYS;
828597d0caeSDavid Teigland 		}
829597d0caeSDavid Teigland 	}
830597d0caeSDavid Teigland 
8318304d6f2SDavid Teigland 	/* if we empty lkb_callbacks, we don't want to unlock the spinlock
83223e8e1aaSDavid Teigland 	   without removing lkb_cb_list; so empty lkb_cb_list is always
8338304d6f2SDavid Teigland 	   consistent with empty lkb_callbacks */
834597d0caeSDavid Teigland 
83523e8e1aaSDavid Teigland 	lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_cb_list);
836597d0caeSDavid Teigland 
837b96f4650SDavid Teigland 	/* rem_lkb_callback sets a new lkb_last_cast */
838b96f4650SDavid Teigland 	old_mode = lkb->lkb_last_cast.mode;
839b96f4650SDavid Teigland 
8408304d6f2SDavid Teigland 	rv = dlm_rem_lkb_callback(lkb->lkb_resource->res_ls, lkb, &cb, &resid);
8418304d6f2SDavid Teigland 	if (rv < 0) {
8428304d6f2SDavid Teigland 		/* this shouldn't happen; lkb should have been removed from
8438304d6f2SDavid Teigland 		   list when resid was zero */
8448304d6f2SDavid Teigland 		log_print("dlm_rem_lkb_callback empty %x", lkb->lkb_id);
84523e8e1aaSDavid Teigland 		list_del_init(&lkb->lkb_cb_list);
8468304d6f2SDavid Teigland 		spin_unlock(&proc->asts_spin);
8478304d6f2SDavid Teigland 		/* removes ref for proc->asts, may cause lkb to be freed */
8488304d6f2SDavid Teigland 		dlm_put_lkb(lkb);
8498304d6f2SDavid Teigland 		goto try_another;
85089d799d0SDavid Teigland 	}
8518304d6f2SDavid Teigland 	if (!resid)
85223e8e1aaSDavid Teigland 		list_del_init(&lkb->lkb_cb_list);
853597d0caeSDavid Teigland 	spin_unlock(&proc->asts_spin);
854597d0caeSDavid Teigland 
8558304d6f2SDavid Teigland 	if (cb.flags & DLM_CB_SKIP) {
8568304d6f2SDavid Teigland 		/* removes ref for proc->asts, may cause lkb to be freed */
8578304d6f2SDavid Teigland 		if (!resid)
8588304d6f2SDavid Teigland 			dlm_put_lkb(lkb);
8598304d6f2SDavid Teigland 		goto try_another;
86089d799d0SDavid Teigland 	}
861597d0caeSDavid Teigland 
8628304d6f2SDavid Teigland 	if (cb.flags & DLM_CB_CAST) {
8638304d6f2SDavid Teigland 		new_mode = cb.mode;
8648304d6f2SDavid Teigland 
8658304d6f2SDavid Teigland 		if (!cb.sb_status && lkb->lkb_lksb->sb_lvbptr &&
8668304d6f2SDavid Teigland 		    dlm_lvb_operations[old_mode + 1][new_mode + 1])
8678304d6f2SDavid Teigland 			copy_lvb = 1;
8688304d6f2SDavid Teigland 
8698304d6f2SDavid Teigland 		lkb->lkb_lksb->sb_status = cb.sb_status;
8708304d6f2SDavid Teigland 		lkb->lkb_lksb->sb_flags = cb.sb_flags;
8718304d6f2SDavid Teigland 	}
8728304d6f2SDavid Teigland 
8738304d6f2SDavid Teigland 	rv = copy_result_to_user(lkb->lkb_ua,
8748304d6f2SDavid Teigland 				 test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
8758304d6f2SDavid Teigland 				 cb.flags, cb.mode, copy_lvb, buf, count);
8768304d6f2SDavid Teigland 
8778304d6f2SDavid Teigland 	/* removes ref for proc->asts, may cause lkb to be freed */
8788304d6f2SDavid Teigland 	if (!resid)
879597d0caeSDavid Teigland 		dlm_put_lkb(lkb);
880597d0caeSDavid Teigland 
8818304d6f2SDavid Teigland 	return rv;
882597d0caeSDavid Teigland }
883597d0caeSDavid Teigland 
884597d0caeSDavid Teigland static unsigned int device_poll(struct file *file, poll_table *wait)
885597d0caeSDavid Teigland {
886597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
887597d0caeSDavid Teigland 
888597d0caeSDavid Teigland 	poll_wait(file, &proc->wait, wait);
889597d0caeSDavid Teigland 
890597d0caeSDavid Teigland 	spin_lock(&proc->asts_spin);
891597d0caeSDavid Teigland 	if (!list_empty(&proc->asts)) {
892597d0caeSDavid Teigland 		spin_unlock(&proc->asts_spin);
893597d0caeSDavid Teigland 		return POLLIN | POLLRDNORM;
894597d0caeSDavid Teigland 	}
895597d0caeSDavid Teigland 	spin_unlock(&proc->asts_spin);
896597d0caeSDavid Teigland 	return 0;
897597d0caeSDavid Teigland }
898597d0caeSDavid Teigland 
899dc68c7edSDavid Teigland int dlm_user_daemon_available(void)
900dc68c7edSDavid Teigland {
901dc68c7edSDavid Teigland 	/* dlm_controld hasn't started (or, has started, but not
902dc68c7edSDavid Teigland 	   properly populated configfs) */
903dc68c7edSDavid Teigland 
904dc68c7edSDavid Teigland 	if (!dlm_our_nodeid())
905dc68c7edSDavid Teigland 		return 0;
906dc68c7edSDavid Teigland 
907dc68c7edSDavid Teigland 	/* This is to deal with versions of dlm_controld that don't
908dc68c7edSDavid Teigland 	   know about the monitor device.  We assume that if the
909dc68c7edSDavid Teigland 	   dlm_controld was started (above), but the monitor device
910dc68c7edSDavid Teigland 	   was never opened, that it's an old version.  dlm_controld
911dc68c7edSDavid Teigland 	   should open the monitor device before populating configfs. */
912dc68c7edSDavid Teigland 
913dc68c7edSDavid Teigland 	if (dlm_monitor_unused)
914dc68c7edSDavid Teigland 		return 1;
915dc68c7edSDavid Teigland 
916dc68c7edSDavid Teigland 	return atomic_read(&dlm_monitor_opened) ? 1 : 0;
917dc68c7edSDavid Teigland }
918dc68c7edSDavid Teigland 
919597d0caeSDavid Teigland static int ctl_device_open(struct inode *inode, struct file *file)
920597d0caeSDavid Teigland {
921597d0caeSDavid Teigland 	file->private_data = NULL;
922597d0caeSDavid Teigland 	return 0;
923597d0caeSDavid Teigland }
924597d0caeSDavid Teigland 
925597d0caeSDavid Teigland static int ctl_device_close(struct inode *inode, struct file *file)
926597d0caeSDavid Teigland {
927597d0caeSDavid Teigland 	return 0;
928597d0caeSDavid Teigland }
929597d0caeSDavid Teigland 
930dc68c7edSDavid Teigland static int monitor_device_open(struct inode *inode, struct file *file)
931dc68c7edSDavid Teigland {
932dc68c7edSDavid Teigland 	atomic_inc(&dlm_monitor_opened);
933dc68c7edSDavid Teigland 	dlm_monitor_unused = 0;
934dc68c7edSDavid Teigland 	return 0;
935dc68c7edSDavid Teigland }
936dc68c7edSDavid Teigland 
937dc68c7edSDavid Teigland static int monitor_device_close(struct inode *inode, struct file *file)
938dc68c7edSDavid Teigland {
939dc68c7edSDavid Teigland 	if (atomic_dec_and_test(&dlm_monitor_opened))
940dc68c7edSDavid Teigland 		dlm_stop_lockspaces();
941dc68c7edSDavid Teigland 	return 0;
942dc68c7edSDavid Teigland }
943dc68c7edSDavid Teigland 
94400977a59SArjan van de Ven static const struct file_operations device_fops = {
945597d0caeSDavid Teigland 	.open    = device_open,
946597d0caeSDavid Teigland 	.release = device_close,
947597d0caeSDavid Teigland 	.read    = device_read,
948597d0caeSDavid Teigland 	.write   = device_write,
949597d0caeSDavid Teigland 	.poll    = device_poll,
950597d0caeSDavid Teigland 	.owner   = THIS_MODULE,
9516038f373SArnd Bergmann 	.llseek  = noop_llseek,
952597d0caeSDavid Teigland };
953597d0caeSDavid Teigland 
95400977a59SArjan van de Ven static const struct file_operations ctl_device_fops = {
955597d0caeSDavid Teigland 	.open    = ctl_device_open,
956597d0caeSDavid Teigland 	.release = ctl_device_close,
957d7db923eSDavid Teigland 	.read    = device_read,
958597d0caeSDavid Teigland 	.write   = device_write,
959597d0caeSDavid Teigland 	.owner   = THIS_MODULE,
9606038f373SArnd Bergmann 	.llseek  = noop_llseek,
961597d0caeSDavid Teigland };
962597d0caeSDavid Teigland 
9630fe410d3SDenis Cheng static struct miscdevice ctl_device = {
9640fe410d3SDenis Cheng 	.name  = "dlm-control",
9650fe410d3SDenis Cheng 	.fops  = &ctl_device_fops,
9660fe410d3SDenis Cheng 	.minor = MISC_DYNAMIC_MINOR,
9670fe410d3SDenis Cheng };
9680fe410d3SDenis Cheng 
969dc68c7edSDavid Teigland static const struct file_operations monitor_device_fops = {
970dc68c7edSDavid Teigland 	.open    = monitor_device_open,
971dc68c7edSDavid Teigland 	.release = monitor_device_close,
972dc68c7edSDavid Teigland 	.owner   = THIS_MODULE,
9736038f373SArnd Bergmann 	.llseek  = noop_llseek,
974dc68c7edSDavid Teigland };
975dc68c7edSDavid Teigland 
976dc68c7edSDavid Teigland static struct miscdevice monitor_device = {
977dc68c7edSDavid Teigland 	.name  = "dlm-monitor",
978dc68c7edSDavid Teigland 	.fops  = &monitor_device_fops,
979dc68c7edSDavid Teigland 	.minor = MISC_DYNAMIC_MINOR,
980dc68c7edSDavid Teigland };
981dc68c7edSDavid Teigland 
98230727174SDenis Cheng int __init dlm_user_init(void)
983597d0caeSDavid Teigland {
984597d0caeSDavid Teigland 	int error;
985597d0caeSDavid Teigland 
986dc68c7edSDavid Teigland 	atomic_set(&dlm_monitor_opened, 0);
987597d0caeSDavid Teigland 
988dc68c7edSDavid Teigland 	error = misc_register(&ctl_device);
989dc68c7edSDavid Teigland 	if (error) {
990dc68c7edSDavid Teigland 		log_print("misc_register failed for control device");
991dc68c7edSDavid Teigland 		goto out;
992dc68c7edSDavid Teigland 	}
993dc68c7edSDavid Teigland 
994dc68c7edSDavid Teigland 	error = misc_register(&monitor_device);
995dc68c7edSDavid Teigland 	if (error) {
996dc68c7edSDavid Teigland 		log_print("misc_register failed for monitor device");
997dc68c7edSDavid Teigland 		misc_deregister(&ctl_device);
998dc68c7edSDavid Teigland 	}
999dc68c7edSDavid Teigland  out:
1000597d0caeSDavid Teigland 	return error;
1001597d0caeSDavid Teigland }
1002597d0caeSDavid Teigland 
1003597d0caeSDavid Teigland void dlm_user_exit(void)
1004597d0caeSDavid Teigland {
1005597d0caeSDavid Teigland 	misc_deregister(&ctl_device);
1006dc68c7edSDavid Teigland 	misc_deregister(&monitor_device);
1007597d0caeSDavid Teigland }
1008597d0caeSDavid Teigland 
1009