xref: /linux/fs/dlm/user.c (revision 72c2be77)
1597d0caeSDavid Teigland /*
2ef0c2bb0SDavid Teigland  * Copyright (C) 2006-2007 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>
20597d0caeSDavid Teigland 
21597d0caeSDavid Teigland #include "dlm_internal.h"
22597d0caeSDavid Teigland #include "lockspace.h"
23597d0caeSDavid Teigland #include "lock.h"
24597d0caeSDavid Teigland #include "lvb_table.h"
2584c6e8cdSAdrian Bunk #include "user.h"
26597d0caeSDavid Teigland 
27597d0caeSDavid Teigland static const char *name_prefix="dlm";
28597d0caeSDavid Teigland static struct miscdevice ctl_device;
2900977a59SArjan van de Ven static const struct file_operations device_fops;
30597d0caeSDavid Teigland 
31597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
32597d0caeSDavid Teigland 
33597d0caeSDavid Teigland struct dlm_lock_params32 {
34597d0caeSDavid Teigland 	__u8 mode;
35597d0caeSDavid Teigland 	__u8 namelen;
36597d0caeSDavid Teigland 	__u16 flags;
37597d0caeSDavid Teigland 	__u32 lkid;
38597d0caeSDavid Teigland 	__u32 parent;
39597d0caeSDavid Teigland 
40597d0caeSDavid Teigland 	__u32 castparam;
41597d0caeSDavid Teigland 	__u32 castaddr;
42597d0caeSDavid Teigland 	__u32 bastparam;
43597d0caeSDavid Teigland 	__u32 bastaddr;
44597d0caeSDavid Teigland 	__u32 lksb;
45597d0caeSDavid Teigland 
46597d0caeSDavid Teigland 	char lvb[DLM_USER_LVB_LEN];
47597d0caeSDavid Teigland 	char name[0];
48597d0caeSDavid Teigland };
49597d0caeSDavid Teigland 
50597d0caeSDavid Teigland struct dlm_write_request32 {
51597d0caeSDavid Teigland 	__u32 version[3];
52597d0caeSDavid Teigland 	__u8 cmd;
53597d0caeSDavid Teigland 	__u8 is64bit;
54597d0caeSDavid Teigland 	__u8 unused[2];
55597d0caeSDavid Teigland 
56597d0caeSDavid Teigland 	union  {
57597d0caeSDavid Teigland 		struct dlm_lock_params32 lock;
58597d0caeSDavid Teigland 		struct dlm_lspace_params lspace;
59*72c2be77SDavid Teigland 		struct dlm_purge_params purge;
60597d0caeSDavid Teigland 	} i;
61597d0caeSDavid Teigland };
62597d0caeSDavid Teigland 
63597d0caeSDavid Teigland struct dlm_lksb32 {
64597d0caeSDavid Teigland 	__u32 sb_status;
65597d0caeSDavid Teigland 	__u32 sb_lkid;
66597d0caeSDavid Teigland 	__u8 sb_flags;
67597d0caeSDavid Teigland 	__u32 sb_lvbptr;
68597d0caeSDavid Teigland };
69597d0caeSDavid Teigland 
70597d0caeSDavid Teigland struct dlm_lock_result32 {
71597d0caeSDavid Teigland 	__u32 length;
72597d0caeSDavid Teigland 	__u32 user_astaddr;
73597d0caeSDavid Teigland 	__u32 user_astparam;
74597d0caeSDavid Teigland 	__u32 user_lksb;
75597d0caeSDavid Teigland 	struct dlm_lksb32 lksb;
76597d0caeSDavid Teigland 	__u8 bast_mode;
77597d0caeSDavid Teigland 	__u8 unused[3];
78597d0caeSDavid Teigland 	/* Offsets may be zero if no data is present */
79597d0caeSDavid Teigland 	__u32 lvb_offset;
80597d0caeSDavid Teigland };
81597d0caeSDavid Teigland 
82597d0caeSDavid Teigland static void compat_input(struct dlm_write_request *kb,
83597d0caeSDavid Teigland 			 struct dlm_write_request32 *kb32)
84597d0caeSDavid Teigland {
85597d0caeSDavid Teigland 	kb->version[0] = kb32->version[0];
86597d0caeSDavid Teigland 	kb->version[1] = kb32->version[1];
87597d0caeSDavid Teigland 	kb->version[2] = kb32->version[2];
88597d0caeSDavid Teigland 
89597d0caeSDavid Teigland 	kb->cmd = kb32->cmd;
90597d0caeSDavid Teigland 	kb->is64bit = kb32->is64bit;
91597d0caeSDavid Teigland 	if (kb->cmd == DLM_USER_CREATE_LOCKSPACE ||
92597d0caeSDavid Teigland 	    kb->cmd == DLM_USER_REMOVE_LOCKSPACE) {
93597d0caeSDavid Teigland 		kb->i.lspace.flags = kb32->i.lspace.flags;
94597d0caeSDavid Teigland 		kb->i.lspace.minor = kb32->i.lspace.minor;
95597d0caeSDavid Teigland 		strcpy(kb->i.lspace.name, kb32->i.lspace.name);
96*72c2be77SDavid Teigland 	} else if (kb->cmd == DLM_USER_PURGE) {
97*72c2be77SDavid Teigland 		kb->i.purge.nodeid = kb32->i.purge.nodeid;
98*72c2be77SDavid Teigland 		kb->i.purge.pid = kb32->i.purge.pid;
99597d0caeSDavid Teigland 	} else {
100597d0caeSDavid Teigland 		kb->i.lock.mode = kb32->i.lock.mode;
101597d0caeSDavid Teigland 		kb->i.lock.namelen = kb32->i.lock.namelen;
102597d0caeSDavid Teigland 		kb->i.lock.flags = kb32->i.lock.flags;
103597d0caeSDavid Teigland 		kb->i.lock.lkid = kb32->i.lock.lkid;
104597d0caeSDavid Teigland 		kb->i.lock.parent = kb32->i.lock.parent;
105597d0caeSDavid Teigland 		kb->i.lock.castparam = (void *)(long)kb32->i.lock.castparam;
106597d0caeSDavid Teigland 		kb->i.lock.castaddr = (void *)(long)kb32->i.lock.castaddr;
107597d0caeSDavid Teigland 		kb->i.lock.bastparam = (void *)(long)kb32->i.lock.bastparam;
108597d0caeSDavid Teigland 		kb->i.lock.bastaddr = (void *)(long)kb32->i.lock.bastaddr;
109597d0caeSDavid Teigland 		kb->i.lock.lksb = (void *)(long)kb32->i.lock.lksb;
110597d0caeSDavid Teigland 		memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN);
111597d0caeSDavid Teigland 		memcpy(kb->i.lock.name, kb32->i.lock.name, kb->i.lock.namelen);
112597d0caeSDavid Teigland 	}
113597d0caeSDavid Teigland }
114597d0caeSDavid Teigland 
115597d0caeSDavid Teigland static void compat_output(struct dlm_lock_result *res,
116597d0caeSDavid Teigland 			  struct dlm_lock_result32 *res32)
117597d0caeSDavid Teigland {
118597d0caeSDavid Teigland 	res32->length = res->length - (sizeof(struct dlm_lock_result) -
119597d0caeSDavid Teigland 				       sizeof(struct dlm_lock_result32));
120597d0caeSDavid Teigland 	res32->user_astaddr = (__u32)(long)res->user_astaddr;
121597d0caeSDavid Teigland 	res32->user_astparam = (__u32)(long)res->user_astparam;
122597d0caeSDavid Teigland 	res32->user_lksb = (__u32)(long)res->user_lksb;
123597d0caeSDavid Teigland 	res32->bast_mode = res->bast_mode;
124597d0caeSDavid Teigland 
125597d0caeSDavid Teigland 	res32->lvb_offset = res->lvb_offset;
126597d0caeSDavid Teigland 	res32->length = res->length;
127597d0caeSDavid Teigland 
128597d0caeSDavid Teigland 	res32->lksb.sb_status = res->lksb.sb_status;
129597d0caeSDavid Teigland 	res32->lksb.sb_flags = res->lksb.sb_flags;
130597d0caeSDavid Teigland 	res32->lksb.sb_lkid = res->lksb.sb_lkid;
131597d0caeSDavid Teigland 	res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr;
132597d0caeSDavid Teigland }
133597d0caeSDavid Teigland #endif
134597d0caeSDavid Teigland 
135ef0c2bb0SDavid Teigland /* we could possibly check if the cancel of an orphan has resulted in the lkb
136ef0c2bb0SDavid Teigland    being removed and then remove that lkb from the orphans list and free it */
137597d0caeSDavid Teigland 
138597d0caeSDavid Teigland void dlm_user_add_ast(struct dlm_lkb *lkb, int type)
139597d0caeSDavid Teigland {
140597d0caeSDavid Teigland 	struct dlm_ls *ls;
141597d0caeSDavid Teigland 	struct dlm_user_args *ua;
142597d0caeSDavid Teigland 	struct dlm_user_proc *proc;
143ef0c2bb0SDavid Teigland 	int eol = 0, ast_type;
144597d0caeSDavid Teigland 
145ef0c2bb0SDavid Teigland 	if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD))
146597d0caeSDavid Teigland 		return;
147597d0caeSDavid Teigland 
148597d0caeSDavid Teigland 	ls = lkb->lkb_resource->res_ls;
149597d0caeSDavid Teigland 	mutex_lock(&ls->ls_clear_proc_locks);
150597d0caeSDavid Teigland 
151597d0caeSDavid Teigland 	/* If ORPHAN/DEAD flag is set, it means the process is dead so an ast
152597d0caeSDavid Teigland 	   can't be delivered.  For ORPHAN's, dlm_clear_proc_locks() freed
153ef0c2bb0SDavid Teigland 	   lkb->ua so we can't try to use it.  This second check is necessary
154ef0c2bb0SDavid Teigland 	   for cases where a completion ast is received for an operation that
155ef0c2bb0SDavid Teigland 	   began before clear_proc_locks did its cancel/unlock. */
156597d0caeSDavid Teigland 
157ef0c2bb0SDavid Teigland 	if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD))
158597d0caeSDavid Teigland 		goto out;
159597d0caeSDavid Teigland 
160597d0caeSDavid Teigland 	DLM_ASSERT(lkb->lkb_astparam, dlm_print_lkb(lkb););
161597d0caeSDavid Teigland 	ua = (struct dlm_user_args *)lkb->lkb_astparam;
162597d0caeSDavid Teigland 	proc = ua->proc;
163597d0caeSDavid Teigland 
164597d0caeSDavid Teigland 	if (type == AST_BAST && ua->bastaddr == NULL)
165597d0caeSDavid Teigland 		goto out;
166597d0caeSDavid Teigland 
167597d0caeSDavid Teigland 	spin_lock(&proc->asts_spin);
168ef0c2bb0SDavid Teigland 
169ef0c2bb0SDavid Teigland 	ast_type = lkb->lkb_ast_type;
170ef0c2bb0SDavid Teigland 	lkb->lkb_ast_type |= type;
171ef0c2bb0SDavid Teigland 
172ef0c2bb0SDavid Teigland 	if (!ast_type) {
173597d0caeSDavid Teigland 		kref_get(&lkb->lkb_ref);
174597d0caeSDavid Teigland 		list_add_tail(&lkb->lkb_astqueue, &proc->asts);
175597d0caeSDavid Teigland 		wake_up_interruptible(&proc->wait);
176597d0caeSDavid Teigland 	}
177ef0c2bb0SDavid Teigland 	if (type == AST_COMP && (ast_type & AST_COMP))
178ef0c2bb0SDavid Teigland 		log_debug(ls, "ast overlap %x status %x %x",
179ef0c2bb0SDavid Teigland 			  lkb->lkb_id, ua->lksb.sb_status, lkb->lkb_flags);
180597d0caeSDavid Teigland 
181ef0c2bb0SDavid Teigland 	/* Figure out if this lock is at the end of its life and no longer
182ef0c2bb0SDavid Teigland 	   available for the application to use.  The lkb still exists until
183ef0c2bb0SDavid Teigland 	   the final ast is read.  A lock becomes EOL in three situations:
184ef0c2bb0SDavid Teigland 	     1. a noqueue request fails with EAGAIN
185ef0c2bb0SDavid Teigland 	     2. an unlock completes with EUNLOCK
186ef0c2bb0SDavid Teigland 	     3. a cancel of a waiting request completes with ECANCEL
187ef0c2bb0SDavid Teigland 	   An EOL lock needs to be removed from the process's list of locks.
188ef0c2bb0SDavid Teigland 	   And we can't allow any new operation on an EOL lock.  This is
189ef0c2bb0SDavid Teigland 	   not related to the lifetime of the lkb struct which is managed
190ef0c2bb0SDavid Teigland 	   entirely by refcount. */
19134e22bedSDavid Teigland 
192ef0c2bb0SDavid Teigland 	if (type == AST_COMP &&
193ef0c2bb0SDavid Teigland 	    lkb->lkb_grmode == DLM_LOCK_IV &&
194ef0c2bb0SDavid Teigland 	    ua->lksb.sb_status == -EAGAIN)
195ef0c2bb0SDavid Teigland 		eol = 1;
196ef0c2bb0SDavid Teigland 	else if (ua->lksb.sb_status == -DLM_EUNLOCK ||
197a1bc86e6SDavid Teigland 	    (ua->lksb.sb_status == -DLM_ECANCEL &&
198a1bc86e6SDavid Teigland 	     lkb->lkb_grmode == DLM_LOCK_IV))
199ef0c2bb0SDavid Teigland 		eol = 1;
200ef0c2bb0SDavid Teigland 	if (eol) {
201ef0c2bb0SDavid Teigland 		lkb->lkb_ast_type &= ~AST_BAST;
202ef0c2bb0SDavid Teigland 		lkb->lkb_flags |= DLM_IFL_ENDOFLIFE;
203ef0c2bb0SDavid Teigland 	}
204a1bc86e6SDavid Teigland 
205597d0caeSDavid Teigland 	/* We want to copy the lvb to userspace when the completion
206597d0caeSDavid Teigland 	   ast is read if the status is 0, the lock has an lvb and
207597d0caeSDavid Teigland 	   lvb_ops says we should.  We could probably have set_lvb_lock()
208597d0caeSDavid Teigland 	   set update_user_lvb instead and not need old_mode */
209597d0caeSDavid Teigland 
210597d0caeSDavid Teigland 	if ((lkb->lkb_ast_type & AST_COMP) &&
211597d0caeSDavid Teigland 	    (lkb->lkb_lksb->sb_status == 0) &&
212597d0caeSDavid Teigland 	    lkb->lkb_lksb->sb_lvbptr &&
213597d0caeSDavid Teigland 	    dlm_lvb_operations[ua->old_mode + 1][lkb->lkb_grmode + 1])
214597d0caeSDavid Teigland 		ua->update_user_lvb = 1;
215597d0caeSDavid Teigland 	else
216597d0caeSDavid Teigland 		ua->update_user_lvb = 0;
217597d0caeSDavid Teigland 
218597d0caeSDavid Teigland 	spin_unlock(&proc->asts_spin);
21934e22bedSDavid Teigland 
220ef0c2bb0SDavid Teigland 	if (eol) {
22134e22bedSDavid Teigland 		spin_lock(&ua->proc->locks_spin);
222ef0c2bb0SDavid Teigland 		if (!list_empty(&lkb->lkb_ownqueue)) {
22334e22bedSDavid Teigland 			list_del_init(&lkb->lkb_ownqueue);
22434e22bedSDavid Teigland 			dlm_put_lkb(lkb);
22534e22bedSDavid Teigland 		}
226ef0c2bb0SDavid Teigland 		spin_unlock(&ua->proc->locks_spin);
227ef0c2bb0SDavid Teigland 	}
228597d0caeSDavid Teigland  out:
229597d0caeSDavid Teigland 	mutex_unlock(&ls->ls_clear_proc_locks);
230597d0caeSDavid Teigland }
231597d0caeSDavid Teigland 
232597d0caeSDavid Teigland static int device_user_lock(struct dlm_user_proc *proc,
233597d0caeSDavid Teigland 			    struct dlm_lock_params *params)
234597d0caeSDavid Teigland {
235597d0caeSDavid Teigland 	struct dlm_ls *ls;
236597d0caeSDavid Teigland 	struct dlm_user_args *ua;
237597d0caeSDavid Teigland 	int error = -ENOMEM;
238597d0caeSDavid Teigland 
239597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
240597d0caeSDavid Teigland 	if (!ls)
241597d0caeSDavid Teigland 		return -ENOENT;
242597d0caeSDavid Teigland 
243597d0caeSDavid Teigland 	if (!params->castaddr || !params->lksb) {
244597d0caeSDavid Teigland 		error = -EINVAL;
245597d0caeSDavid Teigland 		goto out;
246597d0caeSDavid Teigland 	}
247597d0caeSDavid Teigland 
248597d0caeSDavid Teigland 	ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL);
249597d0caeSDavid Teigland 	if (!ua)
250597d0caeSDavid Teigland 		goto out;
251597d0caeSDavid Teigland 	ua->proc = proc;
252597d0caeSDavid Teigland 	ua->user_lksb = params->lksb;
253597d0caeSDavid Teigland 	ua->castparam = params->castparam;
254597d0caeSDavid Teigland 	ua->castaddr = params->castaddr;
255597d0caeSDavid Teigland 	ua->bastparam = params->bastparam;
256597d0caeSDavid Teigland 	ua->bastaddr = params->bastaddr;
257597d0caeSDavid Teigland 
258597d0caeSDavid Teigland 	if (params->flags & DLM_LKF_CONVERT)
259597d0caeSDavid Teigland 		error = dlm_user_convert(ls, ua,
260597d0caeSDavid Teigland 				         params->mode, params->flags,
261597d0caeSDavid Teigland 				         params->lkid, params->lvb);
262597d0caeSDavid Teigland 	else {
263597d0caeSDavid Teigland 		error = dlm_user_request(ls, ua,
264597d0caeSDavid Teigland 					 params->mode, params->flags,
265597d0caeSDavid Teigland 					 params->name, params->namelen,
266597d0caeSDavid Teigland 					 params->parent);
267597d0caeSDavid Teigland 		if (!error)
268597d0caeSDavid Teigland 			error = ua->lksb.sb_lkid;
269597d0caeSDavid Teigland 	}
270597d0caeSDavid Teigland  out:
271597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
272597d0caeSDavid Teigland 	return error;
273597d0caeSDavid Teigland }
274597d0caeSDavid Teigland 
275597d0caeSDavid Teigland static int device_user_unlock(struct dlm_user_proc *proc,
276597d0caeSDavid Teigland 			      struct dlm_lock_params *params)
277597d0caeSDavid Teigland {
278597d0caeSDavid Teigland 	struct dlm_ls *ls;
279597d0caeSDavid Teigland 	struct dlm_user_args *ua;
280597d0caeSDavid Teigland 	int error = -ENOMEM;
281597d0caeSDavid Teigland 
282597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
283597d0caeSDavid Teigland 	if (!ls)
284597d0caeSDavid Teigland 		return -ENOENT;
285597d0caeSDavid Teigland 
286597d0caeSDavid Teigland 	ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL);
287597d0caeSDavid Teigland 	if (!ua)
288597d0caeSDavid Teigland 		goto out;
289597d0caeSDavid Teigland 	ua->proc = proc;
290597d0caeSDavid Teigland 	ua->user_lksb = params->lksb;
291597d0caeSDavid Teigland 	ua->castparam = params->castparam;
292597d0caeSDavid Teigland 	ua->castaddr = params->castaddr;
293597d0caeSDavid Teigland 
294597d0caeSDavid Teigland 	if (params->flags & DLM_LKF_CANCEL)
295597d0caeSDavid Teigland 		error = dlm_user_cancel(ls, ua, params->flags, params->lkid);
296597d0caeSDavid Teigland 	else
297597d0caeSDavid Teigland 		error = dlm_user_unlock(ls, ua, params->flags, params->lkid,
298597d0caeSDavid Teigland 					params->lvb);
299597d0caeSDavid Teigland  out:
300597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
301597d0caeSDavid Teigland 	return error;
302597d0caeSDavid Teigland }
303597d0caeSDavid Teigland 
304254da030SPatrick Caulfield static int create_misc_device(struct dlm_ls *ls, char *name)
305254da030SPatrick Caulfield {
306254da030SPatrick Caulfield 	int error, len;
307254da030SPatrick Caulfield 
308254da030SPatrick Caulfield 	error = -ENOMEM;
309254da030SPatrick Caulfield 	len = strlen(name) + strlen(name_prefix) + 2;
310254da030SPatrick Caulfield 	ls->ls_device.name = kzalloc(len, GFP_KERNEL);
311254da030SPatrick Caulfield 	if (!ls->ls_device.name)
312254da030SPatrick Caulfield 		goto fail;
313254da030SPatrick Caulfield 
314254da030SPatrick Caulfield 	snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix,
315254da030SPatrick Caulfield 		 name);
316254da030SPatrick Caulfield 	ls->ls_device.fops = &device_fops;
317254da030SPatrick Caulfield 	ls->ls_device.minor = MISC_DYNAMIC_MINOR;
318254da030SPatrick Caulfield 
319254da030SPatrick Caulfield 	error = misc_register(&ls->ls_device);
320254da030SPatrick Caulfield 	if (error) {
321254da030SPatrick Caulfield 		kfree(ls->ls_device.name);
322254da030SPatrick Caulfield 	}
323254da030SPatrick Caulfield fail:
324254da030SPatrick Caulfield 	return error;
325254da030SPatrick Caulfield }
326254da030SPatrick Caulfield 
327*72c2be77SDavid Teigland static int device_user_purge(struct dlm_user_proc *proc,
328*72c2be77SDavid Teigland 			     struct dlm_purge_params *params)
329*72c2be77SDavid Teigland {
330*72c2be77SDavid Teigland 	struct dlm_ls *ls;
331*72c2be77SDavid Teigland 	int error;
332*72c2be77SDavid Teigland 
333*72c2be77SDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
334*72c2be77SDavid Teigland 	if (!ls)
335*72c2be77SDavid Teigland 		return -ENOENT;
336*72c2be77SDavid Teigland 
337*72c2be77SDavid Teigland 	error = dlm_user_purge(ls, proc, params->nodeid, params->pid);
338*72c2be77SDavid Teigland 
339*72c2be77SDavid Teigland 	dlm_put_lockspace(ls);
340*72c2be77SDavid Teigland 	return error;
341*72c2be77SDavid Teigland }
342*72c2be77SDavid Teigland 
343597d0caeSDavid Teigland static int device_create_lockspace(struct dlm_lspace_params *params)
344597d0caeSDavid Teigland {
345597d0caeSDavid Teigland 	dlm_lockspace_t *lockspace;
346597d0caeSDavid Teigland 	struct dlm_ls *ls;
347254da030SPatrick Caulfield 	int error;
348597d0caeSDavid Teigland 
349597d0caeSDavid Teigland 	if (!capable(CAP_SYS_ADMIN))
350597d0caeSDavid Teigland 		return -EPERM;
351597d0caeSDavid Teigland 
352597d0caeSDavid Teigland 	error = dlm_new_lockspace(params->name, strlen(params->name),
353597d0caeSDavid Teigland 				  &lockspace, 0, DLM_USER_LVB_LEN);
354597d0caeSDavid Teigland 	if (error)
355597d0caeSDavid Teigland 		return error;
356597d0caeSDavid Teigland 
357597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(lockspace);
358597d0caeSDavid Teigland 	if (!ls)
359597d0caeSDavid Teigland 		return -ENOENT;
360597d0caeSDavid Teigland 
361254da030SPatrick Caulfield 	error = create_misc_device(ls, params->name);
362597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
363597d0caeSDavid Teigland 
364254da030SPatrick Caulfield 	if (error)
365597d0caeSDavid Teigland 		dlm_release_lockspace(lockspace, 0);
366254da030SPatrick Caulfield 	else
367254da030SPatrick Caulfield 		error = ls->ls_device.minor;
368254da030SPatrick Caulfield 
369597d0caeSDavid Teigland 	return error;
370597d0caeSDavid Teigland }
371597d0caeSDavid Teigland 
372597d0caeSDavid Teigland static int device_remove_lockspace(struct dlm_lspace_params *params)
373597d0caeSDavid Teigland {
374597d0caeSDavid Teigland 	dlm_lockspace_t *lockspace;
375597d0caeSDavid Teigland 	struct dlm_ls *ls;
376c6e6f0baSDavid Teigland 	int error, force = 0;
377597d0caeSDavid Teigland 
378597d0caeSDavid Teigland 	if (!capable(CAP_SYS_ADMIN))
379597d0caeSDavid Teigland 		return -EPERM;
380597d0caeSDavid Teigland 
381597d0caeSDavid Teigland 	ls = dlm_find_lockspace_device(params->minor);
382597d0caeSDavid Teigland 	if (!ls)
383597d0caeSDavid Teigland 		return -ENOENT;
384597d0caeSDavid Teigland 
385254da030SPatrick Caulfield 	/* Deregister the misc device first, so we don't have
386254da030SPatrick Caulfield 	 * a device that's not attached to a lockspace. If
387254da030SPatrick Caulfield 	 * dlm_release_lockspace fails then we can recreate it
388254da030SPatrick Caulfield 	 */
389597d0caeSDavid Teigland 	error = misc_deregister(&ls->ls_device);
390597d0caeSDavid Teigland 	if (error) {
391597d0caeSDavid Teigland 		dlm_put_lockspace(ls);
392597d0caeSDavid Teigland 		goto out;
393597d0caeSDavid Teigland 	}
394597d0caeSDavid Teigland 	kfree(ls->ls_device.name);
395597d0caeSDavid Teigland 
396c6e6f0baSDavid Teigland 	if (params->flags & DLM_USER_LSFLG_FORCEFREE)
397c6e6f0baSDavid Teigland 		force = 2;
398c6e6f0baSDavid Teigland 
399597d0caeSDavid Teigland 	lockspace = ls->ls_local_handle;
400597d0caeSDavid Teigland 
401597d0caeSDavid Teigland 	/* dlm_release_lockspace waits for references to go to zero,
402597d0caeSDavid Teigland 	   so all processes will need to close their device for the ls
403597d0caeSDavid Teigland 	   before the release will procede */
404597d0caeSDavid Teigland 
405597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
406c6e6f0baSDavid Teigland 	error = dlm_release_lockspace(lockspace, force);
407254da030SPatrick Caulfield 	if (error)
408254da030SPatrick Caulfield 		create_misc_device(ls, ls->ls_name);
409597d0caeSDavid Teigland  out:
410597d0caeSDavid Teigland 	return error;
411597d0caeSDavid Teigland }
412597d0caeSDavid Teigland 
413597d0caeSDavid Teigland /* Check the user's version matches ours */
414597d0caeSDavid Teigland static int check_version(struct dlm_write_request *req)
415597d0caeSDavid Teigland {
416597d0caeSDavid Teigland 	if (req->version[0] != DLM_DEVICE_VERSION_MAJOR ||
417597d0caeSDavid Teigland 	    (req->version[0] == DLM_DEVICE_VERSION_MAJOR &&
418597d0caeSDavid Teigland 	     req->version[1] > DLM_DEVICE_VERSION_MINOR)) {
419597d0caeSDavid Teigland 
420597d0caeSDavid Teigland 		printk(KERN_DEBUG "dlm: process %s (%d) version mismatch "
421597d0caeSDavid Teigland 		       "user (%d.%d.%d) kernel (%d.%d.%d)\n",
422597d0caeSDavid Teigland 		       current->comm,
423597d0caeSDavid Teigland 		       current->pid,
424597d0caeSDavid Teigland 		       req->version[0],
425597d0caeSDavid Teigland 		       req->version[1],
426597d0caeSDavid Teigland 		       req->version[2],
427597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_MAJOR,
428597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_MINOR,
429597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_PATCH);
430597d0caeSDavid Teigland 		return -EINVAL;
431597d0caeSDavid Teigland 	}
432597d0caeSDavid Teigland 	return 0;
433597d0caeSDavid Teigland }
434597d0caeSDavid Teigland 
435597d0caeSDavid Teigland /*
436597d0caeSDavid Teigland  * device_write
437597d0caeSDavid Teigland  *
438597d0caeSDavid Teigland  *   device_user_lock
439597d0caeSDavid Teigland  *     dlm_user_request -> request_lock
440597d0caeSDavid Teigland  *     dlm_user_convert -> convert_lock
441597d0caeSDavid Teigland  *
442597d0caeSDavid Teigland  *   device_user_unlock
443597d0caeSDavid Teigland  *     dlm_user_unlock -> unlock_lock
444597d0caeSDavid Teigland  *     dlm_user_cancel -> cancel_lock
445597d0caeSDavid Teigland  *
446597d0caeSDavid Teigland  *   device_create_lockspace
447597d0caeSDavid Teigland  *     dlm_new_lockspace
448597d0caeSDavid Teigland  *
449597d0caeSDavid Teigland  *   device_remove_lockspace
450597d0caeSDavid Teigland  *     dlm_release_lockspace
451597d0caeSDavid Teigland  */
452597d0caeSDavid Teigland 
453597d0caeSDavid Teigland /* a write to a lockspace device is a lock or unlock request, a write
454597d0caeSDavid Teigland    to the control device is to create/remove a lockspace */
455597d0caeSDavid Teigland 
456597d0caeSDavid Teigland static ssize_t device_write(struct file *file, const char __user *buf,
457597d0caeSDavid Teigland 			    size_t count, loff_t *ppos)
458597d0caeSDavid Teigland {
459597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
460597d0caeSDavid Teigland 	struct dlm_write_request *kbuf;
461597d0caeSDavid Teigland 	sigset_t tmpsig, allsigs;
462597d0caeSDavid Teigland 	int error;
463597d0caeSDavid Teigland 
464597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
465597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_write_request32))
466597d0caeSDavid Teigland #else
467597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_write_request))
468597d0caeSDavid Teigland #endif
469597d0caeSDavid Teigland 		return -EINVAL;
470597d0caeSDavid Teigland 
471597d0caeSDavid Teigland 	kbuf = kmalloc(count, GFP_KERNEL);
472597d0caeSDavid Teigland 	if (!kbuf)
473597d0caeSDavid Teigland 		return -ENOMEM;
474597d0caeSDavid Teigland 
475597d0caeSDavid Teigland 	if (copy_from_user(kbuf, buf, count)) {
476597d0caeSDavid Teigland 		error = -EFAULT;
477597d0caeSDavid Teigland 		goto out_free;
478597d0caeSDavid Teigland 	}
479597d0caeSDavid Teigland 
480597d0caeSDavid Teigland 	if (check_version(kbuf)) {
481597d0caeSDavid Teigland 		error = -EBADE;
482597d0caeSDavid Teigland 		goto out_free;
483597d0caeSDavid Teigland 	}
484597d0caeSDavid Teigland 
485597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
486597d0caeSDavid Teigland 	if (!kbuf->is64bit) {
487597d0caeSDavid Teigland 		struct dlm_write_request32 *k32buf;
488597d0caeSDavid Teigland 		k32buf = (struct dlm_write_request32 *)kbuf;
489597d0caeSDavid Teigland 		kbuf = kmalloc(count + (sizeof(struct dlm_write_request) -
490597d0caeSDavid Teigland 			       sizeof(struct dlm_write_request32)), GFP_KERNEL);
491597d0caeSDavid Teigland 		if (!kbuf)
492597d0caeSDavid Teigland 			return -ENOMEM;
493597d0caeSDavid Teigland 
494597d0caeSDavid Teigland 		if (proc)
495597d0caeSDavid Teigland 			set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags);
496597d0caeSDavid Teigland 		compat_input(kbuf, k32buf);
497597d0caeSDavid Teigland 		kfree(k32buf);
498597d0caeSDavid Teigland 	}
499597d0caeSDavid Teigland #endif
500597d0caeSDavid Teigland 
501597d0caeSDavid Teigland 	/* do we really need this? can a write happen after a close? */
502597d0caeSDavid Teigland 	if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) &&
503597d0caeSDavid Teigland 	    test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
504597d0caeSDavid Teigland 		return -EINVAL;
505597d0caeSDavid Teigland 
506597d0caeSDavid Teigland 	sigfillset(&allsigs);
507597d0caeSDavid Teigland 	sigprocmask(SIG_BLOCK, &allsigs, &tmpsig);
508597d0caeSDavid Teigland 
509597d0caeSDavid Teigland 	error = -EINVAL;
510597d0caeSDavid Teigland 
511597d0caeSDavid Teigland 	switch (kbuf->cmd)
512597d0caeSDavid Teigland 	{
513597d0caeSDavid Teigland 	case DLM_USER_LOCK:
514597d0caeSDavid Teigland 		if (!proc) {
515597d0caeSDavid Teigland 			log_print("no locking on control device");
516597d0caeSDavid Teigland 			goto out_sig;
517597d0caeSDavid Teigland 		}
518597d0caeSDavid Teigland 		error = device_user_lock(proc, &kbuf->i.lock);
519597d0caeSDavid Teigland 		break;
520597d0caeSDavid Teigland 
521597d0caeSDavid Teigland 	case DLM_USER_UNLOCK:
522597d0caeSDavid Teigland 		if (!proc) {
523597d0caeSDavid Teigland 			log_print("no locking on control device");
524597d0caeSDavid Teigland 			goto out_sig;
525597d0caeSDavid Teigland 		}
526597d0caeSDavid Teigland 		error = device_user_unlock(proc, &kbuf->i.lock);
527597d0caeSDavid Teigland 		break;
528597d0caeSDavid Teigland 
529597d0caeSDavid Teigland 	case DLM_USER_CREATE_LOCKSPACE:
530597d0caeSDavid Teigland 		if (proc) {
531597d0caeSDavid Teigland 			log_print("create/remove only on control device");
532597d0caeSDavid Teigland 			goto out_sig;
533597d0caeSDavid Teigland 		}
534597d0caeSDavid Teigland 		error = device_create_lockspace(&kbuf->i.lspace);
535597d0caeSDavid Teigland 		break;
536597d0caeSDavid Teigland 
537597d0caeSDavid Teigland 	case DLM_USER_REMOVE_LOCKSPACE:
538597d0caeSDavid Teigland 		if (proc) {
539597d0caeSDavid Teigland 			log_print("create/remove only on control device");
540597d0caeSDavid Teigland 			goto out_sig;
541597d0caeSDavid Teigland 		}
542597d0caeSDavid Teigland 		error = device_remove_lockspace(&kbuf->i.lspace);
543597d0caeSDavid Teigland 		break;
544597d0caeSDavid Teigland 
545*72c2be77SDavid Teigland 	case DLM_USER_PURGE:
546*72c2be77SDavid Teigland 		if (!proc) {
547*72c2be77SDavid Teigland 			log_print("no locking on control device");
548*72c2be77SDavid Teigland 			goto out_sig;
549*72c2be77SDavid Teigland 		}
550*72c2be77SDavid Teigland 		error = device_user_purge(proc, &kbuf->i.purge);
551*72c2be77SDavid Teigland 		break;
552*72c2be77SDavid Teigland 
553597d0caeSDavid Teigland 	default:
554597d0caeSDavid Teigland 		log_print("Unknown command passed to DLM device : %d\n",
555597d0caeSDavid Teigland 			  kbuf->cmd);
556597d0caeSDavid Teigland 	}
557597d0caeSDavid Teigland 
558597d0caeSDavid Teigland  out_sig:
559597d0caeSDavid Teigland 	sigprocmask(SIG_SETMASK, &tmpsig, NULL);
560597d0caeSDavid Teigland 	recalc_sigpending();
561597d0caeSDavid Teigland  out_free:
562597d0caeSDavid Teigland 	kfree(kbuf);
563597d0caeSDavid Teigland 	return error;
564597d0caeSDavid Teigland }
565597d0caeSDavid Teigland 
566597d0caeSDavid Teigland /* Every process that opens the lockspace device has its own "proc" structure
567597d0caeSDavid Teigland    hanging off the open file that's used to keep track of locks owned by the
568597d0caeSDavid Teigland    process and asts that need to be delivered to the process. */
569597d0caeSDavid Teigland 
570597d0caeSDavid Teigland static int device_open(struct inode *inode, struct file *file)
571597d0caeSDavid Teigland {
572597d0caeSDavid Teigland 	struct dlm_user_proc *proc;
573597d0caeSDavid Teigland 	struct dlm_ls *ls;
574597d0caeSDavid Teigland 
575597d0caeSDavid Teigland 	ls = dlm_find_lockspace_device(iminor(inode));
576597d0caeSDavid Teigland 	if (!ls)
577597d0caeSDavid Teigland 		return -ENOENT;
578597d0caeSDavid Teigland 
579597d0caeSDavid Teigland 	proc = kzalloc(sizeof(struct dlm_user_proc), GFP_KERNEL);
580597d0caeSDavid Teigland 	if (!proc) {
581597d0caeSDavid Teigland 		dlm_put_lockspace(ls);
582597d0caeSDavid Teigland 		return -ENOMEM;
583597d0caeSDavid Teigland 	}
584597d0caeSDavid Teigland 
585597d0caeSDavid Teigland 	proc->lockspace = ls->ls_local_handle;
586597d0caeSDavid Teigland 	INIT_LIST_HEAD(&proc->asts);
587597d0caeSDavid Teigland 	INIT_LIST_HEAD(&proc->locks);
588a1bc86e6SDavid Teigland 	INIT_LIST_HEAD(&proc->unlocking);
589597d0caeSDavid Teigland 	spin_lock_init(&proc->asts_spin);
590597d0caeSDavid Teigland 	spin_lock_init(&proc->locks_spin);
591597d0caeSDavid Teigland 	init_waitqueue_head(&proc->wait);
592597d0caeSDavid Teigland 	file->private_data = proc;
593597d0caeSDavid Teigland 
594597d0caeSDavid Teigland 	return 0;
595597d0caeSDavid Teigland }
596597d0caeSDavid Teigland 
597597d0caeSDavid Teigland static int device_close(struct inode *inode, struct file *file)
598597d0caeSDavid Teigland {
599597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
600597d0caeSDavid Teigland 	struct dlm_ls *ls;
601597d0caeSDavid Teigland 	sigset_t tmpsig, allsigs;
602597d0caeSDavid Teigland 
603597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
604597d0caeSDavid Teigland 	if (!ls)
605597d0caeSDavid Teigland 		return -ENOENT;
606597d0caeSDavid Teigland 
607597d0caeSDavid Teigland 	sigfillset(&allsigs);
608597d0caeSDavid Teigland 	sigprocmask(SIG_BLOCK, &allsigs, &tmpsig);
609597d0caeSDavid Teigland 
610597d0caeSDavid Teigland 	set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags);
611597d0caeSDavid Teigland 
612597d0caeSDavid Teigland 	dlm_clear_proc_locks(ls, proc);
613597d0caeSDavid Teigland 
614597d0caeSDavid Teigland 	/* at this point no more lkb's should exist for this lockspace,
615597d0caeSDavid Teigland 	   so there's no chance of dlm_user_add_ast() being called and
616597d0caeSDavid Teigland 	   looking for lkb->ua->proc */
617597d0caeSDavid Teigland 
618597d0caeSDavid Teigland 	kfree(proc);
619597d0caeSDavid Teigland 	file->private_data = NULL;
620597d0caeSDavid Teigland 
621597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
622597d0caeSDavid Teigland 	dlm_put_lockspace(ls);  /* for the find in device_open() */
623597d0caeSDavid Teigland 
624597d0caeSDavid Teigland 	/* FIXME: AUTOFREE: if this ls is no longer used do
625597d0caeSDavid Teigland 	   device_remove_lockspace() */
626597d0caeSDavid Teigland 
627597d0caeSDavid Teigland 	sigprocmask(SIG_SETMASK, &tmpsig, NULL);
628597d0caeSDavid Teigland 	recalc_sigpending();
629597d0caeSDavid Teigland 
630597d0caeSDavid Teigland 	return 0;
631597d0caeSDavid Teigland }
632597d0caeSDavid Teigland 
633597d0caeSDavid Teigland static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,
634597d0caeSDavid Teigland 			       int bmode, char __user *buf, size_t count)
635597d0caeSDavid Teigland {
636597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
637597d0caeSDavid Teigland 	struct dlm_lock_result32 result32;
638597d0caeSDavid Teigland #endif
639597d0caeSDavid Teigland 	struct dlm_lock_result result;
640597d0caeSDavid Teigland 	void *resultptr;
641597d0caeSDavid Teigland 	int error=0;
642597d0caeSDavid Teigland 	int len;
643597d0caeSDavid Teigland 	int struct_len;
644597d0caeSDavid Teigland 
645597d0caeSDavid Teigland 	memset(&result, 0, sizeof(struct dlm_lock_result));
646597d0caeSDavid Teigland 	memcpy(&result.lksb, &ua->lksb, sizeof(struct dlm_lksb));
647597d0caeSDavid Teigland 	result.user_lksb = ua->user_lksb;
648597d0caeSDavid Teigland 
649597d0caeSDavid Teigland 	/* FIXME: dlm1 provides for the user's bastparam/addr to not be updated
650597d0caeSDavid Teigland 	   in a conversion unless the conversion is successful.  See code
651597d0caeSDavid Teigland 	   in dlm_user_convert() for updating ua from ua_tmp.  OpenVMS, though,
652597d0caeSDavid Teigland 	   notes that a new blocking AST address and parameter are set even if
653597d0caeSDavid Teigland 	   the conversion fails, so maybe we should just do that. */
654597d0caeSDavid Teigland 
655597d0caeSDavid Teigland 	if (type == AST_BAST) {
656597d0caeSDavid Teigland 		result.user_astaddr = ua->bastaddr;
657597d0caeSDavid Teigland 		result.user_astparam = ua->bastparam;
658597d0caeSDavid Teigland 		result.bast_mode = bmode;
659597d0caeSDavid Teigland 	} else {
660597d0caeSDavid Teigland 		result.user_astaddr = ua->castaddr;
661597d0caeSDavid Teigland 		result.user_astparam = ua->castparam;
662597d0caeSDavid Teigland 	}
663597d0caeSDavid Teigland 
664597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
665597d0caeSDavid Teigland 	if (compat)
666597d0caeSDavid Teigland 		len = sizeof(struct dlm_lock_result32);
667597d0caeSDavid Teigland 	else
668597d0caeSDavid Teigland #endif
669597d0caeSDavid Teigland 		len = sizeof(struct dlm_lock_result);
670597d0caeSDavid Teigland 	struct_len = len;
671597d0caeSDavid Teigland 
672597d0caeSDavid Teigland 	/* copy lvb to userspace if there is one, it's been updated, and
673597d0caeSDavid Teigland 	   the user buffer has space for it */
674597d0caeSDavid Teigland 
675597d0caeSDavid Teigland 	if (ua->update_user_lvb && ua->lksb.sb_lvbptr &&
676597d0caeSDavid Teigland 	    count >= len + DLM_USER_LVB_LEN) {
677597d0caeSDavid Teigland 		if (copy_to_user(buf+len, ua->lksb.sb_lvbptr,
678597d0caeSDavid Teigland 				 DLM_USER_LVB_LEN)) {
679597d0caeSDavid Teigland 			error = -EFAULT;
680597d0caeSDavid Teigland 			goto out;
681597d0caeSDavid Teigland 		}
682597d0caeSDavid Teigland 
683597d0caeSDavid Teigland 		result.lvb_offset = len;
684597d0caeSDavid Teigland 		len += DLM_USER_LVB_LEN;
685597d0caeSDavid Teigland 	}
686597d0caeSDavid Teigland 
687597d0caeSDavid Teigland 	result.length = len;
688597d0caeSDavid Teigland 	resultptr = &result;
689597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
690597d0caeSDavid Teigland 	if (compat) {
691597d0caeSDavid Teigland 		compat_output(&result, &result32);
692597d0caeSDavid Teigland 		resultptr = &result32;
693597d0caeSDavid Teigland 	}
694597d0caeSDavid Teigland #endif
695597d0caeSDavid Teigland 
696597d0caeSDavid Teigland 	if (copy_to_user(buf, resultptr, struct_len))
697597d0caeSDavid Teigland 		error = -EFAULT;
698597d0caeSDavid Teigland 	else
699597d0caeSDavid Teigland 		error = len;
700597d0caeSDavid Teigland  out:
701597d0caeSDavid Teigland 	return error;
702597d0caeSDavid Teigland }
703597d0caeSDavid Teigland 
704597d0caeSDavid Teigland /* a read returns a single ast described in a struct dlm_lock_result */
705597d0caeSDavid Teigland 
706597d0caeSDavid Teigland static ssize_t device_read(struct file *file, char __user *buf, size_t count,
707597d0caeSDavid Teigland 			   loff_t *ppos)
708597d0caeSDavid Teigland {
709597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
710597d0caeSDavid Teigland 	struct dlm_lkb *lkb;
711597d0caeSDavid Teigland 	struct dlm_user_args *ua;
712597d0caeSDavid Teigland 	DECLARE_WAITQUEUE(wait, current);
713597d0caeSDavid Teigland 	int error, type=0, bmode=0, removed = 0;
714597d0caeSDavid Teigland 
715597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
716597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_lock_result32))
717597d0caeSDavid Teigland #else
718597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_lock_result))
719597d0caeSDavid Teigland #endif
720597d0caeSDavid Teigland 		return -EINVAL;
721597d0caeSDavid Teigland 
722597d0caeSDavid Teigland 	/* do we really need this? can a read happen after a close? */
723597d0caeSDavid Teigland 	if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
724597d0caeSDavid Teigland 		return -EINVAL;
725597d0caeSDavid Teigland 
726597d0caeSDavid Teigland 	spin_lock(&proc->asts_spin);
727597d0caeSDavid Teigland 	if (list_empty(&proc->asts)) {
728597d0caeSDavid Teigland 		if (file->f_flags & O_NONBLOCK) {
729597d0caeSDavid Teigland 			spin_unlock(&proc->asts_spin);
730597d0caeSDavid Teigland 			return -EAGAIN;
731597d0caeSDavid Teigland 		}
732597d0caeSDavid Teigland 
733597d0caeSDavid Teigland 		add_wait_queue(&proc->wait, &wait);
734597d0caeSDavid Teigland 
735597d0caeSDavid Teigland 	repeat:
736597d0caeSDavid Teigland 		set_current_state(TASK_INTERRUPTIBLE);
737597d0caeSDavid Teigland 		if (list_empty(&proc->asts) && !signal_pending(current)) {
738597d0caeSDavid Teigland 			spin_unlock(&proc->asts_spin);
739597d0caeSDavid Teigland 			schedule();
740597d0caeSDavid Teigland 			spin_lock(&proc->asts_spin);
741597d0caeSDavid Teigland 			goto repeat;
742597d0caeSDavid Teigland 		}
743597d0caeSDavid Teigland 		set_current_state(TASK_RUNNING);
744597d0caeSDavid Teigland 		remove_wait_queue(&proc->wait, &wait);
745597d0caeSDavid Teigland 
746597d0caeSDavid Teigland 		if (signal_pending(current)) {
747597d0caeSDavid Teigland 			spin_unlock(&proc->asts_spin);
748597d0caeSDavid Teigland 			return -ERESTARTSYS;
749597d0caeSDavid Teigland 		}
750597d0caeSDavid Teigland 	}
751597d0caeSDavid Teigland 
752597d0caeSDavid Teigland 	if (list_empty(&proc->asts)) {
753597d0caeSDavid Teigland 		spin_unlock(&proc->asts_spin);
754597d0caeSDavid Teigland 		return -EAGAIN;
755597d0caeSDavid Teigland 	}
756597d0caeSDavid Teigland 
757597d0caeSDavid Teigland 	/* there may be both completion and blocking asts to return for
758597d0caeSDavid Teigland 	   the lkb, don't remove lkb from asts list unless no asts remain */
759597d0caeSDavid Teigland 
760597d0caeSDavid Teigland 	lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue);
761597d0caeSDavid Teigland 
762597d0caeSDavid Teigland 	if (lkb->lkb_ast_type & AST_COMP) {
763597d0caeSDavid Teigland 		lkb->lkb_ast_type &= ~AST_COMP;
764597d0caeSDavid Teigland 		type = AST_COMP;
765597d0caeSDavid Teigland 	} else if (lkb->lkb_ast_type & AST_BAST) {
766597d0caeSDavid Teigland 		lkb->lkb_ast_type &= ~AST_BAST;
767597d0caeSDavid Teigland 		type = AST_BAST;
768597d0caeSDavid Teigland 		bmode = lkb->lkb_bastmode;
769597d0caeSDavid Teigland 	}
770597d0caeSDavid Teigland 
771597d0caeSDavid Teigland 	if (!lkb->lkb_ast_type) {
772597d0caeSDavid Teigland 		list_del(&lkb->lkb_astqueue);
773597d0caeSDavid Teigland 		removed = 1;
774597d0caeSDavid Teigland 	}
775597d0caeSDavid Teigland 	spin_unlock(&proc->asts_spin);
776597d0caeSDavid Teigland 
777597d0caeSDavid Teigland 	ua = (struct dlm_user_args *)lkb->lkb_astparam;
778597d0caeSDavid Teigland 	error = copy_result_to_user(ua,
779597d0caeSDavid Teigland 			 	test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
780597d0caeSDavid Teigland 				type, bmode, buf, count);
781597d0caeSDavid Teigland 
782597d0caeSDavid Teigland 	/* removes reference for the proc->asts lists added by
783597d0caeSDavid Teigland 	   dlm_user_add_ast() and may result in the lkb being freed */
784597d0caeSDavid Teigland 	if (removed)
785597d0caeSDavid Teigland 		dlm_put_lkb(lkb);
786597d0caeSDavid Teigland 
787597d0caeSDavid Teigland 	return error;
788597d0caeSDavid Teigland }
789597d0caeSDavid Teigland 
790597d0caeSDavid Teigland static unsigned int device_poll(struct file *file, poll_table *wait)
791597d0caeSDavid Teigland {
792597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
793597d0caeSDavid Teigland 
794597d0caeSDavid Teigland 	poll_wait(file, &proc->wait, wait);
795597d0caeSDavid Teigland 
796597d0caeSDavid Teigland 	spin_lock(&proc->asts_spin);
797597d0caeSDavid Teigland 	if (!list_empty(&proc->asts)) {
798597d0caeSDavid Teigland 		spin_unlock(&proc->asts_spin);
799597d0caeSDavid Teigland 		return POLLIN | POLLRDNORM;
800597d0caeSDavid Teigland 	}
801597d0caeSDavid Teigland 	spin_unlock(&proc->asts_spin);
802597d0caeSDavid Teigland 	return 0;
803597d0caeSDavid Teigland }
804597d0caeSDavid Teigland 
805597d0caeSDavid Teigland static int ctl_device_open(struct inode *inode, struct file *file)
806597d0caeSDavid Teigland {
807597d0caeSDavid Teigland 	file->private_data = NULL;
808597d0caeSDavid Teigland 	return 0;
809597d0caeSDavid Teigland }
810597d0caeSDavid Teigland 
811597d0caeSDavid Teigland static int ctl_device_close(struct inode *inode, struct file *file)
812597d0caeSDavid Teigland {
813597d0caeSDavid Teigland 	return 0;
814597d0caeSDavid Teigland }
815597d0caeSDavid Teigland 
81600977a59SArjan van de Ven static const struct file_operations device_fops = {
817597d0caeSDavid Teigland 	.open    = device_open,
818597d0caeSDavid Teigland 	.release = device_close,
819597d0caeSDavid Teigland 	.read    = device_read,
820597d0caeSDavid Teigland 	.write   = device_write,
821597d0caeSDavid Teigland 	.poll    = device_poll,
822597d0caeSDavid Teigland 	.owner   = THIS_MODULE,
823597d0caeSDavid Teigland };
824597d0caeSDavid Teigland 
82500977a59SArjan van de Ven static const struct file_operations ctl_device_fops = {
826597d0caeSDavid Teigland 	.open    = ctl_device_open,
827597d0caeSDavid Teigland 	.release = ctl_device_close,
828597d0caeSDavid Teigland 	.write   = device_write,
829597d0caeSDavid Teigland 	.owner   = THIS_MODULE,
830597d0caeSDavid Teigland };
831597d0caeSDavid Teigland 
832597d0caeSDavid Teigland int dlm_user_init(void)
833597d0caeSDavid Teigland {
834597d0caeSDavid Teigland 	int error;
835597d0caeSDavid Teigland 
836597d0caeSDavid Teigland 	ctl_device.name = "dlm-control";
837597d0caeSDavid Teigland 	ctl_device.fops = &ctl_device_fops;
838597d0caeSDavid Teigland 	ctl_device.minor = MISC_DYNAMIC_MINOR;
839597d0caeSDavid Teigland 
840597d0caeSDavid Teigland 	error = misc_register(&ctl_device);
841597d0caeSDavid Teigland 	if (error)
842597d0caeSDavid Teigland 		log_print("misc_register failed for control device");
843597d0caeSDavid Teigland 
844597d0caeSDavid Teigland 	return error;
845597d0caeSDavid Teigland }
846597d0caeSDavid Teigland 
847597d0caeSDavid Teigland void dlm_user_exit(void)
848597d0caeSDavid Teigland {
849597d0caeSDavid Teigland 	misc_deregister(&ctl_device);
850597d0caeSDavid Teigland }
851597d0caeSDavid Teigland 
852