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