xref: /linux/fs/dlm/user.c (revision c6e6f0ba)
1597d0caeSDavid Teigland /*
2597d0caeSDavid Teigland  * Copyright (C) 2006 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"
25597d0caeSDavid Teigland 
26597d0caeSDavid Teigland static const char *name_prefix="dlm";
27597d0caeSDavid Teigland static struct miscdevice ctl_device;
28597d0caeSDavid Teigland static struct file_operations device_fops;
29597d0caeSDavid Teigland 
30597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
31597d0caeSDavid Teigland 
32597d0caeSDavid Teigland struct dlm_lock_params32 {
33597d0caeSDavid Teigland 	__u8 mode;
34597d0caeSDavid Teigland 	__u8 namelen;
35597d0caeSDavid Teigland 	__u16 flags;
36597d0caeSDavid Teigland 	__u32 lkid;
37597d0caeSDavid Teigland 	__u32 parent;
38597d0caeSDavid Teigland 
39597d0caeSDavid Teigland 	__u32 castparam;
40597d0caeSDavid Teigland 	__u32 castaddr;
41597d0caeSDavid Teigland 	__u32 bastparam;
42597d0caeSDavid Teigland 	__u32 bastaddr;
43597d0caeSDavid Teigland 	__u32 lksb;
44597d0caeSDavid Teigland 
45597d0caeSDavid Teigland 	char lvb[DLM_USER_LVB_LEN];
46597d0caeSDavid Teigland 	char name[0];
47597d0caeSDavid Teigland };
48597d0caeSDavid Teigland 
49597d0caeSDavid Teigland struct dlm_write_request32 {
50597d0caeSDavid Teigland 	__u32 version[3];
51597d0caeSDavid Teigland 	__u8 cmd;
52597d0caeSDavid Teigland 	__u8 is64bit;
53597d0caeSDavid Teigland 	__u8 unused[2];
54597d0caeSDavid Teigland 
55597d0caeSDavid Teigland 	union  {
56597d0caeSDavid Teigland 		struct dlm_lock_params32 lock;
57597d0caeSDavid Teigland 		struct dlm_lspace_params lspace;
58597d0caeSDavid Teigland 	} i;
59597d0caeSDavid Teigland };
60597d0caeSDavid Teigland 
61597d0caeSDavid Teigland struct dlm_lksb32 {
62597d0caeSDavid Teigland 	__u32 sb_status;
63597d0caeSDavid Teigland 	__u32 sb_lkid;
64597d0caeSDavid Teigland 	__u8 sb_flags;
65597d0caeSDavid Teigland 	__u32 sb_lvbptr;
66597d0caeSDavid Teigland };
67597d0caeSDavid Teigland 
68597d0caeSDavid Teigland struct dlm_lock_result32 {
69597d0caeSDavid Teigland 	__u32 length;
70597d0caeSDavid Teigland 	__u32 user_astaddr;
71597d0caeSDavid Teigland 	__u32 user_astparam;
72597d0caeSDavid Teigland 	__u32 user_lksb;
73597d0caeSDavid Teigland 	struct dlm_lksb32 lksb;
74597d0caeSDavid Teigland 	__u8 bast_mode;
75597d0caeSDavid Teigland 	__u8 unused[3];
76597d0caeSDavid Teigland 	/* Offsets may be zero if no data is present */
77597d0caeSDavid Teigland 	__u32 lvb_offset;
78597d0caeSDavid Teigland };
79597d0caeSDavid Teigland 
80597d0caeSDavid Teigland static void compat_input(struct dlm_write_request *kb,
81597d0caeSDavid Teigland 			 struct dlm_write_request32 *kb32)
82597d0caeSDavid Teigland {
83597d0caeSDavid Teigland 	kb->version[0] = kb32->version[0];
84597d0caeSDavid Teigland 	kb->version[1] = kb32->version[1];
85597d0caeSDavid Teigland 	kb->version[2] = kb32->version[2];
86597d0caeSDavid Teigland 
87597d0caeSDavid Teigland 	kb->cmd = kb32->cmd;
88597d0caeSDavid Teigland 	kb->is64bit = kb32->is64bit;
89597d0caeSDavid Teigland 	if (kb->cmd == DLM_USER_CREATE_LOCKSPACE ||
90597d0caeSDavid Teigland 	    kb->cmd == DLM_USER_REMOVE_LOCKSPACE) {
91597d0caeSDavid Teigland 		kb->i.lspace.flags = kb32->i.lspace.flags;
92597d0caeSDavid Teigland 		kb->i.lspace.minor = kb32->i.lspace.minor;
93597d0caeSDavid Teigland 		strcpy(kb->i.lspace.name, kb32->i.lspace.name);
94597d0caeSDavid Teigland 	} else {
95597d0caeSDavid Teigland 		kb->i.lock.mode = kb32->i.lock.mode;
96597d0caeSDavid Teigland 		kb->i.lock.namelen = kb32->i.lock.namelen;
97597d0caeSDavid Teigland 		kb->i.lock.flags = kb32->i.lock.flags;
98597d0caeSDavid Teigland 		kb->i.lock.lkid = kb32->i.lock.lkid;
99597d0caeSDavid Teigland 		kb->i.lock.parent = kb32->i.lock.parent;
100597d0caeSDavid Teigland 		kb->i.lock.castparam = (void *)(long)kb32->i.lock.castparam;
101597d0caeSDavid Teigland 		kb->i.lock.castaddr = (void *)(long)kb32->i.lock.castaddr;
102597d0caeSDavid Teigland 		kb->i.lock.bastparam = (void *)(long)kb32->i.lock.bastparam;
103597d0caeSDavid Teigland 		kb->i.lock.bastaddr = (void *)(long)kb32->i.lock.bastaddr;
104597d0caeSDavid Teigland 		kb->i.lock.lksb = (void *)(long)kb32->i.lock.lksb;
105597d0caeSDavid Teigland 		memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN);
106597d0caeSDavid Teigland 		memcpy(kb->i.lock.name, kb32->i.lock.name, kb->i.lock.namelen);
107597d0caeSDavid Teigland 	}
108597d0caeSDavid Teigland }
109597d0caeSDavid Teigland 
110597d0caeSDavid Teigland static void compat_output(struct dlm_lock_result *res,
111597d0caeSDavid Teigland 			  struct dlm_lock_result32 *res32)
112597d0caeSDavid Teigland {
113597d0caeSDavid Teigland 	res32->length = res->length - (sizeof(struct dlm_lock_result) -
114597d0caeSDavid Teigland 				       sizeof(struct dlm_lock_result32));
115597d0caeSDavid Teigland 	res32->user_astaddr = (__u32)(long)res->user_astaddr;
116597d0caeSDavid Teigland 	res32->user_astparam = (__u32)(long)res->user_astparam;
117597d0caeSDavid Teigland 	res32->user_lksb = (__u32)(long)res->user_lksb;
118597d0caeSDavid Teigland 	res32->bast_mode = res->bast_mode;
119597d0caeSDavid Teigland 
120597d0caeSDavid Teigland 	res32->lvb_offset = res->lvb_offset;
121597d0caeSDavid Teigland 	res32->length = res->length;
122597d0caeSDavid Teigland 
123597d0caeSDavid Teigland 	res32->lksb.sb_status = res->lksb.sb_status;
124597d0caeSDavid Teigland 	res32->lksb.sb_flags = res->lksb.sb_flags;
125597d0caeSDavid Teigland 	res32->lksb.sb_lkid = res->lksb.sb_lkid;
126597d0caeSDavid Teigland 	res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr;
127597d0caeSDavid Teigland }
128597d0caeSDavid Teigland #endif
129597d0caeSDavid Teigland 
130597d0caeSDavid Teigland 
131597d0caeSDavid Teigland void dlm_user_add_ast(struct dlm_lkb *lkb, int type)
132597d0caeSDavid Teigland {
133597d0caeSDavid Teigland 	struct dlm_ls *ls;
134597d0caeSDavid Teigland 	struct dlm_user_args *ua;
135597d0caeSDavid Teigland 	struct dlm_user_proc *proc;
13634e22bedSDavid Teigland 	int remove_ownqueue = 0;
137597d0caeSDavid Teigland 
138597d0caeSDavid Teigland 	/* dlm_clear_proc_locks() sets ORPHAN/DEAD flag on each
139597d0caeSDavid Teigland 	   lkb before dealing with it.  We need to check this
140597d0caeSDavid Teigland 	   flag before taking ls_clear_proc_locks mutex because if
141597d0caeSDavid Teigland 	   it's set, dlm_clear_proc_locks() holds the mutex. */
142597d0caeSDavid Teigland 
143597d0caeSDavid Teigland 	if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) {
144597d0caeSDavid Teigland 		/* log_print("user_add_ast skip1 %x", lkb->lkb_flags); */
145597d0caeSDavid Teigland 		return;
146597d0caeSDavid Teigland 	}
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
153597d0caeSDavid Teigland 	   lkb->ua so we can't try to use it. */
154597d0caeSDavid Teigland 
155597d0caeSDavid Teigland 	if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) {
156597d0caeSDavid Teigland 		/* log_print("user_add_ast skip2 %x", lkb->lkb_flags); */
157597d0caeSDavid Teigland 		goto out;
158597d0caeSDavid Teigland 	}
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);
168597d0caeSDavid Teigland 	if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) {
169597d0caeSDavid Teigland 		kref_get(&lkb->lkb_ref);
170597d0caeSDavid Teigland 		list_add_tail(&lkb->lkb_astqueue, &proc->asts);
171597d0caeSDavid Teigland 		lkb->lkb_ast_type |= type;
172597d0caeSDavid Teigland 		wake_up_interruptible(&proc->wait);
173597d0caeSDavid Teigland 	}
174597d0caeSDavid Teigland 
17534e22bedSDavid Teigland 	/* noqueue requests that fail may need to be removed from the
17634e22bedSDavid Teigland 	   proc's locks list, there should be a better way of detecting
17734e22bedSDavid Teigland 	   this situation than checking all these things... */
17834e22bedSDavid Teigland 
17934e22bedSDavid Teigland 	if (type == AST_COMP && lkb->lkb_grmode == DLM_LOCK_IV &&
18034e22bedSDavid Teigland 	    ua->lksb.sb_status == -EAGAIN && !list_empty(&lkb->lkb_ownqueue))
18134e22bedSDavid Teigland 		remove_ownqueue = 1;
18234e22bedSDavid Teigland 
183597d0caeSDavid Teigland 	/* We want to copy the lvb to userspace when the completion
184597d0caeSDavid Teigland 	   ast is read if the status is 0, the lock has an lvb and
185597d0caeSDavid Teigland 	   lvb_ops says we should.  We could probably have set_lvb_lock()
186597d0caeSDavid Teigland 	   set update_user_lvb instead and not need old_mode */
187597d0caeSDavid Teigland 
188597d0caeSDavid Teigland 	if ((lkb->lkb_ast_type & AST_COMP) &&
189597d0caeSDavid Teigland 	    (lkb->lkb_lksb->sb_status == 0) &&
190597d0caeSDavid Teigland 	    lkb->lkb_lksb->sb_lvbptr &&
191597d0caeSDavid Teigland 	    dlm_lvb_operations[ua->old_mode + 1][lkb->lkb_grmode + 1])
192597d0caeSDavid Teigland 		ua->update_user_lvb = 1;
193597d0caeSDavid Teigland 	else
194597d0caeSDavid Teigland 		ua->update_user_lvb = 0;
195597d0caeSDavid Teigland 
196597d0caeSDavid Teigland 	spin_unlock(&proc->asts_spin);
19734e22bedSDavid Teigland 
19834e22bedSDavid Teigland 	if (remove_ownqueue) {
19934e22bedSDavid Teigland 		spin_lock(&ua->proc->locks_spin);
20034e22bedSDavid Teigland 		list_del_init(&lkb->lkb_ownqueue);
20134e22bedSDavid Teigland 		spin_unlock(&ua->proc->locks_spin);
20234e22bedSDavid Teigland 		dlm_put_lkb(lkb);
20334e22bedSDavid Teigland 	}
204597d0caeSDavid Teigland  out:
205597d0caeSDavid Teigland 	mutex_unlock(&ls->ls_clear_proc_locks);
206597d0caeSDavid Teigland }
207597d0caeSDavid Teigland 
208597d0caeSDavid Teigland static int device_user_lock(struct dlm_user_proc *proc,
209597d0caeSDavid Teigland 			    struct dlm_lock_params *params)
210597d0caeSDavid Teigland {
211597d0caeSDavid Teigland 	struct dlm_ls *ls;
212597d0caeSDavid Teigland 	struct dlm_user_args *ua;
213597d0caeSDavid Teigland 	int error = -ENOMEM;
214597d0caeSDavid Teigland 
215597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
216597d0caeSDavid Teigland 	if (!ls)
217597d0caeSDavid Teigland 		return -ENOENT;
218597d0caeSDavid Teigland 
219597d0caeSDavid Teigland 	if (!params->castaddr || !params->lksb) {
220597d0caeSDavid Teigland 		error = -EINVAL;
221597d0caeSDavid Teigland 		goto out;
222597d0caeSDavid Teigland 	}
223597d0caeSDavid Teigland 
224597d0caeSDavid Teigland 	ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL);
225597d0caeSDavid Teigland 	if (!ua)
226597d0caeSDavid Teigland 		goto out;
227597d0caeSDavid Teigland 	ua->proc = proc;
228597d0caeSDavid Teigland 	ua->user_lksb = params->lksb;
229597d0caeSDavid Teigland 	ua->castparam = params->castparam;
230597d0caeSDavid Teigland 	ua->castaddr = params->castaddr;
231597d0caeSDavid Teigland 	ua->bastparam = params->bastparam;
232597d0caeSDavid Teigland 	ua->bastaddr = params->bastaddr;
233597d0caeSDavid Teigland 
234597d0caeSDavid Teigland 	if (params->flags & DLM_LKF_CONVERT)
235597d0caeSDavid Teigland 		error = dlm_user_convert(ls, ua,
236597d0caeSDavid Teigland 				         params->mode, params->flags,
237597d0caeSDavid Teigland 				         params->lkid, params->lvb);
238597d0caeSDavid Teigland 	else {
239597d0caeSDavid Teigland 		error = dlm_user_request(ls, ua,
240597d0caeSDavid Teigland 					 params->mode, params->flags,
241597d0caeSDavid Teigland 					 params->name, params->namelen,
242597d0caeSDavid Teigland 					 params->parent);
243597d0caeSDavid Teigland 		if (!error)
244597d0caeSDavid Teigland 			error = ua->lksb.sb_lkid;
245597d0caeSDavid Teigland 	}
246597d0caeSDavid Teigland  out:
247597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
248597d0caeSDavid Teigland 	return error;
249597d0caeSDavid Teigland }
250597d0caeSDavid Teigland 
251597d0caeSDavid Teigland static int device_user_unlock(struct dlm_user_proc *proc,
252597d0caeSDavid Teigland 			      struct dlm_lock_params *params)
253597d0caeSDavid Teigland {
254597d0caeSDavid Teigland 	struct dlm_ls *ls;
255597d0caeSDavid Teigland 	struct dlm_user_args *ua;
256597d0caeSDavid Teigland 	int error = -ENOMEM;
257597d0caeSDavid Teigland 
258597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
259597d0caeSDavid Teigland 	if (!ls)
260597d0caeSDavid Teigland 		return -ENOENT;
261597d0caeSDavid Teigland 
262597d0caeSDavid Teigland 	ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL);
263597d0caeSDavid Teigland 	if (!ua)
264597d0caeSDavid Teigland 		goto out;
265597d0caeSDavid Teigland 	ua->proc = proc;
266597d0caeSDavid Teigland 	ua->user_lksb = params->lksb;
267597d0caeSDavid Teigland 	ua->castparam = params->castparam;
268597d0caeSDavid Teigland 	ua->castaddr = params->castaddr;
269597d0caeSDavid Teigland 
270597d0caeSDavid Teigland 	if (params->flags & DLM_LKF_CANCEL)
271597d0caeSDavid Teigland 		error = dlm_user_cancel(ls, ua, params->flags, params->lkid);
272597d0caeSDavid Teigland 	else
273597d0caeSDavid Teigland 		error = dlm_user_unlock(ls, ua, params->flags, params->lkid,
274597d0caeSDavid Teigland 					params->lvb);
275597d0caeSDavid Teigland  out:
276597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
277597d0caeSDavid Teigland 	return error;
278597d0caeSDavid Teigland }
279597d0caeSDavid Teigland 
280597d0caeSDavid Teigland static int device_create_lockspace(struct dlm_lspace_params *params)
281597d0caeSDavid Teigland {
282597d0caeSDavid Teigland 	dlm_lockspace_t *lockspace;
283597d0caeSDavid Teigland 	struct dlm_ls *ls;
284597d0caeSDavid Teigland 	int error, len;
285597d0caeSDavid Teigland 
286597d0caeSDavid Teigland 	if (!capable(CAP_SYS_ADMIN))
287597d0caeSDavid Teigland 		return -EPERM;
288597d0caeSDavid Teigland 
289597d0caeSDavid Teigland 	error = dlm_new_lockspace(params->name, strlen(params->name),
290597d0caeSDavid Teigland 				  &lockspace, 0, DLM_USER_LVB_LEN);
291597d0caeSDavid Teigland 	if (error)
292597d0caeSDavid Teigland 		return error;
293597d0caeSDavid Teigland 
294597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(lockspace);
295597d0caeSDavid Teigland 	if (!ls)
296597d0caeSDavid Teigland 		return -ENOENT;
297597d0caeSDavid Teigland 
298597d0caeSDavid Teigland 	error = -ENOMEM;
299597d0caeSDavid Teigland 	len = strlen(params->name) + strlen(name_prefix) + 2;
300597d0caeSDavid Teigland 	ls->ls_device.name = kzalloc(len, GFP_KERNEL);
301597d0caeSDavid Teigland 	if (!ls->ls_device.name)
302597d0caeSDavid Teigland 		goto fail;
303597d0caeSDavid Teigland 	snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix,
304597d0caeSDavid Teigland 		 params->name);
305597d0caeSDavid Teigland 	ls->ls_device.fops = &device_fops;
306597d0caeSDavid Teigland 	ls->ls_device.minor = MISC_DYNAMIC_MINOR;
307597d0caeSDavid Teigland 
308597d0caeSDavid Teigland 	error = misc_register(&ls->ls_device);
309597d0caeSDavid Teigland 	if (error) {
310597d0caeSDavid Teigland 		kfree(ls->ls_device.name);
311597d0caeSDavid Teigland 		goto fail;
312597d0caeSDavid Teigland 	}
313597d0caeSDavid Teigland 
314597d0caeSDavid Teigland 	error = ls->ls_device.minor;
315597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
316597d0caeSDavid Teigland 	return error;
317597d0caeSDavid Teigland 
318597d0caeSDavid Teigland  fail:
319597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
320597d0caeSDavid Teigland 	dlm_release_lockspace(lockspace, 0);
321597d0caeSDavid Teigland 	return error;
322597d0caeSDavid Teigland }
323597d0caeSDavid Teigland 
324597d0caeSDavid Teigland static int device_remove_lockspace(struct dlm_lspace_params *params)
325597d0caeSDavid Teigland {
326597d0caeSDavid Teigland 	dlm_lockspace_t *lockspace;
327597d0caeSDavid Teigland 	struct dlm_ls *ls;
328*c6e6f0baSDavid Teigland 	int error, force = 0;
329597d0caeSDavid Teigland 
330597d0caeSDavid Teigland 	if (!capable(CAP_SYS_ADMIN))
331597d0caeSDavid Teigland 		return -EPERM;
332597d0caeSDavid Teigland 
333597d0caeSDavid Teigland 	ls = dlm_find_lockspace_device(params->minor);
334597d0caeSDavid Teigland 	if (!ls)
335597d0caeSDavid Teigland 		return -ENOENT;
336597d0caeSDavid Teigland 
337597d0caeSDavid Teigland 	error = misc_deregister(&ls->ls_device);
338597d0caeSDavid Teigland 	if (error) {
339597d0caeSDavid Teigland 		dlm_put_lockspace(ls);
340597d0caeSDavid Teigland 		goto out;
341597d0caeSDavid Teigland 	}
342597d0caeSDavid Teigland 	kfree(ls->ls_device.name);
343597d0caeSDavid Teigland 
344*c6e6f0baSDavid Teigland 	if (params->flags & DLM_USER_LSFLG_FORCEFREE)
345*c6e6f0baSDavid Teigland 		force = 2;
346*c6e6f0baSDavid Teigland 
347597d0caeSDavid Teigland 	lockspace = ls->ls_local_handle;
348597d0caeSDavid Teigland 
349597d0caeSDavid Teigland 	/* dlm_release_lockspace waits for references to go to zero,
350597d0caeSDavid Teigland 	   so all processes will need to close their device for the ls
351597d0caeSDavid Teigland 	   before the release will procede */
352597d0caeSDavid Teigland 
353597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
354*c6e6f0baSDavid Teigland 	error = dlm_release_lockspace(lockspace, force);
355597d0caeSDavid Teigland  out:
356597d0caeSDavid Teigland 	return error;
357597d0caeSDavid Teigland }
358597d0caeSDavid Teigland 
359597d0caeSDavid Teigland /* Check the user's version matches ours */
360597d0caeSDavid Teigland static int check_version(struct dlm_write_request *req)
361597d0caeSDavid Teigland {
362597d0caeSDavid Teigland 	if (req->version[0] != DLM_DEVICE_VERSION_MAJOR ||
363597d0caeSDavid Teigland 	    (req->version[0] == DLM_DEVICE_VERSION_MAJOR &&
364597d0caeSDavid Teigland 	     req->version[1] > DLM_DEVICE_VERSION_MINOR)) {
365597d0caeSDavid Teigland 
366597d0caeSDavid Teigland 		printk(KERN_DEBUG "dlm: process %s (%d) version mismatch "
367597d0caeSDavid Teigland 		       "user (%d.%d.%d) kernel (%d.%d.%d)\n",
368597d0caeSDavid Teigland 		       current->comm,
369597d0caeSDavid Teigland 		       current->pid,
370597d0caeSDavid Teigland 		       req->version[0],
371597d0caeSDavid Teigland 		       req->version[1],
372597d0caeSDavid Teigland 		       req->version[2],
373597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_MAJOR,
374597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_MINOR,
375597d0caeSDavid Teigland 		       DLM_DEVICE_VERSION_PATCH);
376597d0caeSDavid Teigland 		return -EINVAL;
377597d0caeSDavid Teigland 	}
378597d0caeSDavid Teigland 	return 0;
379597d0caeSDavid Teigland }
380597d0caeSDavid Teigland 
381597d0caeSDavid Teigland /*
382597d0caeSDavid Teigland  * device_write
383597d0caeSDavid Teigland  *
384597d0caeSDavid Teigland  *   device_user_lock
385597d0caeSDavid Teigland  *     dlm_user_request -> request_lock
386597d0caeSDavid Teigland  *     dlm_user_convert -> convert_lock
387597d0caeSDavid Teigland  *
388597d0caeSDavid Teigland  *   device_user_unlock
389597d0caeSDavid Teigland  *     dlm_user_unlock -> unlock_lock
390597d0caeSDavid Teigland  *     dlm_user_cancel -> cancel_lock
391597d0caeSDavid Teigland  *
392597d0caeSDavid Teigland  *   device_create_lockspace
393597d0caeSDavid Teigland  *     dlm_new_lockspace
394597d0caeSDavid Teigland  *
395597d0caeSDavid Teigland  *   device_remove_lockspace
396597d0caeSDavid Teigland  *     dlm_release_lockspace
397597d0caeSDavid Teigland  */
398597d0caeSDavid Teigland 
399597d0caeSDavid Teigland /* a write to a lockspace device is a lock or unlock request, a write
400597d0caeSDavid Teigland    to the control device is to create/remove a lockspace */
401597d0caeSDavid Teigland 
402597d0caeSDavid Teigland static ssize_t device_write(struct file *file, const char __user *buf,
403597d0caeSDavid Teigland 			    size_t count, loff_t *ppos)
404597d0caeSDavid Teigland {
405597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
406597d0caeSDavid Teigland 	struct dlm_write_request *kbuf;
407597d0caeSDavid Teigland 	sigset_t tmpsig, allsigs;
408597d0caeSDavid Teigland 	int error;
409597d0caeSDavid Teigland 
410597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
411597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_write_request32))
412597d0caeSDavid Teigland #else
413597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_write_request))
414597d0caeSDavid Teigland #endif
415597d0caeSDavid Teigland 		return -EINVAL;
416597d0caeSDavid Teigland 
417597d0caeSDavid Teigland 	kbuf = kmalloc(count, GFP_KERNEL);
418597d0caeSDavid Teigland 	if (!kbuf)
419597d0caeSDavid Teigland 		return -ENOMEM;
420597d0caeSDavid Teigland 
421597d0caeSDavid Teigland 	if (copy_from_user(kbuf, buf, count)) {
422597d0caeSDavid Teigland 		error = -EFAULT;
423597d0caeSDavid Teigland 		goto out_free;
424597d0caeSDavid Teigland 	}
425597d0caeSDavid Teigland 
426597d0caeSDavid Teigland 	if (check_version(kbuf)) {
427597d0caeSDavid Teigland 		error = -EBADE;
428597d0caeSDavid Teigland 		goto out_free;
429597d0caeSDavid Teigland 	}
430597d0caeSDavid Teigland 
431597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
432597d0caeSDavid Teigland 	if (!kbuf->is64bit) {
433597d0caeSDavid Teigland 		struct dlm_write_request32 *k32buf;
434597d0caeSDavid Teigland 		k32buf = (struct dlm_write_request32 *)kbuf;
435597d0caeSDavid Teigland 		kbuf = kmalloc(count + (sizeof(struct dlm_write_request) -
436597d0caeSDavid Teigland 			       sizeof(struct dlm_write_request32)), GFP_KERNEL);
437597d0caeSDavid Teigland 		if (!kbuf)
438597d0caeSDavid Teigland 			return -ENOMEM;
439597d0caeSDavid Teigland 
440597d0caeSDavid Teigland 		if (proc)
441597d0caeSDavid Teigland 			set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags);
442597d0caeSDavid Teigland 		compat_input(kbuf, k32buf);
443597d0caeSDavid Teigland 		kfree(k32buf);
444597d0caeSDavid Teigland 	}
445597d0caeSDavid Teigland #endif
446597d0caeSDavid Teigland 
447597d0caeSDavid Teigland 	/* do we really need this? can a write happen after a close? */
448597d0caeSDavid Teigland 	if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) &&
449597d0caeSDavid Teigland 	    test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
450597d0caeSDavid Teigland 		return -EINVAL;
451597d0caeSDavid Teigland 
452597d0caeSDavid Teigland 	sigfillset(&allsigs);
453597d0caeSDavid Teigland 	sigprocmask(SIG_BLOCK, &allsigs, &tmpsig);
454597d0caeSDavid Teigland 
455597d0caeSDavid Teigland 	error = -EINVAL;
456597d0caeSDavid Teigland 
457597d0caeSDavid Teigland 	switch (kbuf->cmd)
458597d0caeSDavid Teigland 	{
459597d0caeSDavid Teigland 	case DLM_USER_LOCK:
460597d0caeSDavid Teigland 		if (!proc) {
461597d0caeSDavid Teigland 			log_print("no locking on control device");
462597d0caeSDavid Teigland 			goto out_sig;
463597d0caeSDavid Teigland 		}
464597d0caeSDavid Teigland 		error = device_user_lock(proc, &kbuf->i.lock);
465597d0caeSDavid Teigland 		break;
466597d0caeSDavid Teigland 
467597d0caeSDavid Teigland 	case DLM_USER_UNLOCK:
468597d0caeSDavid Teigland 		if (!proc) {
469597d0caeSDavid Teigland 			log_print("no locking on control device");
470597d0caeSDavid Teigland 			goto out_sig;
471597d0caeSDavid Teigland 		}
472597d0caeSDavid Teigland 		error = device_user_unlock(proc, &kbuf->i.lock);
473597d0caeSDavid Teigland 		break;
474597d0caeSDavid Teigland 
475597d0caeSDavid Teigland 	case DLM_USER_CREATE_LOCKSPACE:
476597d0caeSDavid Teigland 		if (proc) {
477597d0caeSDavid Teigland 			log_print("create/remove only on control device");
478597d0caeSDavid Teigland 			goto out_sig;
479597d0caeSDavid Teigland 		}
480597d0caeSDavid Teigland 		error = device_create_lockspace(&kbuf->i.lspace);
481597d0caeSDavid Teigland 		break;
482597d0caeSDavid Teigland 
483597d0caeSDavid Teigland 	case DLM_USER_REMOVE_LOCKSPACE:
484597d0caeSDavid Teigland 		if (proc) {
485597d0caeSDavid Teigland 			log_print("create/remove only on control device");
486597d0caeSDavid Teigland 			goto out_sig;
487597d0caeSDavid Teigland 		}
488597d0caeSDavid Teigland 		error = device_remove_lockspace(&kbuf->i.lspace);
489597d0caeSDavid Teigland 		break;
490597d0caeSDavid Teigland 
491597d0caeSDavid Teigland 	default:
492597d0caeSDavid Teigland 		log_print("Unknown command passed to DLM device : %d\n",
493597d0caeSDavid Teigland 			  kbuf->cmd);
494597d0caeSDavid Teigland 	}
495597d0caeSDavid Teigland 
496597d0caeSDavid Teigland  out_sig:
497597d0caeSDavid Teigland 	sigprocmask(SIG_SETMASK, &tmpsig, NULL);
498597d0caeSDavid Teigland 	recalc_sigpending();
499597d0caeSDavid Teigland  out_free:
500597d0caeSDavid Teigland 	kfree(kbuf);
501597d0caeSDavid Teigland 	return error;
502597d0caeSDavid Teigland }
503597d0caeSDavid Teigland 
504597d0caeSDavid Teigland /* Every process that opens the lockspace device has its own "proc" structure
505597d0caeSDavid Teigland    hanging off the open file that's used to keep track of locks owned by the
506597d0caeSDavid Teigland    process and asts that need to be delivered to the process. */
507597d0caeSDavid Teigland 
508597d0caeSDavid Teigland static int device_open(struct inode *inode, struct file *file)
509597d0caeSDavid Teigland {
510597d0caeSDavid Teigland 	struct dlm_user_proc *proc;
511597d0caeSDavid Teigland 	struct dlm_ls *ls;
512597d0caeSDavid Teigland 
513597d0caeSDavid Teigland 	ls = dlm_find_lockspace_device(iminor(inode));
514597d0caeSDavid Teigland 	if (!ls)
515597d0caeSDavid Teigland 		return -ENOENT;
516597d0caeSDavid Teigland 
517597d0caeSDavid Teigland 	proc = kzalloc(sizeof(struct dlm_user_proc), GFP_KERNEL);
518597d0caeSDavid Teigland 	if (!proc) {
519597d0caeSDavid Teigland 		dlm_put_lockspace(ls);
520597d0caeSDavid Teigland 		return -ENOMEM;
521597d0caeSDavid Teigland 	}
522597d0caeSDavid Teigland 
523597d0caeSDavid Teigland 	proc->lockspace = ls->ls_local_handle;
524597d0caeSDavid Teigland 	INIT_LIST_HEAD(&proc->asts);
525597d0caeSDavid Teigland 	INIT_LIST_HEAD(&proc->locks);
526597d0caeSDavid Teigland 	spin_lock_init(&proc->asts_spin);
527597d0caeSDavid Teigland 	spin_lock_init(&proc->locks_spin);
528597d0caeSDavid Teigland 	init_waitqueue_head(&proc->wait);
529597d0caeSDavid Teigland 	file->private_data = proc;
530597d0caeSDavid Teigland 
531597d0caeSDavid Teigland 	return 0;
532597d0caeSDavid Teigland }
533597d0caeSDavid Teigland 
534597d0caeSDavid Teigland static int device_close(struct inode *inode, struct file *file)
535597d0caeSDavid Teigland {
536597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
537597d0caeSDavid Teigland 	struct dlm_ls *ls;
538597d0caeSDavid Teigland 	sigset_t tmpsig, allsigs;
539597d0caeSDavid Teigland 
540597d0caeSDavid Teigland 	ls = dlm_find_lockspace_local(proc->lockspace);
541597d0caeSDavid Teigland 	if (!ls)
542597d0caeSDavid Teigland 		return -ENOENT;
543597d0caeSDavid Teigland 
544597d0caeSDavid Teigland 	sigfillset(&allsigs);
545597d0caeSDavid Teigland 	sigprocmask(SIG_BLOCK, &allsigs, &tmpsig);
546597d0caeSDavid Teigland 
547597d0caeSDavid Teigland 	set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags);
548597d0caeSDavid Teigland 
549597d0caeSDavid Teigland 	dlm_clear_proc_locks(ls, proc);
550597d0caeSDavid Teigland 
551597d0caeSDavid Teigland 	/* at this point no more lkb's should exist for this lockspace,
552597d0caeSDavid Teigland 	   so there's no chance of dlm_user_add_ast() being called and
553597d0caeSDavid Teigland 	   looking for lkb->ua->proc */
554597d0caeSDavid Teigland 
555597d0caeSDavid Teigland 	kfree(proc);
556597d0caeSDavid Teigland 	file->private_data = NULL;
557597d0caeSDavid Teigland 
558597d0caeSDavid Teigland 	dlm_put_lockspace(ls);
559597d0caeSDavid Teigland 	dlm_put_lockspace(ls);  /* for the find in device_open() */
560597d0caeSDavid Teigland 
561597d0caeSDavid Teigland 	/* FIXME: AUTOFREE: if this ls is no longer used do
562597d0caeSDavid Teigland 	   device_remove_lockspace() */
563597d0caeSDavid Teigland 
564597d0caeSDavid Teigland 	sigprocmask(SIG_SETMASK, &tmpsig, NULL);
565597d0caeSDavid Teigland 	recalc_sigpending();
566597d0caeSDavid Teigland 
567597d0caeSDavid Teigland 	return 0;
568597d0caeSDavid Teigland }
569597d0caeSDavid Teigland 
570597d0caeSDavid Teigland static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,
571597d0caeSDavid Teigland 			       int bmode, char __user *buf, size_t count)
572597d0caeSDavid Teigland {
573597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
574597d0caeSDavid Teigland 	struct dlm_lock_result32 result32;
575597d0caeSDavid Teigland #endif
576597d0caeSDavid Teigland 	struct dlm_lock_result result;
577597d0caeSDavid Teigland 	void *resultptr;
578597d0caeSDavid Teigland 	int error=0;
579597d0caeSDavid Teigland 	int len;
580597d0caeSDavid Teigland 	int struct_len;
581597d0caeSDavid Teigland 
582597d0caeSDavid Teigland 	memset(&result, 0, sizeof(struct dlm_lock_result));
583597d0caeSDavid Teigland 	memcpy(&result.lksb, &ua->lksb, sizeof(struct dlm_lksb));
584597d0caeSDavid Teigland 	result.user_lksb = ua->user_lksb;
585597d0caeSDavid Teigland 
586597d0caeSDavid Teigland 	/* FIXME: dlm1 provides for the user's bastparam/addr to not be updated
587597d0caeSDavid Teigland 	   in a conversion unless the conversion is successful.  See code
588597d0caeSDavid Teigland 	   in dlm_user_convert() for updating ua from ua_tmp.  OpenVMS, though,
589597d0caeSDavid Teigland 	   notes that a new blocking AST address and parameter are set even if
590597d0caeSDavid Teigland 	   the conversion fails, so maybe we should just do that. */
591597d0caeSDavid Teigland 
592597d0caeSDavid Teigland 	if (type == AST_BAST) {
593597d0caeSDavid Teigland 		result.user_astaddr = ua->bastaddr;
594597d0caeSDavid Teigland 		result.user_astparam = ua->bastparam;
595597d0caeSDavid Teigland 		result.bast_mode = bmode;
596597d0caeSDavid Teigland 	} else {
597597d0caeSDavid Teigland 		result.user_astaddr = ua->castaddr;
598597d0caeSDavid Teigland 		result.user_astparam = ua->castparam;
599597d0caeSDavid Teigland 	}
600597d0caeSDavid Teigland 
601597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
602597d0caeSDavid Teigland 	if (compat)
603597d0caeSDavid Teigland 		len = sizeof(struct dlm_lock_result32);
604597d0caeSDavid Teigland 	else
605597d0caeSDavid Teigland #endif
606597d0caeSDavid Teigland 		len = sizeof(struct dlm_lock_result);
607597d0caeSDavid Teigland 	struct_len = len;
608597d0caeSDavid Teigland 
609597d0caeSDavid Teigland 	/* copy lvb to userspace if there is one, it's been updated, and
610597d0caeSDavid Teigland 	   the user buffer has space for it */
611597d0caeSDavid Teigland 
612597d0caeSDavid Teigland 	if (ua->update_user_lvb && ua->lksb.sb_lvbptr &&
613597d0caeSDavid Teigland 	    count >= len + DLM_USER_LVB_LEN) {
614597d0caeSDavid Teigland 		if (copy_to_user(buf+len, ua->lksb.sb_lvbptr,
615597d0caeSDavid Teigland 				 DLM_USER_LVB_LEN)) {
616597d0caeSDavid Teigland 			error = -EFAULT;
617597d0caeSDavid Teigland 			goto out;
618597d0caeSDavid Teigland 		}
619597d0caeSDavid Teigland 
620597d0caeSDavid Teigland 		result.lvb_offset = len;
621597d0caeSDavid Teigland 		len += DLM_USER_LVB_LEN;
622597d0caeSDavid Teigland 	}
623597d0caeSDavid Teigland 
624597d0caeSDavid Teigland 	result.length = len;
625597d0caeSDavid Teigland 	resultptr = &result;
626597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
627597d0caeSDavid Teigland 	if (compat) {
628597d0caeSDavid Teigland 		compat_output(&result, &result32);
629597d0caeSDavid Teigland 		resultptr = &result32;
630597d0caeSDavid Teigland 	}
631597d0caeSDavid Teigland #endif
632597d0caeSDavid Teigland 
633597d0caeSDavid Teigland 	if (copy_to_user(buf, resultptr, struct_len))
634597d0caeSDavid Teigland 		error = -EFAULT;
635597d0caeSDavid Teigland 	else
636597d0caeSDavid Teigland 		error = len;
637597d0caeSDavid Teigland  out:
638597d0caeSDavid Teigland 	return error;
639597d0caeSDavid Teigland }
640597d0caeSDavid Teigland 
641597d0caeSDavid Teigland /* a read returns a single ast described in a struct dlm_lock_result */
642597d0caeSDavid Teigland 
643597d0caeSDavid Teigland static ssize_t device_read(struct file *file, char __user *buf, size_t count,
644597d0caeSDavid Teigland 			   loff_t *ppos)
645597d0caeSDavid Teigland {
646597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
647597d0caeSDavid Teigland 	struct dlm_lkb *lkb;
648597d0caeSDavid Teigland 	struct dlm_user_args *ua;
649597d0caeSDavid Teigland 	DECLARE_WAITQUEUE(wait, current);
650597d0caeSDavid Teigland 	int error, type=0, bmode=0, removed = 0;
651597d0caeSDavid Teigland 
652597d0caeSDavid Teigland #ifdef CONFIG_COMPAT
653597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_lock_result32))
654597d0caeSDavid Teigland #else
655597d0caeSDavid Teigland 	if (count < sizeof(struct dlm_lock_result))
656597d0caeSDavid Teigland #endif
657597d0caeSDavid Teigland 		return -EINVAL;
658597d0caeSDavid Teigland 
659597d0caeSDavid Teigland 	/* do we really need this? can a read happen after a close? */
660597d0caeSDavid Teigland 	if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
661597d0caeSDavid Teigland 		return -EINVAL;
662597d0caeSDavid Teigland 
663597d0caeSDavid Teigland 	spin_lock(&proc->asts_spin);
664597d0caeSDavid Teigland 	if (list_empty(&proc->asts)) {
665597d0caeSDavid Teigland 		if (file->f_flags & O_NONBLOCK) {
666597d0caeSDavid Teigland 			spin_unlock(&proc->asts_spin);
667597d0caeSDavid Teigland 			return -EAGAIN;
668597d0caeSDavid Teigland 		}
669597d0caeSDavid Teigland 
670597d0caeSDavid Teigland 		add_wait_queue(&proc->wait, &wait);
671597d0caeSDavid Teigland 
672597d0caeSDavid Teigland 	repeat:
673597d0caeSDavid Teigland 		set_current_state(TASK_INTERRUPTIBLE);
674597d0caeSDavid Teigland 		if (list_empty(&proc->asts) && !signal_pending(current)) {
675597d0caeSDavid Teigland 			spin_unlock(&proc->asts_spin);
676597d0caeSDavid Teigland 			schedule();
677597d0caeSDavid Teigland 			spin_lock(&proc->asts_spin);
678597d0caeSDavid Teigland 			goto repeat;
679597d0caeSDavid Teigland 		}
680597d0caeSDavid Teigland 		set_current_state(TASK_RUNNING);
681597d0caeSDavid Teigland 		remove_wait_queue(&proc->wait, &wait);
682597d0caeSDavid Teigland 
683597d0caeSDavid Teigland 		if (signal_pending(current)) {
684597d0caeSDavid Teigland 			spin_unlock(&proc->asts_spin);
685597d0caeSDavid Teigland 			return -ERESTARTSYS;
686597d0caeSDavid Teigland 		}
687597d0caeSDavid Teigland 	}
688597d0caeSDavid Teigland 
689597d0caeSDavid Teigland 	if (list_empty(&proc->asts)) {
690597d0caeSDavid Teigland 		spin_unlock(&proc->asts_spin);
691597d0caeSDavid Teigland 		return -EAGAIN;
692597d0caeSDavid Teigland 	}
693597d0caeSDavid Teigland 
694597d0caeSDavid Teigland 	/* there may be both completion and blocking asts to return for
695597d0caeSDavid Teigland 	   the lkb, don't remove lkb from asts list unless no asts remain */
696597d0caeSDavid Teigland 
697597d0caeSDavid Teigland 	lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue);
698597d0caeSDavid Teigland 
699597d0caeSDavid Teigland 	if (lkb->lkb_ast_type & AST_COMP) {
700597d0caeSDavid Teigland 		lkb->lkb_ast_type &= ~AST_COMP;
701597d0caeSDavid Teigland 		type = AST_COMP;
702597d0caeSDavid Teigland 	} else if (lkb->lkb_ast_type & AST_BAST) {
703597d0caeSDavid Teigland 		lkb->lkb_ast_type &= ~AST_BAST;
704597d0caeSDavid Teigland 		type = AST_BAST;
705597d0caeSDavid Teigland 		bmode = lkb->lkb_bastmode;
706597d0caeSDavid Teigland 	}
707597d0caeSDavid Teigland 
708597d0caeSDavid Teigland 	if (!lkb->lkb_ast_type) {
709597d0caeSDavid Teigland 		list_del(&lkb->lkb_astqueue);
710597d0caeSDavid Teigland 		removed = 1;
711597d0caeSDavid Teigland 	}
712597d0caeSDavid Teigland 	spin_unlock(&proc->asts_spin);
713597d0caeSDavid Teigland 
714597d0caeSDavid Teigland 	ua = (struct dlm_user_args *)lkb->lkb_astparam;
715597d0caeSDavid Teigland 	error = copy_result_to_user(ua,
716597d0caeSDavid Teigland 			 	test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
717597d0caeSDavid Teigland 				type, bmode, buf, count);
718597d0caeSDavid Teigland 
719597d0caeSDavid Teigland 	/* removes reference for the proc->asts lists added by
720597d0caeSDavid Teigland 	   dlm_user_add_ast() and may result in the lkb being freed */
721597d0caeSDavid Teigland 	if (removed)
722597d0caeSDavid Teigland 		dlm_put_lkb(lkb);
723597d0caeSDavid Teigland 
724597d0caeSDavid Teigland 	return error;
725597d0caeSDavid Teigland }
726597d0caeSDavid Teigland 
727597d0caeSDavid Teigland static unsigned int device_poll(struct file *file, poll_table *wait)
728597d0caeSDavid Teigland {
729597d0caeSDavid Teigland 	struct dlm_user_proc *proc = file->private_data;
730597d0caeSDavid Teigland 
731597d0caeSDavid Teigland 	poll_wait(file, &proc->wait, wait);
732597d0caeSDavid Teigland 
733597d0caeSDavid Teigland 	spin_lock(&proc->asts_spin);
734597d0caeSDavid Teigland 	if (!list_empty(&proc->asts)) {
735597d0caeSDavid Teigland 		spin_unlock(&proc->asts_spin);
736597d0caeSDavid Teigland 		return POLLIN | POLLRDNORM;
737597d0caeSDavid Teigland 	}
738597d0caeSDavid Teigland 	spin_unlock(&proc->asts_spin);
739597d0caeSDavid Teigland 	return 0;
740597d0caeSDavid Teigland }
741597d0caeSDavid Teigland 
742597d0caeSDavid Teigland static int ctl_device_open(struct inode *inode, struct file *file)
743597d0caeSDavid Teigland {
744597d0caeSDavid Teigland 	file->private_data = NULL;
745597d0caeSDavid Teigland 	return 0;
746597d0caeSDavid Teigland }
747597d0caeSDavid Teigland 
748597d0caeSDavid Teigland static int ctl_device_close(struct inode *inode, struct file *file)
749597d0caeSDavid Teigland {
750597d0caeSDavid Teigland 	return 0;
751597d0caeSDavid Teigland }
752597d0caeSDavid Teigland 
753597d0caeSDavid Teigland static struct file_operations device_fops = {
754597d0caeSDavid Teigland 	.open    = device_open,
755597d0caeSDavid Teigland 	.release = device_close,
756597d0caeSDavid Teigland 	.read    = device_read,
757597d0caeSDavid Teigland 	.write   = device_write,
758597d0caeSDavid Teigland 	.poll    = device_poll,
759597d0caeSDavid Teigland 	.owner   = THIS_MODULE,
760597d0caeSDavid Teigland };
761597d0caeSDavid Teigland 
762597d0caeSDavid Teigland static struct file_operations ctl_device_fops = {
763597d0caeSDavid Teigland 	.open    = ctl_device_open,
764597d0caeSDavid Teigland 	.release = ctl_device_close,
765597d0caeSDavid Teigland 	.write   = device_write,
766597d0caeSDavid Teigland 	.owner   = THIS_MODULE,
767597d0caeSDavid Teigland };
768597d0caeSDavid Teigland 
769597d0caeSDavid Teigland int dlm_user_init(void)
770597d0caeSDavid Teigland {
771597d0caeSDavid Teigland 	int error;
772597d0caeSDavid Teigland 
773597d0caeSDavid Teigland 	ctl_device.name = "dlm-control";
774597d0caeSDavid Teigland 	ctl_device.fops = &ctl_device_fops;
775597d0caeSDavid Teigland 	ctl_device.minor = MISC_DYNAMIC_MINOR;
776597d0caeSDavid Teigland 
777597d0caeSDavid Teigland 	error = misc_register(&ctl_device);
778597d0caeSDavid Teigland 	if (error)
779597d0caeSDavid Teigland 		log_print("misc_register failed for control device");
780597d0caeSDavid Teigland 
781597d0caeSDavid Teigland 	return error;
782597d0caeSDavid Teigland }
783597d0caeSDavid Teigland 
784597d0caeSDavid Teigland void dlm_user_exit(void)
785597d0caeSDavid Teigland {
786597d0caeSDavid Teigland 	misc_deregister(&ctl_device);
787597d0caeSDavid Teigland }
788597d0caeSDavid Teigland 
789