xref: /linux/fs/dlm/user.c (revision 578acf9a)
12522fe45SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2597d0caeSDavid Teigland /*
37fe2b319SDavid Teigland  * Copyright (C) 2006-2010 Red Hat, Inc.  All rights reserved.
4597d0caeSDavid Teigland  */
5597d0caeSDavid Teigland 
6597d0caeSDavid Teigland #include <linux/miscdevice.h>
7597d0caeSDavid Teigland #include <linux/init.h>
8597d0caeSDavid Teigland #include <linux/wait.h>
9597d0caeSDavid Teigland #include <linux/file.h>
10597d0caeSDavid Teigland #include <linux/fs.h>
11597d0caeSDavid Teigland #include <linux/poll.h>
12597d0caeSDavid Teigland #include <linux/signal.h>
13597d0caeSDavid Teigland #include <linux/spinlock.h>
14597d0caeSDavid Teigland #include <linux/dlm.h>
15597d0caeSDavid Teigland #include <linux/dlm_device.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
17174cd4b1SIngo Molnar #include <linux/sched/signal.h>
18597d0caeSDavid Teigland 
197a3de732SAlexander Aring #include <trace/events/dlm.h>
207a3de732SAlexander Aring 
21597d0caeSDavid Teigland #include "dlm_internal.h"
22597d0caeSDavid Teigland #include "lockspace.h"
23597d0caeSDavid Teigland #include "lock.h"
24986ae3c2SAlexander Aring #include "lvb_table.h"
2584c6e8cdSAdrian Bunk #include "user.h"
268304d6f2SDavid Teigland #include "ast.h"
273595c559SDavid Teigland #include "config.h"
2861bed0baSAlexander Aring #include "memory.h"
29597d0caeSDavid Teigland 
300fe410d3SDenis Cheng static const char name_prefix[] = "dlm";
3100977a59SArjan van de Ven static const struct file_operations device_fops;
32dc68c7edSDavid Teigland static atomic_t dlm_monitor_opened;
33dc68c7edSDavid Teigland static int dlm_monitor_unused = 1;
34597d0caeSDavid Teigland 
35597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
36597d0caeSDavid Teigland 
37597d0caeSDavid Teigland struct dlm_lock_params32 {
38597d0caeSDavid Teigland 	__u8 mode;
39597d0caeSDavid Teigland 	__u8 namelen;
40d7db923eSDavid Teigland 	__u16 unused;
41d7db923eSDavid Teigland 	__u32 flags;
42597d0caeSDavid Teigland 	__u32 lkid;
43597d0caeSDavid Teigland 	__u32 parent;
44d7db923eSDavid Teigland 	__u64 xid;
45d7db923eSDavid Teigland 	__u64 timeout;
46597d0caeSDavid Teigland 	__u32 castparam;
47597d0caeSDavid Teigland 	__u32 castaddr;
48597d0caeSDavid Teigland 	__u32 bastparam;
49597d0caeSDavid Teigland 	__u32 bastaddr;
50597d0caeSDavid Teigland 	__u32 lksb;
51597d0caeSDavid Teigland 	char lvb[DLM_USER_LVB_LEN];
523c80d379SGustavo A. R. Silva 	char name[];
53597d0caeSDavid Teigland };
54597d0caeSDavid Teigland 
55597d0caeSDavid Teigland struct dlm_write_request32 {
56597d0caeSDavid Teigland 	__u32 version[3];
57597d0caeSDavid Teigland 	__u8 cmd;
58597d0caeSDavid Teigland 	__u8 is64bit;
59597d0caeSDavid Teigland 	__u8 unused[2];
60597d0caeSDavid Teigland 
61597d0caeSDavid Teigland 	union  {
62597d0caeSDavid Teigland 		struct dlm_lock_params32 lock;
63597d0caeSDavid Teigland 		struct dlm_lspace_params lspace;
6472c2be77SDavid Teigland 		struct dlm_purge_params purge;
65597d0caeSDavid Teigland 	} i;
66597d0caeSDavid Teigland };
67597d0caeSDavid Teigland 
68597d0caeSDavid Teigland struct dlm_lksb32 {
69597d0caeSDavid Teigland 	__u32 sb_status;
70597d0caeSDavid Teigland 	__u32 sb_lkid;
71597d0caeSDavid Teigland 	__u8 sb_flags;
72597d0caeSDavid Teigland 	__u32 sb_lvbptr;
73597d0caeSDavid Teigland };
74597d0caeSDavid Teigland 
75597d0caeSDavid Teigland struct dlm_lock_result32 {
76d7db923eSDavid Teigland 	__u32 version[3];
77597d0caeSDavid Teigland 	__u32 length;
78597d0caeSDavid Teigland 	__u32 user_astaddr;
79597d0caeSDavid Teigland 	__u32 user_astparam;
80597d0caeSDavid Teigland 	__u32 user_lksb;
81597d0caeSDavid Teigland 	struct dlm_lksb32 lksb;
82597d0caeSDavid Teigland 	__u8 bast_mode;
83597d0caeSDavid Teigland 	__u8 unused[3];
84597d0caeSDavid Teigland 	/* Offsets may be zero if no data is present */
85597d0caeSDavid Teigland 	__u32 lvb_offset;
86597d0caeSDavid Teigland };
87597d0caeSDavid Teigland 
compat_input(struct dlm_write_request * kb,struct dlm_write_request32 * kb32,int namelen)88597d0caeSDavid Teigland static void compat_input(struct dlm_write_request *kb,
892a79289eSPatrick Caulfeld 			 struct dlm_write_request32 *kb32,
901fecb1c4SDavid Teigland 			 int namelen)
91597d0caeSDavid Teigland {
92597d0caeSDavid Teigland 	kb->version[0] = kb32->version[0];
93597d0caeSDavid Teigland 	kb->version[1] = kb32->version[1];
94597d0caeSDavid Teigland 	kb->version[2] = kb32->version[2];
95597d0caeSDavid Teigland 
96597d0caeSDavid Teigland 	kb->cmd = kb32->cmd;
97597d0caeSDavid Teigland 	kb->is64bit = kb32->is64bit;
98597d0caeSDavid Teigland 	if (kb->cmd == DLM_USER_CREATE_LOCKSPACE ||
99597d0caeSDavid Teigland 	    kb->cmd == DLM_USER_REMOVE_LOCKSPACE) {
100597d0caeSDavid Teigland 		kb->i.lspace.flags = kb32->i.lspace.flags;
101597d0caeSDavid Teigland 		kb->i.lspace.minor = kb32->i.lspace.minor;
1021fecb1c4SDavid Teigland 		memcpy(kb->i.lspace.name, kb32->i.lspace.name, namelen);
10372c2be77SDavid Teigland 	} else if (kb->cmd == DLM_USER_PURGE) {
10472c2be77SDavid Teigland 		kb->i.purge.nodeid = kb32->i.purge.nodeid;
10572c2be77SDavid Teigland 		kb->i.purge.pid = kb32->i.purge.pid;
106597d0caeSDavid Teigland 	} else {
107597d0caeSDavid Teigland 		kb->i.lock.mode = kb32->i.lock.mode;
108597d0caeSDavid Teigland 		kb->i.lock.namelen = kb32->i.lock.namelen;
109597d0caeSDavid Teigland 		kb->i.lock.flags = kb32->i.lock.flags;
110597d0caeSDavid Teigland 		kb->i.lock.lkid = kb32->i.lock.lkid;
111597d0caeSDavid Teigland 		kb->i.lock.parent = kb32->i.lock.parent;
112d7db923eSDavid Teigland 		kb->i.lock.xid = kb32->i.lock.xid;
113d7db923eSDavid Teigland 		kb->i.lock.timeout = kb32->i.lock.timeout;
114c087eabdSAlexander Aring 		kb->i.lock.castparam = (__user void *)(long)kb32->i.lock.castparam;
115c087eabdSAlexander Aring 		kb->i.lock.castaddr = (__user void *)(long)kb32->i.lock.castaddr;
116c087eabdSAlexander Aring 		kb->i.lock.bastparam = (__user void *)(long)kb32->i.lock.bastparam;
117c087eabdSAlexander Aring 		kb->i.lock.bastaddr = (__user void *)(long)kb32->i.lock.bastaddr;
118c087eabdSAlexander Aring 		kb->i.lock.lksb = (__user void *)(long)kb32->i.lock.lksb;
119597d0caeSDavid Teigland 		memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN);
1201fecb1c4SDavid Teigland 		memcpy(kb->i.lock.name, kb32->i.lock.name, namelen);
121597d0caeSDavid Teigland 	}
122597d0caeSDavid Teigland }
123597d0caeSDavid Teigland 
compat_output(struct dlm_lock_result * res,struct dlm_lock_result32 * res32)124597d0caeSDavid Teigland static void compat_output(struct dlm_lock_result *res,
125597d0caeSDavid Teigland 			  struct dlm_lock_result32 *res32)
126597d0caeSDavid Teigland {
1278286d6b1SVlad Tsyrklevich 	memset(res32, 0, sizeof(*res32));
1288286d6b1SVlad Tsyrklevich 
129d7db923eSDavid Teigland 	res32->version[0] = res->version[0];
130d7db923eSDavid Teigland 	res32->version[1] = res->version[1];
131d7db923eSDavid Teigland 	res32->version[2] = res->version[2];
132d7db923eSDavid Teigland 
133c087eabdSAlexander Aring 	res32->user_astaddr = (__u32)(__force long)res->user_astaddr;
134c087eabdSAlexander Aring 	res32->user_astparam = (__u32)(__force long)res->user_astparam;
135c087eabdSAlexander Aring 	res32->user_lksb = (__u32)(__force long)res->user_lksb;
136597d0caeSDavid Teigland 	res32->bast_mode = res->bast_mode;
137597d0caeSDavid Teigland 
138597d0caeSDavid Teigland 	res32->lvb_offset = res->lvb_offset;
139597d0caeSDavid Teigland 	res32->length = res->length;
140597d0caeSDavid Teigland 
141597d0caeSDavid Teigland 	res32->lksb.sb_status = res->lksb.sb_status;
142597d0caeSDavid Teigland 	res32->lksb.sb_flags = res->lksb.sb_flags;
143597d0caeSDavid Teigland 	res32->lksb.sb_lkid = res->lksb.sb_lkid;
144597d0caeSDavid Teigland 	res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr;
145597d0caeSDavid Teigland }
146597d0caeSDavid Teigland #endif
147597d0caeSDavid Teigland 
14884d8cd69SDavid Teigland /* Figure out if this lock is at the end of its life and no longer
14984d8cd69SDavid Teigland    available for the application to use.  The lkb still exists until
15084d8cd69SDavid Teigland    the final ast is read.  A lock becomes EOL in three situations:
15184d8cd69SDavid Teigland      1. a noqueue request fails with EAGAIN
15284d8cd69SDavid Teigland      2. an unlock completes with EUNLOCK
15384d8cd69SDavid Teigland      3. a cancel of a waiting request completes with ECANCEL/EDEADLK
15484d8cd69SDavid Teigland    An EOL lock needs to be removed from the process's list of locks.
15584d8cd69SDavid Teigland    And we can't allow any new operation on an EOL lock.  This is
15684d8cd69SDavid Teigland    not related to the lifetime of the lkb struct which is managed
15784d8cd69SDavid Teigland    entirely by refcount. */
15884d8cd69SDavid Teigland 
lkb_is_endoflife(int mode,int status)1598304d6f2SDavid Teigland static int lkb_is_endoflife(int mode, int status)
16084d8cd69SDavid Teigland {
1618304d6f2SDavid Teigland 	switch (status) {
16284d8cd69SDavid Teigland 	case -DLM_EUNLOCK:
16384d8cd69SDavid Teigland 		return 1;
16484d8cd69SDavid Teigland 	case -DLM_ECANCEL:
16584d8cd69SDavid Teigland 	case -ETIMEDOUT:
1668b4021faSDavid Teigland 	case -EDEADLK:
16784d8cd69SDavid Teigland 	case -EAGAIN:
1688304d6f2SDavid Teigland 		if (mode == DLM_LOCK_IV)
16984d8cd69SDavid Teigland 			return 1;
17084d8cd69SDavid Teigland 		break;
17184d8cd69SDavid Teigland 	}
17284d8cd69SDavid Teigland 	return 0;
17384d8cd69SDavid Teigland }
17484d8cd69SDavid Teigland 
175ef0c2bb0SDavid Teigland /* we could possibly check if the cancel of an orphan has resulted in the lkb
176ef0c2bb0SDavid Teigland    being removed and then remove that lkb from the orphans list and free it */
177597d0caeSDavid Teigland 
dlm_user_add_ast(struct dlm_lkb * lkb,uint32_t flags,int mode,int status,uint32_t sbflags)1788304d6f2SDavid Teigland void dlm_user_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode,
17961bed0baSAlexander Aring 		      int status, uint32_t sbflags)
180597d0caeSDavid Teigland {
181597d0caeSDavid Teigland 	struct dlm_ls *ls;
182597d0caeSDavid Teigland 	struct dlm_user_args *ua;
183597d0caeSDavid Teigland 	struct dlm_user_proc *proc;
184986ae3c2SAlexander Aring 	struct dlm_callback *cb;
1858304d6f2SDavid Teigland 	int rv;
186597d0caeSDavid Teigland 
1878a39dcd9SAlexander Aring 	if (test_bit(DLM_DFL_ORPHAN_BIT, &lkb->lkb_dflags) ||
188e1af8728SAlexander Aring 	    test_bit(DLM_IFL_DEAD_BIT, &lkb->lkb_iflags))
189597d0caeSDavid Teigland 		return;
190597d0caeSDavid Teigland 
191597d0caeSDavid Teigland 	ls = lkb->lkb_resource->res_ls;
192*578acf9aSAlexander Aring 	spin_lock_bh(&ls->ls_clear_proc_locks);
193597d0caeSDavid Teigland 
194597d0caeSDavid Teigland 	/* If ORPHAN/DEAD flag is set, it means the process is dead so an ast
195597d0caeSDavid Teigland 	   can't be delivered.  For ORPHAN's, dlm_clear_proc_locks() freed
196ef0c2bb0SDavid Teigland 	   lkb->ua so we can't try to use it.  This second check is necessary
197ef0c2bb0SDavid Teigland 	   for cases where a completion ast is received for an operation that
198ef0c2bb0SDavid Teigland 	   began before clear_proc_locks did its cancel/unlock. */
199597d0caeSDavid Teigland 
2008a39dcd9SAlexander Aring 	if (test_bit(DLM_DFL_ORPHAN_BIT, &lkb->lkb_dflags) ||
201e1af8728SAlexander Aring 	    test_bit(DLM_IFL_DEAD_BIT, &lkb->lkb_iflags))
202597d0caeSDavid Teigland 		goto out;
203597d0caeSDavid Teigland 
204d292c0ccSDavid Teigland 	DLM_ASSERT(lkb->lkb_ua, dlm_print_lkb(lkb););
205d292c0ccSDavid Teigland 	ua = lkb->lkb_ua;
206597d0caeSDavid Teigland 	proc = ua->proc;
207597d0caeSDavid Teigland 
2088304d6f2SDavid Teigland 	if ((flags & DLM_CB_BAST) && ua->bastaddr == NULL)
209597d0caeSDavid Teigland 		goto out;
210597d0caeSDavid Teigland 
2118304d6f2SDavid Teigland 	if ((flags & DLM_CB_CAST) && lkb_is_endoflife(mode, status))
212e1af8728SAlexander Aring 		set_bit(DLM_IFL_ENDOFLIFE_BIT, &lkb->lkb_iflags);
2138304d6f2SDavid Teigland 
214*578acf9aSAlexander Aring 	spin_lock_bh(&proc->asts_spin);
215ef0c2bb0SDavid Teigland 
216986ae3c2SAlexander Aring 	rv = dlm_queue_lkb_callback(lkb, flags, mode, status, sbflags, &cb);
21761bed0baSAlexander Aring 	switch (rv) {
21861bed0baSAlexander Aring 	case DLM_ENQUEUE_CALLBACK_NEED_SCHED:
219986ae3c2SAlexander Aring 		cb->ua = *ua;
220986ae3c2SAlexander Aring 		cb->lkb_lksb = &cb->ua.lksb;
221986ae3c2SAlexander Aring 		if (cb->copy_lvb) {
222986ae3c2SAlexander Aring 			memcpy(cb->lvbptr, ua->lksb.sb_lvbptr,
223986ae3c2SAlexander Aring 			       DLM_USER_LVB_LEN);
224986ae3c2SAlexander Aring 			cb->lkb_lksb->sb_lvbptr = cb->lvbptr;
225986ae3c2SAlexander Aring 		}
226986ae3c2SAlexander Aring 
227986ae3c2SAlexander Aring 		list_add_tail(&cb->list, &proc->asts);
228597d0caeSDavid Teigland 		wake_up_interruptible(&proc->wait);
22961bed0baSAlexander Aring 		break;
23061bed0baSAlexander Aring 	case DLM_ENQUEUE_CALLBACK_SUCCESS:
23161bed0baSAlexander Aring 		break;
2320175e51bSAlexander Aring 	case DLM_ENQUEUE_CALLBACK_FAILURE:
2330175e51bSAlexander Aring 		fallthrough;
23461bed0baSAlexander Aring 	default:
235*578acf9aSAlexander Aring 		spin_unlock_bh(&proc->asts_spin);
236740bb8fcSAlexander Aring 		WARN_ON_ONCE(1);
2370175e51bSAlexander Aring 		goto out;
238597d0caeSDavid Teigland 	}
239*578acf9aSAlexander Aring 	spin_unlock_bh(&proc->asts_spin);
24034e22bedSDavid Teigland 
241e1af8728SAlexander Aring 	if (test_bit(DLM_IFL_ENDOFLIFE_BIT, &lkb->lkb_iflags)) {
2428304d6f2SDavid Teigland 		/* N.B. spin_lock locks_spin, not asts_spin */
243*578acf9aSAlexander Aring 		spin_lock_bh(&proc->locks_spin);
244ef0c2bb0SDavid Teigland 		if (!list_empty(&lkb->lkb_ownqueue)) {
24534e22bedSDavid Teigland 			list_del_init(&lkb->lkb_ownqueue);
24634e22bedSDavid Teigland 			dlm_put_lkb(lkb);
24734e22bedSDavid Teigland 		}
248*578acf9aSAlexander Aring 		spin_unlock_bh(&proc->locks_spin);
249ef0c2bb0SDavid Teigland 	}
250597d0caeSDavid Teigland  out:
251*578acf9aSAlexander Aring 	spin_unlock_bh(&ls->ls_clear_proc_locks);
252597d0caeSDavid Teigland }
253597d0caeSDavid Teigland 
device_user_lock(struct dlm_user_proc * proc,struct dlm_lock_params * params)254597d0caeSDavid Teigland static int device_user_lock(struct dlm_user_proc *proc,
255597d0caeSDavid Teigland 			    struct dlm_lock_params *params)
256597d0caeSDavid Teigland {
257597d0caeSDavid Teigland 	struct dlm_ls *ls;
258597d0caeSDavid Teigland 	struct dlm_user_args *ua;
2592ab4bd8eSDavid Teigland 	uint32_t lkid;
260597d0caeSDavid Teigland 	int error = -ENOMEM;
261597d0caeSDavid Teigland 
262597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
263597d0caeSDavid Teigland 	if (!ls)
264597d0caeSDavid Teigland 		return -ENOENT;
265597d0caeSDavid Teigland 
266597d0caeSDavid Teigland 	if (!params->castaddr || !params->lksb) {
267597d0caeSDavid Teigland 		error = -EINVAL;
268597d0caeSDavid Teigland 		goto out;
269597d0caeSDavid Teigland 	}
270597d0caeSDavid Teigland 
271573c24c4SDavid Teigland 	ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS);
272597d0caeSDavid Teigland 	if (!ua)
273597d0caeSDavid Teigland 		goto out;
274597d0caeSDavid Teigland 	ua->proc = proc;
275597d0caeSDavid Teigland 	ua->user_lksb = params->lksb;
276597d0caeSDavid Teigland 	ua->castparam = params->castparam;
277597d0caeSDavid Teigland 	ua->castaddr = params->castaddr;
278597d0caeSDavid Teigland 	ua->bastparam = params->bastparam;
279597d0caeSDavid Teigland 	ua->bastaddr = params->bastaddr;
280d7db923eSDavid Teigland 	ua->xid = params->xid;
281597d0caeSDavid Teigland 
2822ab4bd8eSDavid Teigland 	if (params->flags & DLM_LKF_CONVERT) {
2836b0afc0cSAlexander Aring 		error = dlm_user_convert(ls, ua,
2846b0afc0cSAlexander Aring 					 params->mode, params->flags,
2856b0afc0cSAlexander Aring 					 params->lkid, params->lvb);
2862ab4bd8eSDavid Teigland 	} else if (params->flags & DLM_LKF_ORPHAN) {
2872ab4bd8eSDavid Teigland 		error = dlm_user_adopt_orphan(ls, ua,
2882ab4bd8eSDavid Teigland 					 params->mode, params->flags,
2892ab4bd8eSDavid Teigland 					 params->name, params->namelen,
2902ab4bd8eSDavid Teigland 					 &lkid);
2912ab4bd8eSDavid Teigland 		if (!error)
2922ab4bd8eSDavid Teigland 			error = lkid;
2932ab4bd8eSDavid Teigland 	} else {
2946b0afc0cSAlexander Aring 		error = dlm_user_request(ls, ua,
2956b0afc0cSAlexander Aring 					 params->mode, params->flags,
2966b0afc0cSAlexander Aring 					 params->name, params->namelen);
297597d0caeSDavid Teigland 		if (!error)
298597d0caeSDavid Teigland 			error = ua->lksb.sb_lkid;
299597d0caeSDavid Teigland 	}
300597d0caeSDavid Teigland  out:
301597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
302597d0caeSDavid Teigland 	return error;
303597d0caeSDavid Teigland }
304597d0caeSDavid Teigland 
device_user_unlock(struct dlm_user_proc * proc,struct dlm_lock_params * params)305597d0caeSDavid Teigland static int device_user_unlock(struct dlm_user_proc *proc,
306597d0caeSDavid Teigland 			      struct dlm_lock_params *params)
307597d0caeSDavid Teigland {
308597d0caeSDavid Teigland 	struct dlm_ls *ls;
309597d0caeSDavid Teigland 	struct dlm_user_args *ua;
310597d0caeSDavid Teigland 	int error = -ENOMEM;
311597d0caeSDavid Teigland 
312597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
313597d0caeSDavid Teigland 	if (!ls)
314597d0caeSDavid Teigland 		return -ENOENT;
315597d0caeSDavid Teigland 
316573c24c4SDavid Teigland 	ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS);
317597d0caeSDavid Teigland 	if (!ua)
318597d0caeSDavid Teigland 		goto out;
319597d0caeSDavid Teigland 	ua->proc = proc;
320597d0caeSDavid Teigland 	ua->user_lksb = params->lksb;
321597d0caeSDavid Teigland 	ua->castparam = params->castparam;
322597d0caeSDavid Teigland 	ua->castaddr = params->castaddr;
323597d0caeSDavid Teigland 
324597d0caeSDavid Teigland 	if (params->flags & DLM_LKF_CANCEL)
325597d0caeSDavid Teigland 		error = dlm_user_cancel(ls, ua, params->flags, params->lkid);
326597d0caeSDavid Teigland 	else
327597d0caeSDavid Teigland 		error = dlm_user_unlock(ls, ua, params->flags, params->lkid,
328597d0caeSDavid Teigland 					params->lvb);
329597d0caeSDavid Teigland  out:
330597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
331597d0caeSDavid Teigland 	return error;
332597d0caeSDavid Teigland }
333597d0caeSDavid Teigland 
device_user_deadlock(struct dlm_user_proc * proc,struct dlm_lock_params * params)3348b4021faSDavid Teigland static int device_user_deadlock(struct dlm_user_proc *proc,
3358b4021faSDavid Teigland 				struct dlm_lock_params *params)
3368b4021faSDavid Teigland {
3378b4021faSDavid Teigland 	struct dlm_ls *ls;
3388b4021faSDavid Teigland 	int error;
3398b4021faSDavid Teigland 
3408b4021faSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
3418b4021faSDavid Teigland 	if (!ls)
3428b4021faSDavid Teigland 		return -ENOENT;
3438b4021faSDavid Teigland 
3448b4021faSDavid Teigland 	error = dlm_user_deadlock(ls, params->flags, params->lkid);
3458b4021faSDavid Teigland 
3468b4021faSDavid Teigland 	dlm_put_lockspace(ls);
3478b4021faSDavid Teigland 	return error;
3488b4021faSDavid Teigland }
3498b4021faSDavid Teigland 
dlm_device_register(struct dlm_ls * ls,char * name)3500f8e0d9aSDavid Teigland static int dlm_device_register(struct dlm_ls *ls, char *name)
351254da030SPatrick Caulfield {
352254da030SPatrick Caulfield 	int error, len;
353254da030SPatrick Caulfield 
3540f8e0d9aSDavid Teigland 	/* The device is already registered.  This happens when the
3550f8e0d9aSDavid Teigland 	   lockspace is created multiple times from userspace. */
3560f8e0d9aSDavid Teigland 	if (ls->ls_device.name)
3570f8e0d9aSDavid Teigland 		return 0;
3580f8e0d9aSDavid Teigland 
359254da030SPatrick Caulfield 	error = -ENOMEM;
360254da030SPatrick Caulfield 	len = strlen(name) + strlen(name_prefix) + 2;
361573c24c4SDavid Teigland 	ls->ls_device.name = kzalloc(len, GFP_NOFS);
362254da030SPatrick Caulfield 	if (!ls->ls_device.name)
363254da030SPatrick Caulfield 		goto fail;
364254da030SPatrick Caulfield 
365254da030SPatrick Caulfield 	snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix,
366254da030SPatrick Caulfield 		 name);
367254da030SPatrick Caulfield 	ls->ls_device.fops = &device_fops;
368254da030SPatrick Caulfield 	ls->ls_device.minor = MISC_DYNAMIC_MINOR;
369254da030SPatrick Caulfield 
370254da030SPatrick Caulfield 	error = misc_register(&ls->ls_device);
371254da030SPatrick Caulfield 	if (error) {
372254da030SPatrick Caulfield 		kfree(ls->ls_device.name);
37355acdd92SEdwin Török 		/* this has to be set to NULL
37455acdd92SEdwin Török 		 * to avoid a double-free in dlm_device_deregister
37555acdd92SEdwin Török 		 */
37655acdd92SEdwin Török 		ls->ls_device.name = NULL;
377254da030SPatrick Caulfield 	}
378254da030SPatrick Caulfield fail:
379254da030SPatrick Caulfield 	return error;
380254da030SPatrick Caulfield }
381254da030SPatrick Caulfield 
dlm_device_deregister(struct dlm_ls * ls)3820f8e0d9aSDavid Teigland int dlm_device_deregister(struct dlm_ls *ls)
3830f8e0d9aSDavid Teigland {
3840f8e0d9aSDavid Teigland 	/* The device is not registered.  This happens when the lockspace
3850f8e0d9aSDavid Teigland 	   was never used from userspace, or when device_create_lockspace()
3860f8e0d9aSDavid Teigland 	   calls dlm_release_lockspace() after the register fails. */
3870f8e0d9aSDavid Teigland 	if (!ls->ls_device.name)
3880f8e0d9aSDavid Teigland 		return 0;
3890f8e0d9aSDavid Teigland 
390f368ed60SGreg Kroah-Hartman 	misc_deregister(&ls->ls_device);
3910f8e0d9aSDavid Teigland 	kfree(ls->ls_device.name);
392f368ed60SGreg Kroah-Hartman 	return 0;
3930f8e0d9aSDavid Teigland }
3940f8e0d9aSDavid Teigland 
device_user_purge(struct dlm_user_proc * proc,struct dlm_purge_params * params)39572c2be77SDavid Teigland static int device_user_purge(struct dlm_user_proc *proc,
39672c2be77SDavid Teigland 			     struct dlm_purge_params *params)
39772c2be77SDavid Teigland {
39872c2be77SDavid Teigland 	struct dlm_ls *ls;
39972c2be77SDavid Teigland 	int error;
40072c2be77SDavid Teigland 
40172c2be77SDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
40272c2be77SDavid Teigland 	if (!ls)
40372c2be77SDavid Teigland 		return -ENOENT;
40472c2be77SDavid Teigland 
40572c2be77SDavid Teigland 	error = dlm_user_purge(ls, proc, params->nodeid, params->pid);
40672c2be77SDavid Teigland 
40772c2be77SDavid Teigland 	dlm_put_lockspace(ls);
40872c2be77SDavid Teigland 	return error;
40972c2be77SDavid Teigland }
41072c2be77SDavid Teigland 
device_create_lockspace(struct dlm_lspace_params * params)411597d0caeSDavid Teigland static int device_create_lockspace(struct dlm_lspace_params *params)
412597d0caeSDavid Teigland {
413597d0caeSDavid Teigland 	dlm_lockspace_t *lockspace;
414597d0caeSDavid Teigland 	struct dlm_ls *ls;
415254da030SPatrick Caulfield 	int error;
416597d0caeSDavid Teigland 
417597d0caeSDavid Teigland 	if (!capable(CAP_SYS_ADMIN))
418597d0caeSDavid Teigland 		return -EPERM;
419597d0caeSDavid Teigland 
42012cda13cSAlexander Aring 	error = dlm_new_user_lockspace(params->name, dlm_config.ci_cluster_name,
42112cda13cSAlexander Aring 				       params->flags, DLM_USER_LVB_LEN, NULL,
42212cda13cSAlexander Aring 				       NULL, NULL, &lockspace);
423597d0caeSDavid Teigland 	if (error)
424597d0caeSDavid Teigland 		return error;
425597d0caeSDavid Teigland 
426597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(lockspace);
427597d0caeSDavid Teigland 	if (!ls)
428597d0caeSDavid Teigland 		return -ENOENT;
429597d0caeSDavid Teigland 
4300f8e0d9aSDavid Teigland 	error = dlm_device_register(ls, params->name);
431597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
432597d0caeSDavid Teigland 
433254da030SPatrick Caulfield 	if (error)
434597d0caeSDavid Teigland 		dlm_release_lockspace(lockspace, 0);
435254da030SPatrick Caulfield 	else
436254da030SPatrick Caulfield 		error = ls->ls_device.minor;
437254da030SPatrick Caulfield 
438597d0caeSDavid Teigland 	return error;
439597d0caeSDavid Teigland }
440597d0caeSDavid Teigland 
device_remove_lockspace(struct dlm_lspace_params * params)441597d0caeSDavid Teigland static int device_remove_lockspace(struct dlm_lspace_params *params)
442597d0caeSDavid Teigland {
443597d0caeSDavid Teigland 	dlm_lockspace_t *lockspace;
444597d0caeSDavid Teigland 	struct dlm_ls *ls;
445c6e6f0baSDavid Teigland 	int error, force = 0;
446597d0caeSDavid Teigland 
447597d0caeSDavid Teigland 	if (!capable(CAP_SYS_ADMIN))
448597d0caeSDavid Teigland 		return -EPERM;
449597d0caeSDavid Teigland 
450597d0caeSDavid Teigland 	ls = dlm_find_lockspace_device(params->minor);
451597d0caeSDavid Teigland 	if (!ls)
452597d0caeSDavid Teigland 		return -ENOENT;
453597d0caeSDavid Teigland 
454c6e6f0baSDavid Teigland 	if (params->flags & DLM_USER_LSFLG_FORCEFREE)
455c6e6f0baSDavid Teigland 		force = 2;
456c6e6f0baSDavid Teigland 
457597d0caeSDavid Teigland 	lockspace = ls->ls_local_handle;
458597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
4590f8e0d9aSDavid Teigland 
4600f8e0d9aSDavid Teigland 	/* The final dlm_release_lockspace waits for references to go to
4610f8e0d9aSDavid Teigland 	   zero, so all processes will need to close their device for the
4620f8e0d9aSDavid Teigland 	   ls before the release will proceed.  release also calls the
4630f8e0d9aSDavid Teigland 	   device_deregister above.  Converting a positive return value
4640f8e0d9aSDavid Teigland 	   from release to zero means that userspace won't know when its
4650f8e0d9aSDavid Teigland 	   release was the final one, but it shouldn't need to know. */
4660f8e0d9aSDavid Teigland 
467c6e6f0baSDavid Teigland 	error = dlm_release_lockspace(lockspace, force);
4680f8e0d9aSDavid Teigland 	if (error > 0)
4690f8e0d9aSDavid Teigland 		error = 0;
470597d0caeSDavid Teigland 	return error;
471597d0caeSDavid Teigland }
472597d0caeSDavid Teigland 
473597d0caeSDavid Teigland /* Check the user's version matches ours */
check_version(struct dlm_write_request * req)474597d0caeSDavid Teigland static int check_version(struct dlm_write_request *req)
475597d0caeSDavid Teigland {
476597d0caeSDavid Teigland 	if (req->version[0] != DLM_DEVICE_VERSION_MAJOR ||
477597d0caeSDavid Teigland 	    (req->version[0] == DLM_DEVICE_VERSION_MAJOR &&
478597d0caeSDavid Teigland 	     req->version[1] > DLM_DEVICE_VERSION_MINOR)) {
479597d0caeSDavid Teigland 
480597d0caeSDavid Teigland 		printk(KERN_DEBUG "dlm: process %s (%d) version mismatch "
481597d0caeSDavid Teigland 		       "user (%d.%d.%d) kernel (%d.%d.%d)\n",
482597d0caeSDavid Teigland 		       current->comm,
483ba25f9dcSPavel Emelyanov 		       task_pid_nr(current),
484597d0caeSDavid Teigland 		       req->version[0],
485597d0caeSDavid Teigland 		       req->version[1],
486597d0caeSDavid Teigland 		       req->version[2],
487597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_MAJOR,
488597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_MINOR,
489597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_PATCH);
490597d0caeSDavid Teigland 		return -EINVAL;
491597d0caeSDavid Teigland 	}
492597d0caeSDavid Teigland 	return 0;
493597d0caeSDavid Teigland }
494597d0caeSDavid Teigland 
495597d0caeSDavid Teigland /*
496597d0caeSDavid Teigland  * device_write
497597d0caeSDavid Teigland  *
498597d0caeSDavid Teigland  *   device_user_lock
499597d0caeSDavid Teigland  *     dlm_user_request -> request_lock
500597d0caeSDavid Teigland  *     dlm_user_convert -> convert_lock
501597d0caeSDavid Teigland  *
502597d0caeSDavid Teigland  *   device_user_unlock
503597d0caeSDavid Teigland  *     dlm_user_unlock -> unlock_lock
504597d0caeSDavid Teigland  *     dlm_user_cancel -> cancel_lock
505597d0caeSDavid Teigland  *
506597d0caeSDavid Teigland  *   device_create_lockspace
507597d0caeSDavid Teigland  *     dlm_new_lockspace
508597d0caeSDavid Teigland  *
509597d0caeSDavid Teigland  *   device_remove_lockspace
510597d0caeSDavid Teigland  *     dlm_release_lockspace
511597d0caeSDavid Teigland  */
512597d0caeSDavid Teigland 
513597d0caeSDavid Teigland /* a write to a lockspace device is a lock or unlock request, a write
514597d0caeSDavid Teigland    to the control device is to create/remove a lockspace */
515597d0caeSDavid Teigland 
device_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)516597d0caeSDavid Teigland static ssize_t device_write(struct file *file, const char __user *buf,
517597d0caeSDavid Teigland 			    size_t count, loff_t *ppos)
518597d0caeSDavid Teigland {
519597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
520597d0caeSDavid Teigland 	struct dlm_write_request *kbuf;
521597d0caeSDavid Teigland 	int error;
522597d0caeSDavid Teigland 
523597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
524597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_write_request32))
525597d0caeSDavid Teigland #else
526597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_write_request))
527597d0caeSDavid Teigland #endif
528597d0caeSDavid Teigland 		return -EINVAL;
529597d0caeSDavid Teigland 
530d4b0bcf3SDavid Teigland 	/*
531d4b0bcf3SDavid Teigland 	 * can't compare against COMPAT/dlm_write_request32 because
532d4b0bcf3SDavid Teigland 	 * we don't yet know if is64bit is zero
533d4b0bcf3SDavid Teigland 	 */
5342b75bc91SSasha Levin 	if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN)
5352b75bc91SSasha Levin 		return -EINVAL;
5362b75bc91SSasha Levin 
53716e5c1fcSAl Viro 	kbuf = memdup_user_nul(buf, count);
538117aa41eSAl Viro 	if (IS_ERR(kbuf))
53916e5c1fcSAl Viro 		return PTR_ERR(kbuf);
540597d0caeSDavid Teigland 
541597d0caeSDavid Teigland 	if (check_version(kbuf)) {
542597d0caeSDavid Teigland 		error = -EBADE;
543597d0caeSDavid Teigland 		goto out_free;
544597d0caeSDavid Teigland 	}
545597d0caeSDavid Teigland 
546597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
547597d0caeSDavid Teigland 	if (!kbuf->is64bit) {
548597d0caeSDavid Teigland 		struct dlm_write_request32 *k32buf;
5491fecb1c4SDavid Teigland 		int namelen = 0;
5501fecb1c4SDavid Teigland 
5511fecb1c4SDavid Teigland 		if (count > sizeof(struct dlm_write_request32))
5521fecb1c4SDavid Teigland 			namelen = count - sizeof(struct dlm_write_request32);
5531fecb1c4SDavid Teigland 
554597d0caeSDavid Teigland 		k32buf = (struct dlm_write_request32 *)kbuf;
5551fecb1c4SDavid Teigland 
5561fecb1c4SDavid Teigland 		/* add 1 after namelen so that the name string is terminated */
5571fecb1c4SDavid Teigland 		kbuf = kzalloc(sizeof(struct dlm_write_request) + namelen + 1,
558573c24c4SDavid Teigland 			       GFP_NOFS);
559cb980d9aSDavid Teigland 		if (!kbuf) {
560cb980d9aSDavid Teigland 			kfree(k32buf);
561597d0caeSDavid Teigland 			return -ENOMEM;
562cb980d9aSDavid Teigland 		}
563597d0caeSDavid Teigland 
564597d0caeSDavid Teigland 		if (proc)
565597d0caeSDavid Teigland 			set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags);
5661fecb1c4SDavid Teigland 
5671fecb1c4SDavid Teigland 		compat_input(kbuf, k32buf, namelen);
568597d0caeSDavid Teigland 		kfree(k32buf);
569597d0caeSDavid Teigland 	}
570597d0caeSDavid Teigland #endif
571597d0caeSDavid Teigland 
572597d0caeSDavid Teigland 	/* do we really need this? can a write happen after a close? */
573597d0caeSDavid Teigland 	if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) &&
574cb980d9aSDavid Teigland 	    (proc && test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))) {
575cb980d9aSDavid Teigland 		error = -EINVAL;
576cb980d9aSDavid Teigland 		goto out_free;
577cb980d9aSDavid Teigland 	}
578597d0caeSDavid Teigland 
579597d0caeSDavid Teigland 	error = -EINVAL;
580597d0caeSDavid Teigland 
581597d0caeSDavid Teigland 	switch (kbuf->cmd)
582597d0caeSDavid Teigland 	{
583597d0caeSDavid Teigland 	case DLM_USER_LOCK:
584597d0caeSDavid Teigland 		if (!proc) {
585597d0caeSDavid Teigland 			log_print("no locking on control device");
586c6ca7bc9SDavid Teigland 			goto out_free;
587597d0caeSDavid Teigland 		}
588597d0caeSDavid Teigland 		error = device_user_lock(proc, &kbuf->i.lock);
589597d0caeSDavid Teigland 		break;
590597d0caeSDavid Teigland 
591597d0caeSDavid Teigland 	case DLM_USER_UNLOCK:
592597d0caeSDavid Teigland 		if (!proc) {
593597d0caeSDavid Teigland 			log_print("no locking on control device");
594c6ca7bc9SDavid Teigland 			goto out_free;
595597d0caeSDavid Teigland 		}
596597d0caeSDavid Teigland 		error = device_user_unlock(proc, &kbuf->i.lock);
597597d0caeSDavid Teigland 		break;
598597d0caeSDavid Teigland 
5998b4021faSDavid Teigland 	case DLM_USER_DEADLOCK:
6008b4021faSDavid Teigland 		if (!proc) {
6018b4021faSDavid Teigland 			log_print("no locking on control device");
602c6ca7bc9SDavid Teigland 			goto out_free;
6038b4021faSDavid Teigland 		}
6048b4021faSDavid Teigland 		error = device_user_deadlock(proc, &kbuf->i.lock);
6058b4021faSDavid Teigland 		break;
6068b4021faSDavid Teigland 
607597d0caeSDavid Teigland 	case DLM_USER_CREATE_LOCKSPACE:
608597d0caeSDavid Teigland 		if (proc) {
609597d0caeSDavid Teigland 			log_print("create/remove only on control device");
610c6ca7bc9SDavid Teigland 			goto out_free;
611597d0caeSDavid Teigland 		}
612597d0caeSDavid Teigland 		error = device_create_lockspace(&kbuf->i.lspace);
613597d0caeSDavid Teigland 		break;
614597d0caeSDavid Teigland 
615597d0caeSDavid Teigland 	case DLM_USER_REMOVE_LOCKSPACE:
616597d0caeSDavid Teigland 		if (proc) {
617597d0caeSDavid Teigland 			log_print("create/remove only on control device");
618c6ca7bc9SDavid Teigland 			goto out_free;
619597d0caeSDavid Teigland 		}
620597d0caeSDavid Teigland 		error = device_remove_lockspace(&kbuf->i.lspace);
621597d0caeSDavid Teigland 		break;
622597d0caeSDavid Teigland 
62372c2be77SDavid Teigland 	case DLM_USER_PURGE:
62472c2be77SDavid Teigland 		if (!proc) {
62572c2be77SDavid Teigland 			log_print("no locking on control device");
626c6ca7bc9SDavid Teigland 			goto out_free;
62772c2be77SDavid Teigland 		}
62872c2be77SDavid Teigland 		error = device_user_purge(proc, &kbuf->i.purge);
62972c2be77SDavid Teigland 		break;
63072c2be77SDavid Teigland 
631597d0caeSDavid Teigland 	default:
632597d0caeSDavid Teigland 		log_print("Unknown command passed to DLM device : %d\n",
633597d0caeSDavid Teigland 			  kbuf->cmd);
634597d0caeSDavid Teigland 	}
635597d0caeSDavid Teigland 
636597d0caeSDavid Teigland  out_free:
637597d0caeSDavid Teigland 	kfree(kbuf);
638597d0caeSDavid Teigland 	return error;
639597d0caeSDavid Teigland }
640597d0caeSDavid Teigland 
641597d0caeSDavid Teigland /* Every process that opens the lockspace device has its own "proc" structure
642597d0caeSDavid Teigland    hanging off the open file that's used to keep track of locks owned by the
643597d0caeSDavid Teigland    process and asts that need to be delivered to the process. */
644597d0caeSDavid Teigland 
device_open(struct inode * inode,struct file * file)645597d0caeSDavid Teigland static int device_open(struct inode *inode, struct file *file)
646597d0caeSDavid Teigland {
647597d0caeSDavid Teigland 	struct dlm_user_proc *proc;
648597d0caeSDavid Teigland 	struct dlm_ls *ls;
649597d0caeSDavid Teigland 
650597d0caeSDavid Teigland 	ls = dlm_find_lockspace_device(iminor(inode));
651f9f2ed48SDavid Teigland 	if (!ls)
652597d0caeSDavid Teigland 		return -ENOENT;
653597d0caeSDavid Teigland 
654573c24c4SDavid Teigland 	proc = kzalloc(sizeof(struct dlm_user_proc), GFP_NOFS);
655597d0caeSDavid Teigland 	if (!proc) {
656597d0caeSDavid Teigland 		dlm_put_lockspace(ls);
657597d0caeSDavid Teigland 		return -ENOMEM;
658597d0caeSDavid Teigland 	}
659597d0caeSDavid Teigland 
660597d0caeSDavid Teigland 	proc->lockspace = ls->ls_local_handle;
661597d0caeSDavid Teigland 	INIT_LIST_HEAD(&proc->asts);
662597d0caeSDavid Teigland 	INIT_LIST_HEAD(&proc->locks);
663a1bc86e6SDavid Teigland 	INIT_LIST_HEAD(&proc->unlocking);
664597d0caeSDavid Teigland 	spin_lock_init(&proc->asts_spin);
665597d0caeSDavid Teigland 	spin_lock_init(&proc->locks_spin);
666597d0caeSDavid Teigland 	init_waitqueue_head(&proc->wait);
667597d0caeSDavid Teigland 	file->private_data = proc;
668597d0caeSDavid Teigland 
669597d0caeSDavid Teigland 	return 0;
670597d0caeSDavid Teigland }
671597d0caeSDavid Teigland 
device_close(struct inode * inode,struct file * file)672597d0caeSDavid Teigland static int device_close(struct inode *inode, struct file *file)
673597d0caeSDavid Teigland {
674597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
675597d0caeSDavid Teigland 	struct dlm_ls *ls;
676597d0caeSDavid Teigland 
677597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
678597d0caeSDavid Teigland 	if (!ls)
679597d0caeSDavid Teigland 		return -ENOENT;
680597d0caeSDavid Teigland 
681597d0caeSDavid Teigland 	set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags);
682597d0caeSDavid Teigland 
683597d0caeSDavid Teigland 	dlm_clear_proc_locks(ls, proc);
684597d0caeSDavid Teigland 
685597d0caeSDavid Teigland 	/* at this point no more lkb's should exist for this lockspace,
686597d0caeSDavid Teigland 	   so there's no chance of dlm_user_add_ast() being called and
687597d0caeSDavid Teigland 	   looking for lkb->ua->proc */
688597d0caeSDavid Teigland 
689597d0caeSDavid Teigland 	kfree(proc);
690597d0caeSDavid Teigland 	file->private_data = NULL;
691597d0caeSDavid Teigland 
692597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
693597d0caeSDavid Teigland 	dlm_put_lockspace(ls);  /* for the find in device_open() */
694597d0caeSDavid Teigland 
695597d0caeSDavid Teigland 	/* FIXME: AUTOFREE: if this ls is no longer used do
696597d0caeSDavid Teigland 	   device_remove_lockspace() */
697597d0caeSDavid Teigland 
698597d0caeSDavid Teigland 	return 0;
699597d0caeSDavid Teigland }
700597d0caeSDavid Teigland 
copy_result_to_user(struct dlm_user_args * ua,int compat,uint32_t flags,int mode,int copy_lvb,char __user * buf,size_t count)7018304d6f2SDavid Teigland static int copy_result_to_user(struct dlm_user_args *ua, int compat,
7028304d6f2SDavid Teigland 			       uint32_t flags, int mode, int copy_lvb,
7038304d6f2SDavid Teigland 			       char __user *buf, size_t count)
704597d0caeSDavid Teigland {
705597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
706597d0caeSDavid Teigland 	struct dlm_lock_result32 result32;
707597d0caeSDavid Teigland #endif
708597d0caeSDavid Teigland 	struct dlm_lock_result result;
709597d0caeSDavid Teigland 	void *resultptr;
710597d0caeSDavid Teigland 	int error=0;
711597d0caeSDavid Teigland 	int len;
712597d0caeSDavid Teigland 	int struct_len;
713597d0caeSDavid Teigland 
714597d0caeSDavid Teigland 	memset(&result, 0, sizeof(struct dlm_lock_result));
715d7db923eSDavid Teigland 	result.version[0] = DLM_DEVICE_VERSION_MAJOR;
716d7db923eSDavid Teigland 	result.version[1] = DLM_DEVICE_VERSION_MINOR;
717d7db923eSDavid Teigland 	result.version[2] = DLM_DEVICE_VERSION_PATCH;
7189de30f3fSTycho Andersen 	memcpy(&result.lksb, &ua->lksb, offsetof(struct dlm_lksb, sb_lvbptr));
719597d0caeSDavid Teigland 	result.user_lksb = ua->user_lksb;
720597d0caeSDavid Teigland 
721597d0caeSDavid Teigland 	/* FIXME: dlm1 provides for the user's bastparam/addr to not be updated
722597d0caeSDavid Teigland 	   in a conversion unless the conversion is successful.  See code
723597d0caeSDavid Teigland 	   in dlm_user_convert() for updating ua from ua_tmp.  OpenVMS, though,
724597d0caeSDavid Teigland 	   notes that a new blocking AST address and parameter are set even if
725597d0caeSDavid Teigland 	   the conversion fails, so maybe we should just do that. */
726597d0caeSDavid Teigland 
7278304d6f2SDavid Teigland 	if (flags & DLM_CB_BAST) {
728597d0caeSDavid Teigland 		result.user_astaddr = ua->bastaddr;
729597d0caeSDavid Teigland 		result.user_astparam = ua->bastparam;
73089d799d0SDavid Teigland 		result.bast_mode = mode;
731597d0caeSDavid Teigland 	} else {
732597d0caeSDavid Teigland 		result.user_astaddr = ua->castaddr;
733597d0caeSDavid Teigland 		result.user_astparam = ua->castparam;
734597d0caeSDavid Teigland 	}
735597d0caeSDavid Teigland 
736597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
737597d0caeSDavid Teigland 	if (compat)
738597d0caeSDavid Teigland 		len = sizeof(struct dlm_lock_result32);
739597d0caeSDavid Teigland 	else
740597d0caeSDavid Teigland #endif
741597d0caeSDavid Teigland 		len = sizeof(struct dlm_lock_result);
742597d0caeSDavid Teigland 	struct_len = len;
743597d0caeSDavid Teigland 
744597d0caeSDavid Teigland 	/* copy lvb to userspace if there is one, it's been updated, and
745597d0caeSDavid Teigland 	   the user buffer has space for it */
746597d0caeSDavid Teigland 
7478304d6f2SDavid Teigland 	if (copy_lvb && ua->lksb.sb_lvbptr && count >= len + DLM_USER_LVB_LEN) {
748597d0caeSDavid Teigland 		if (copy_to_user(buf+len, ua->lksb.sb_lvbptr,
749597d0caeSDavid Teigland 				 DLM_USER_LVB_LEN)) {
750597d0caeSDavid Teigland 			error = -EFAULT;
751597d0caeSDavid Teigland 			goto out;
752597d0caeSDavid Teigland 		}
753597d0caeSDavid Teigland 
754597d0caeSDavid Teigland 		result.lvb_offset = len;
755597d0caeSDavid Teigland 		len += DLM_USER_LVB_LEN;
756597d0caeSDavid Teigland 	}
757597d0caeSDavid Teigland 
758597d0caeSDavid Teigland 	result.length = len;
759597d0caeSDavid Teigland 	resultptr = &result;
760597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
761597d0caeSDavid Teigland 	if (compat) {
762597d0caeSDavid Teigland 		compat_output(&result, &result32);
763597d0caeSDavid Teigland 		resultptr = &result32;
764597d0caeSDavid Teigland 	}
765597d0caeSDavid Teigland #endif
766597d0caeSDavid Teigland 
767597d0caeSDavid Teigland 	if (copy_to_user(buf, resultptr, struct_len))
768597d0caeSDavid Teigland 		error = -EFAULT;
769597d0caeSDavid Teigland 	else
770597d0caeSDavid Teigland 		error = len;
771597d0caeSDavid Teigland  out:
772597d0caeSDavid Teigland 	return error;
773597d0caeSDavid Teigland }
774597d0caeSDavid Teigland 
copy_version_to_user(char __user * buf,size_t count)775d7db923eSDavid Teigland static int copy_version_to_user(char __user *buf, size_t count)
776d7db923eSDavid Teigland {
777d7db923eSDavid Teigland 	struct dlm_device_version ver;
778d7db923eSDavid Teigland 
779d7db923eSDavid Teigland 	memset(&ver, 0, sizeof(struct dlm_device_version));
780d7db923eSDavid Teigland 	ver.version[0] = DLM_DEVICE_VERSION_MAJOR;
781d7db923eSDavid Teigland 	ver.version[1] = DLM_DEVICE_VERSION_MINOR;
782d7db923eSDavid Teigland 	ver.version[2] = DLM_DEVICE_VERSION_PATCH;
783d7db923eSDavid Teigland 
784d7db923eSDavid Teigland 	if (copy_to_user(buf, &ver, sizeof(struct dlm_device_version)))
785d7db923eSDavid Teigland 		return -EFAULT;
786d7db923eSDavid Teigland 	return sizeof(struct dlm_device_version);
787d7db923eSDavid Teigland }
788d7db923eSDavid Teigland 
789597d0caeSDavid Teigland /* a read returns a single ast described in a struct dlm_lock_result */
790597d0caeSDavid Teigland 
device_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)791597d0caeSDavid Teigland static ssize_t device_read(struct file *file, char __user *buf, size_t count,
792597d0caeSDavid Teigland 			   loff_t *ppos)
793597d0caeSDavid Teigland {
794597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
795597d0caeSDavid Teigland 	DECLARE_WAITQUEUE(wait, current);
79661bed0baSAlexander Aring 	struct dlm_callback *cb;
797ad191e0eSAlexander Aring 	int rv, ret;
798597d0caeSDavid Teigland 
799d7db923eSDavid Teigland 	if (count == sizeof(struct dlm_device_version)) {
8008304d6f2SDavid Teigland 		rv = copy_version_to_user(buf, count);
8018304d6f2SDavid Teigland 		return rv;
802d7db923eSDavid Teigland 	}
803d7db923eSDavid Teigland 
804d7db923eSDavid Teigland 	if (!proc) {
805d7db923eSDavid Teigland 		log_print("non-version read from control device %zu", count);
806d7db923eSDavid Teigland 		return -EINVAL;
807d7db923eSDavid Teigland 	}
808d7db923eSDavid Teigland 
809597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
810597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_lock_result32))
811597d0caeSDavid Teigland #else
812597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_lock_result))
813597d0caeSDavid Teigland #endif
814597d0caeSDavid Teigland 		return -EINVAL;
815597d0caeSDavid Teigland 
816597d0caeSDavid Teigland 	/* do we really need this? can a read happen after a close? */
817597d0caeSDavid Teigland 	if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
818597d0caeSDavid Teigland 		return -EINVAL;
819597d0caeSDavid Teigland 
820*578acf9aSAlexander Aring 	spin_lock_bh(&proc->asts_spin);
821597d0caeSDavid Teigland 	if (list_empty(&proc->asts)) {
822597d0caeSDavid Teigland 		if (file->f_flags & O_NONBLOCK) {
823*578acf9aSAlexander Aring 			spin_unlock_bh(&proc->asts_spin);
824597d0caeSDavid Teigland 			return -EAGAIN;
825597d0caeSDavid Teigland 		}
826597d0caeSDavid Teigland 
827597d0caeSDavid Teigland 		add_wait_queue(&proc->wait, &wait);
828597d0caeSDavid Teigland 
829597d0caeSDavid Teigland 	repeat:
830597d0caeSDavid Teigland 		set_current_state(TASK_INTERRUPTIBLE);
831597d0caeSDavid Teigland 		if (list_empty(&proc->asts) && !signal_pending(current)) {
832*578acf9aSAlexander Aring 			spin_unlock_bh(&proc->asts_spin);
833597d0caeSDavid Teigland 			schedule();
834*578acf9aSAlexander Aring 			spin_lock_bh(&proc->asts_spin);
835597d0caeSDavid Teigland 			goto repeat;
836597d0caeSDavid Teigland 		}
837597d0caeSDavid Teigland 		set_current_state(TASK_RUNNING);
838597d0caeSDavid Teigland 		remove_wait_queue(&proc->wait, &wait);
839597d0caeSDavid Teigland 
840597d0caeSDavid Teigland 		if (signal_pending(current)) {
841*578acf9aSAlexander Aring 			spin_unlock_bh(&proc->asts_spin);
842597d0caeSDavid Teigland 			return -ERESTARTSYS;
843597d0caeSDavid Teigland 		}
844597d0caeSDavid Teigland 	}
845597d0caeSDavid Teigland 
8468304d6f2SDavid Teigland 	/* if we empty lkb_callbacks, we don't want to unlock the spinlock
84723e8e1aaSDavid Teigland 	   without removing lkb_cb_list; so empty lkb_cb_list is always
8488304d6f2SDavid Teigland 	   consistent with empty lkb_callbacks */
849597d0caeSDavid Teigland 
850986ae3c2SAlexander Aring 	cb = list_first_entry(&proc->asts, struct dlm_callback, list);
851986ae3c2SAlexander Aring 	list_del(&cb->list);
852*578acf9aSAlexander Aring 	spin_unlock_bh(&proc->asts_spin);
853597d0caeSDavid Teigland 
85461bed0baSAlexander Aring 	if (cb->flags & DLM_CB_BAST) {
855986ae3c2SAlexander Aring 		trace_dlm_bast(cb->ls_id, cb->lkb_id, cb->mode, cb->res_name,
856986ae3c2SAlexander Aring 			       cb->res_length);
85761bed0baSAlexander Aring 	} else if (cb->flags & DLM_CB_CAST) {
858986ae3c2SAlexander Aring 		cb->lkb_lksb->sb_status = cb->sb_status;
859986ae3c2SAlexander Aring 		cb->lkb_lksb->sb_flags = cb->sb_flags;
860986ae3c2SAlexander Aring 		trace_dlm_ast(cb->ls_id, cb->lkb_id, cb->sb_status,
861986ae3c2SAlexander Aring 			      cb->sb_flags, cb->res_name, cb->res_length);
8628304d6f2SDavid Teigland 	}
8638304d6f2SDavid Teigland 
864986ae3c2SAlexander Aring 	ret = copy_result_to_user(&cb->ua,
8658304d6f2SDavid Teigland 				  test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
866ad191e0eSAlexander Aring 				  cb->flags, cb->mode, cb->copy_lvb, buf, count);
8672bec1bbdSAlexander Aring 	dlm_free_cb(cb);
8682ab3d705SAlexander Aring 	return ret;
869597d0caeSDavid Teigland }
870597d0caeSDavid Teigland 
device_poll(struct file * file,poll_table * wait)871076ccb76SAl Viro static __poll_t device_poll(struct file *file, poll_table *wait)
872597d0caeSDavid Teigland {
873597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
874597d0caeSDavid Teigland 
875597d0caeSDavid Teigland 	poll_wait(file, &proc->wait, wait);
876597d0caeSDavid Teigland 
877*578acf9aSAlexander Aring 	spin_lock_bh(&proc->asts_spin);
878597d0caeSDavid Teigland 	if (!list_empty(&proc->asts)) {
879*578acf9aSAlexander Aring 		spin_unlock_bh(&proc->asts_spin);
880a9a08845SLinus Torvalds 		return EPOLLIN | EPOLLRDNORM;
881597d0caeSDavid Teigland 	}
882*578acf9aSAlexander Aring 	spin_unlock_bh(&proc->asts_spin);
883597d0caeSDavid Teigland 	return 0;
884597d0caeSDavid Teigland }
885597d0caeSDavid Teigland 
dlm_user_daemon_available(void)886dc68c7edSDavid Teigland int dlm_user_daemon_available(void)
887dc68c7edSDavid Teigland {
888dc68c7edSDavid Teigland 	/* dlm_controld hasn't started (or, has started, but not
889dc68c7edSDavid Teigland 	   properly populated configfs) */
890dc68c7edSDavid Teigland 
891dc68c7edSDavid Teigland 	if (!dlm_our_nodeid())
892dc68c7edSDavid Teigland 		return 0;
893dc68c7edSDavid Teigland 
894dc68c7edSDavid Teigland 	/* This is to deal with versions of dlm_controld that don't
895dc68c7edSDavid Teigland 	   know about the monitor device.  We assume that if the
896dc68c7edSDavid Teigland 	   dlm_controld was started (above), but the monitor device
897dc68c7edSDavid Teigland 	   was never opened, that it's an old version.  dlm_controld
898dc68c7edSDavid Teigland 	   should open the monitor device before populating configfs. */
899dc68c7edSDavid Teigland 
900dc68c7edSDavid Teigland 	if (dlm_monitor_unused)
901dc68c7edSDavid Teigland 		return 1;
902dc68c7edSDavid Teigland 
903dc68c7edSDavid Teigland 	return atomic_read(&dlm_monitor_opened) ? 1 : 0;
904dc68c7edSDavid Teigland }
905dc68c7edSDavid Teigland 
ctl_device_open(struct inode * inode,struct file * file)906597d0caeSDavid Teigland static int ctl_device_open(struct inode *inode, struct file *file)
907597d0caeSDavid Teigland {
908597d0caeSDavid Teigland 	file->private_data = NULL;
909597d0caeSDavid Teigland 	return 0;
910597d0caeSDavid Teigland }
911597d0caeSDavid Teigland 
ctl_device_close(struct inode * inode,struct file * file)912597d0caeSDavid Teigland static int ctl_device_close(struct inode *inode, struct file *file)
913597d0caeSDavid Teigland {
914597d0caeSDavid Teigland 	return 0;
915597d0caeSDavid Teigland }
916597d0caeSDavid Teigland 
monitor_device_open(struct inode * inode,struct file * file)917dc68c7edSDavid Teigland static int monitor_device_open(struct inode *inode, struct file *file)
918dc68c7edSDavid Teigland {
919dc68c7edSDavid Teigland 	atomic_inc(&dlm_monitor_opened);
920dc68c7edSDavid Teigland 	dlm_monitor_unused = 0;
921dc68c7edSDavid Teigland 	return 0;
922dc68c7edSDavid Teigland }
923dc68c7edSDavid Teigland 
monitor_device_close(struct inode * inode,struct file * file)924dc68c7edSDavid Teigland static int monitor_device_close(struct inode *inode, struct file *file)
925dc68c7edSDavid Teigland {
926dc68c7edSDavid Teigland 	if (atomic_dec_and_test(&dlm_monitor_opened))
927dc68c7edSDavid Teigland 		dlm_stop_lockspaces();
928dc68c7edSDavid Teigland 	return 0;
929dc68c7edSDavid Teigland }
930dc68c7edSDavid Teigland 
93100977a59SArjan van de Ven static const struct file_operations device_fops = {
932597d0caeSDavid Teigland 	.open    = device_open,
933597d0caeSDavid Teigland 	.release = device_close,
934597d0caeSDavid Teigland 	.read    = device_read,
935597d0caeSDavid Teigland 	.write   = device_write,
936597d0caeSDavid Teigland 	.poll    = device_poll,
937597d0caeSDavid Teigland 	.owner   = THIS_MODULE,
9386038f373SArnd Bergmann 	.llseek  = noop_llseek,
939597d0caeSDavid Teigland };
940597d0caeSDavid Teigland 
94100977a59SArjan van de Ven static const struct file_operations ctl_device_fops = {
942597d0caeSDavid Teigland 	.open    = ctl_device_open,
943597d0caeSDavid Teigland 	.release = ctl_device_close,
944d7db923eSDavid Teigland 	.read    = device_read,
945597d0caeSDavid Teigland 	.write   = device_write,
946597d0caeSDavid Teigland 	.owner   = THIS_MODULE,
9476038f373SArnd Bergmann 	.llseek  = noop_llseek,
948597d0caeSDavid Teigland };
949597d0caeSDavid Teigland 
9500fe410d3SDenis Cheng static struct miscdevice ctl_device = {
9510fe410d3SDenis Cheng 	.name  = "dlm-control",
9520fe410d3SDenis Cheng 	.fops  = &ctl_device_fops,
9530fe410d3SDenis Cheng 	.minor = MISC_DYNAMIC_MINOR,
9540fe410d3SDenis Cheng };
9550fe410d3SDenis Cheng 
956dc68c7edSDavid Teigland static const struct file_operations monitor_device_fops = {
957dc68c7edSDavid Teigland 	.open    = monitor_device_open,
958dc68c7edSDavid Teigland 	.release = monitor_device_close,
959dc68c7edSDavid Teigland 	.owner   = THIS_MODULE,
9606038f373SArnd Bergmann 	.llseek  = noop_llseek,
961dc68c7edSDavid Teigland };
962dc68c7edSDavid Teigland 
963dc68c7edSDavid Teigland static struct miscdevice monitor_device = {
964dc68c7edSDavid Teigland 	.name  = "dlm-monitor",
965dc68c7edSDavid Teigland 	.fops  = &monitor_device_fops,
966dc68c7edSDavid Teigland 	.minor = MISC_DYNAMIC_MINOR,
967dc68c7edSDavid Teigland };
968dc68c7edSDavid Teigland 
dlm_user_init(void)96930727174SDenis Cheng int __init dlm_user_init(void)
970597d0caeSDavid Teigland {
971597d0caeSDavid Teigland 	int error;
972597d0caeSDavid Teigland 
973dc68c7edSDavid Teigland 	atomic_set(&dlm_monitor_opened, 0);
974597d0caeSDavid Teigland 
975dc68c7edSDavid Teigland 	error = misc_register(&ctl_device);
976dc68c7edSDavid Teigland 	if (error) {
977dc68c7edSDavid Teigland 		log_print("misc_register failed for control device");
978dc68c7edSDavid Teigland 		goto out;
979dc68c7edSDavid Teigland 	}
980dc68c7edSDavid Teigland 
981dc68c7edSDavid Teigland 	error = misc_register(&monitor_device);
982dc68c7edSDavid Teigland 	if (error) {
983dc68c7edSDavid Teigland 		log_print("misc_register failed for monitor device");
984dc68c7edSDavid Teigland 		misc_deregister(&ctl_device);
985dc68c7edSDavid Teigland 	}
986dc68c7edSDavid Teigland  out:
987597d0caeSDavid Teigland 	return error;
988597d0caeSDavid Teigland }
989597d0caeSDavid Teigland 
dlm_user_exit(void)990597d0caeSDavid Teigland void dlm_user_exit(void)
991597d0caeSDavid Teigland {
992597d0caeSDavid Teigland 	misc_deregister(&ctl_device);
993dc68c7edSDavid Teigland 	misc_deregister(&monitor_device);
994597d0caeSDavid Teigland }
995597d0caeSDavid Teigland 
996