162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2006-2010 Red Hat, Inc.  All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/miscdevice.h>
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/wait.h>
962306a36Sopenharmony_ci#include <linux/file.h>
1062306a36Sopenharmony_ci#include <linux/fs.h>
1162306a36Sopenharmony_ci#include <linux/poll.h>
1262306a36Sopenharmony_ci#include <linux/signal.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci#include <linux/dlm.h>
1562306a36Sopenharmony_ci#include <linux/dlm_device.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/sched/signal.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <trace/events/dlm.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "dlm_internal.h"
2262306a36Sopenharmony_ci#include "lockspace.h"
2362306a36Sopenharmony_ci#include "lock.h"
2462306a36Sopenharmony_ci#include "lvb_table.h"
2562306a36Sopenharmony_ci#include "user.h"
2662306a36Sopenharmony_ci#include "ast.h"
2762306a36Sopenharmony_ci#include "config.h"
2862306a36Sopenharmony_ci#include "memory.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const char name_prefix[] = "dlm";
3162306a36Sopenharmony_cistatic const struct file_operations device_fops;
3262306a36Sopenharmony_cistatic atomic_t dlm_monitor_opened;
3362306a36Sopenharmony_cistatic int dlm_monitor_unused = 1;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct dlm_lock_params32 {
3862306a36Sopenharmony_ci	__u8 mode;
3962306a36Sopenharmony_ci	__u8 namelen;
4062306a36Sopenharmony_ci	__u16 unused;
4162306a36Sopenharmony_ci	__u32 flags;
4262306a36Sopenharmony_ci	__u32 lkid;
4362306a36Sopenharmony_ci	__u32 parent;
4462306a36Sopenharmony_ci	__u64 xid;
4562306a36Sopenharmony_ci	__u64 timeout;
4662306a36Sopenharmony_ci	__u32 castparam;
4762306a36Sopenharmony_ci	__u32 castaddr;
4862306a36Sopenharmony_ci	__u32 bastparam;
4962306a36Sopenharmony_ci	__u32 bastaddr;
5062306a36Sopenharmony_ci	__u32 lksb;
5162306a36Sopenharmony_ci	char lvb[DLM_USER_LVB_LEN];
5262306a36Sopenharmony_ci	char name[];
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct dlm_write_request32 {
5662306a36Sopenharmony_ci	__u32 version[3];
5762306a36Sopenharmony_ci	__u8 cmd;
5862306a36Sopenharmony_ci	__u8 is64bit;
5962306a36Sopenharmony_ci	__u8 unused[2];
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	union  {
6262306a36Sopenharmony_ci		struct dlm_lock_params32 lock;
6362306a36Sopenharmony_ci		struct dlm_lspace_params lspace;
6462306a36Sopenharmony_ci		struct dlm_purge_params purge;
6562306a36Sopenharmony_ci	} i;
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct dlm_lksb32 {
6962306a36Sopenharmony_ci	__u32 sb_status;
7062306a36Sopenharmony_ci	__u32 sb_lkid;
7162306a36Sopenharmony_ci	__u8 sb_flags;
7262306a36Sopenharmony_ci	__u32 sb_lvbptr;
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct dlm_lock_result32 {
7662306a36Sopenharmony_ci	__u32 version[3];
7762306a36Sopenharmony_ci	__u32 length;
7862306a36Sopenharmony_ci	__u32 user_astaddr;
7962306a36Sopenharmony_ci	__u32 user_astparam;
8062306a36Sopenharmony_ci	__u32 user_lksb;
8162306a36Sopenharmony_ci	struct dlm_lksb32 lksb;
8262306a36Sopenharmony_ci	__u8 bast_mode;
8362306a36Sopenharmony_ci	__u8 unused[3];
8462306a36Sopenharmony_ci	/* Offsets may be zero if no data is present */
8562306a36Sopenharmony_ci	__u32 lvb_offset;
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void compat_input(struct dlm_write_request *kb,
8962306a36Sopenharmony_ci			 struct dlm_write_request32 *kb32,
9062306a36Sopenharmony_ci			 int namelen)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	kb->version[0] = kb32->version[0];
9362306a36Sopenharmony_ci	kb->version[1] = kb32->version[1];
9462306a36Sopenharmony_ci	kb->version[2] = kb32->version[2];
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	kb->cmd = kb32->cmd;
9762306a36Sopenharmony_ci	kb->is64bit = kb32->is64bit;
9862306a36Sopenharmony_ci	if (kb->cmd == DLM_USER_CREATE_LOCKSPACE ||
9962306a36Sopenharmony_ci	    kb->cmd == DLM_USER_REMOVE_LOCKSPACE) {
10062306a36Sopenharmony_ci		kb->i.lspace.flags = kb32->i.lspace.flags;
10162306a36Sopenharmony_ci		kb->i.lspace.minor = kb32->i.lspace.minor;
10262306a36Sopenharmony_ci		memcpy(kb->i.lspace.name, kb32->i.lspace.name, namelen);
10362306a36Sopenharmony_ci	} else if (kb->cmd == DLM_USER_PURGE) {
10462306a36Sopenharmony_ci		kb->i.purge.nodeid = kb32->i.purge.nodeid;
10562306a36Sopenharmony_ci		kb->i.purge.pid = kb32->i.purge.pid;
10662306a36Sopenharmony_ci	} else {
10762306a36Sopenharmony_ci		kb->i.lock.mode = kb32->i.lock.mode;
10862306a36Sopenharmony_ci		kb->i.lock.namelen = kb32->i.lock.namelen;
10962306a36Sopenharmony_ci		kb->i.lock.flags = kb32->i.lock.flags;
11062306a36Sopenharmony_ci		kb->i.lock.lkid = kb32->i.lock.lkid;
11162306a36Sopenharmony_ci		kb->i.lock.parent = kb32->i.lock.parent;
11262306a36Sopenharmony_ci		kb->i.lock.xid = kb32->i.lock.xid;
11362306a36Sopenharmony_ci		kb->i.lock.timeout = kb32->i.lock.timeout;
11462306a36Sopenharmony_ci		kb->i.lock.castparam = (__user void *)(long)kb32->i.lock.castparam;
11562306a36Sopenharmony_ci		kb->i.lock.castaddr = (__user void *)(long)kb32->i.lock.castaddr;
11662306a36Sopenharmony_ci		kb->i.lock.bastparam = (__user void *)(long)kb32->i.lock.bastparam;
11762306a36Sopenharmony_ci		kb->i.lock.bastaddr = (__user void *)(long)kb32->i.lock.bastaddr;
11862306a36Sopenharmony_ci		kb->i.lock.lksb = (__user void *)(long)kb32->i.lock.lksb;
11962306a36Sopenharmony_ci		memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN);
12062306a36Sopenharmony_ci		memcpy(kb->i.lock.name, kb32->i.lock.name, namelen);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void compat_output(struct dlm_lock_result *res,
12562306a36Sopenharmony_ci			  struct dlm_lock_result32 *res32)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	memset(res32, 0, sizeof(*res32));
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	res32->version[0] = res->version[0];
13062306a36Sopenharmony_ci	res32->version[1] = res->version[1];
13162306a36Sopenharmony_ci	res32->version[2] = res->version[2];
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	res32->user_astaddr = (__u32)(__force long)res->user_astaddr;
13462306a36Sopenharmony_ci	res32->user_astparam = (__u32)(__force long)res->user_astparam;
13562306a36Sopenharmony_ci	res32->user_lksb = (__u32)(__force long)res->user_lksb;
13662306a36Sopenharmony_ci	res32->bast_mode = res->bast_mode;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	res32->lvb_offset = res->lvb_offset;
13962306a36Sopenharmony_ci	res32->length = res->length;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	res32->lksb.sb_status = res->lksb.sb_status;
14262306a36Sopenharmony_ci	res32->lksb.sb_flags = res->lksb.sb_flags;
14362306a36Sopenharmony_ci	res32->lksb.sb_lkid = res->lksb.sb_lkid;
14462306a36Sopenharmony_ci	res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci#endif
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/* should held proc->asts_spin lock */
14962306a36Sopenharmony_civoid dlm_purge_lkb_callbacks(struct dlm_lkb *lkb)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct dlm_callback *cb, *safe;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	list_for_each_entry_safe(cb, safe, &lkb->lkb_callbacks, list) {
15462306a36Sopenharmony_ci		list_del(&cb->list);
15562306a36Sopenharmony_ci		kref_put(&cb->ref, dlm_release_callback);
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	clear_bit(DLM_IFL_CB_PENDING_BIT, &lkb->lkb_iflags);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* invalidate */
16162306a36Sopenharmony_ci	dlm_callback_set_last_ptr(&lkb->lkb_last_cast, NULL);
16262306a36Sopenharmony_ci	dlm_callback_set_last_ptr(&lkb->lkb_last_cb, NULL);
16362306a36Sopenharmony_ci	lkb->lkb_last_bast_mode = -1;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/* Figure out if this lock is at the end of its life and no longer
16762306a36Sopenharmony_ci   available for the application to use.  The lkb still exists until
16862306a36Sopenharmony_ci   the final ast is read.  A lock becomes EOL in three situations:
16962306a36Sopenharmony_ci     1. a noqueue request fails with EAGAIN
17062306a36Sopenharmony_ci     2. an unlock completes with EUNLOCK
17162306a36Sopenharmony_ci     3. a cancel of a waiting request completes with ECANCEL/EDEADLK
17262306a36Sopenharmony_ci   An EOL lock needs to be removed from the process's list of locks.
17362306a36Sopenharmony_ci   And we can't allow any new operation on an EOL lock.  This is
17462306a36Sopenharmony_ci   not related to the lifetime of the lkb struct which is managed
17562306a36Sopenharmony_ci   entirely by refcount. */
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic int lkb_is_endoflife(int mode, int status)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	switch (status) {
18062306a36Sopenharmony_ci	case -DLM_EUNLOCK:
18162306a36Sopenharmony_ci		return 1;
18262306a36Sopenharmony_ci	case -DLM_ECANCEL:
18362306a36Sopenharmony_ci	case -ETIMEDOUT:
18462306a36Sopenharmony_ci	case -EDEADLK:
18562306a36Sopenharmony_ci	case -EAGAIN:
18662306a36Sopenharmony_ci		if (mode == DLM_LOCK_IV)
18762306a36Sopenharmony_ci			return 1;
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci	return 0;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/* we could possibly check if the cancel of an orphan has resulted in the lkb
19462306a36Sopenharmony_ci   being removed and then remove that lkb from the orphans list and free it */
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_civoid dlm_user_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode,
19762306a36Sopenharmony_ci		      int status, uint32_t sbflags)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct dlm_ls *ls;
20062306a36Sopenharmony_ci	struct dlm_user_args *ua;
20162306a36Sopenharmony_ci	struct dlm_user_proc *proc;
20262306a36Sopenharmony_ci	int rv;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (test_bit(DLM_DFL_ORPHAN_BIT, &lkb->lkb_dflags) ||
20562306a36Sopenharmony_ci	    test_bit(DLM_IFL_DEAD_BIT, &lkb->lkb_iflags))
20662306a36Sopenharmony_ci		return;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	ls = lkb->lkb_resource->res_ls;
20962306a36Sopenharmony_ci	spin_lock(&ls->ls_clear_proc_locks);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* If ORPHAN/DEAD flag is set, it means the process is dead so an ast
21262306a36Sopenharmony_ci	   can't be delivered.  For ORPHAN's, dlm_clear_proc_locks() freed
21362306a36Sopenharmony_ci	   lkb->ua so we can't try to use it.  This second check is necessary
21462306a36Sopenharmony_ci	   for cases where a completion ast is received for an operation that
21562306a36Sopenharmony_ci	   began before clear_proc_locks did its cancel/unlock. */
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (test_bit(DLM_DFL_ORPHAN_BIT, &lkb->lkb_dflags) ||
21862306a36Sopenharmony_ci	    test_bit(DLM_IFL_DEAD_BIT, &lkb->lkb_iflags))
21962306a36Sopenharmony_ci		goto out;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	DLM_ASSERT(lkb->lkb_ua, dlm_print_lkb(lkb););
22262306a36Sopenharmony_ci	ua = lkb->lkb_ua;
22362306a36Sopenharmony_ci	proc = ua->proc;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if ((flags & DLM_CB_BAST) && ua->bastaddr == NULL)
22662306a36Sopenharmony_ci		goto out;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if ((flags & DLM_CB_CAST) && lkb_is_endoflife(mode, status))
22962306a36Sopenharmony_ci		set_bit(DLM_IFL_ENDOFLIFE_BIT, &lkb->lkb_iflags);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	spin_lock(&proc->asts_spin);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	rv = dlm_enqueue_lkb_callback(lkb, flags, mode, status, sbflags);
23462306a36Sopenharmony_ci	switch (rv) {
23562306a36Sopenharmony_ci	case DLM_ENQUEUE_CALLBACK_FAILURE:
23662306a36Sopenharmony_ci		spin_unlock(&proc->asts_spin);
23762306a36Sopenharmony_ci		WARN_ON_ONCE(1);
23862306a36Sopenharmony_ci		goto out;
23962306a36Sopenharmony_ci	case DLM_ENQUEUE_CALLBACK_NEED_SCHED:
24062306a36Sopenharmony_ci		kref_get(&lkb->lkb_ref);
24162306a36Sopenharmony_ci		list_add_tail(&lkb->lkb_cb_list, &proc->asts);
24262306a36Sopenharmony_ci		wake_up_interruptible(&proc->wait);
24362306a36Sopenharmony_ci		break;
24462306a36Sopenharmony_ci	case DLM_ENQUEUE_CALLBACK_SUCCESS:
24562306a36Sopenharmony_ci		break;
24662306a36Sopenharmony_ci	default:
24762306a36Sopenharmony_ci		WARN_ON_ONCE(1);
24862306a36Sopenharmony_ci		break;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	spin_unlock(&proc->asts_spin);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (test_bit(DLM_IFL_ENDOFLIFE_BIT, &lkb->lkb_iflags)) {
25362306a36Sopenharmony_ci		/* N.B. spin_lock locks_spin, not asts_spin */
25462306a36Sopenharmony_ci		spin_lock(&proc->locks_spin);
25562306a36Sopenharmony_ci		if (!list_empty(&lkb->lkb_ownqueue)) {
25662306a36Sopenharmony_ci			list_del_init(&lkb->lkb_ownqueue);
25762306a36Sopenharmony_ci			dlm_put_lkb(lkb);
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci		spin_unlock(&proc->locks_spin);
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci out:
26262306a36Sopenharmony_ci	spin_unlock(&ls->ls_clear_proc_locks);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int device_user_lock(struct dlm_user_proc *proc,
26662306a36Sopenharmony_ci			    struct dlm_lock_params *params)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct dlm_ls *ls;
26962306a36Sopenharmony_ci	struct dlm_user_args *ua;
27062306a36Sopenharmony_ci	uint32_t lkid;
27162306a36Sopenharmony_ci	int error = -ENOMEM;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	ls = dlm_find_lockspace_local(proc->lockspace);
27462306a36Sopenharmony_ci	if (!ls)
27562306a36Sopenharmony_ci		return -ENOENT;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (!params->castaddr || !params->lksb) {
27862306a36Sopenharmony_ci		error = -EINVAL;
27962306a36Sopenharmony_ci		goto out;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS);
28362306a36Sopenharmony_ci	if (!ua)
28462306a36Sopenharmony_ci		goto out;
28562306a36Sopenharmony_ci	ua->proc = proc;
28662306a36Sopenharmony_ci	ua->user_lksb = params->lksb;
28762306a36Sopenharmony_ci	ua->castparam = params->castparam;
28862306a36Sopenharmony_ci	ua->castaddr = params->castaddr;
28962306a36Sopenharmony_ci	ua->bastparam = params->bastparam;
29062306a36Sopenharmony_ci	ua->bastaddr = params->bastaddr;
29162306a36Sopenharmony_ci	ua->xid = params->xid;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (params->flags & DLM_LKF_CONVERT) {
29462306a36Sopenharmony_ci		error = dlm_user_convert(ls, ua,
29562306a36Sopenharmony_ci					 params->mode, params->flags,
29662306a36Sopenharmony_ci					 params->lkid, params->lvb);
29762306a36Sopenharmony_ci	} else if (params->flags & DLM_LKF_ORPHAN) {
29862306a36Sopenharmony_ci		error = dlm_user_adopt_orphan(ls, ua,
29962306a36Sopenharmony_ci					 params->mode, params->flags,
30062306a36Sopenharmony_ci					 params->name, params->namelen,
30162306a36Sopenharmony_ci					 &lkid);
30262306a36Sopenharmony_ci		if (!error)
30362306a36Sopenharmony_ci			error = lkid;
30462306a36Sopenharmony_ci	} else {
30562306a36Sopenharmony_ci		error = dlm_user_request(ls, ua,
30662306a36Sopenharmony_ci					 params->mode, params->flags,
30762306a36Sopenharmony_ci					 params->name, params->namelen);
30862306a36Sopenharmony_ci		if (!error)
30962306a36Sopenharmony_ci			error = ua->lksb.sb_lkid;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci out:
31262306a36Sopenharmony_ci	dlm_put_lockspace(ls);
31362306a36Sopenharmony_ci	return error;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic int device_user_unlock(struct dlm_user_proc *proc,
31762306a36Sopenharmony_ci			      struct dlm_lock_params *params)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct dlm_ls *ls;
32062306a36Sopenharmony_ci	struct dlm_user_args *ua;
32162306a36Sopenharmony_ci	int error = -ENOMEM;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	ls = dlm_find_lockspace_local(proc->lockspace);
32462306a36Sopenharmony_ci	if (!ls)
32562306a36Sopenharmony_ci		return -ENOENT;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS);
32862306a36Sopenharmony_ci	if (!ua)
32962306a36Sopenharmony_ci		goto out;
33062306a36Sopenharmony_ci	ua->proc = proc;
33162306a36Sopenharmony_ci	ua->user_lksb = params->lksb;
33262306a36Sopenharmony_ci	ua->castparam = params->castparam;
33362306a36Sopenharmony_ci	ua->castaddr = params->castaddr;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (params->flags & DLM_LKF_CANCEL)
33662306a36Sopenharmony_ci		error = dlm_user_cancel(ls, ua, params->flags, params->lkid);
33762306a36Sopenharmony_ci	else
33862306a36Sopenharmony_ci		error = dlm_user_unlock(ls, ua, params->flags, params->lkid,
33962306a36Sopenharmony_ci					params->lvb);
34062306a36Sopenharmony_ci out:
34162306a36Sopenharmony_ci	dlm_put_lockspace(ls);
34262306a36Sopenharmony_ci	return error;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int device_user_deadlock(struct dlm_user_proc *proc,
34662306a36Sopenharmony_ci				struct dlm_lock_params *params)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct dlm_ls *ls;
34962306a36Sopenharmony_ci	int error;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	ls = dlm_find_lockspace_local(proc->lockspace);
35262306a36Sopenharmony_ci	if (!ls)
35362306a36Sopenharmony_ci		return -ENOENT;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	error = dlm_user_deadlock(ls, params->flags, params->lkid);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	dlm_put_lockspace(ls);
35862306a36Sopenharmony_ci	return error;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int dlm_device_register(struct dlm_ls *ls, char *name)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	int error, len;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/* The device is already registered.  This happens when the
36662306a36Sopenharmony_ci	   lockspace is created multiple times from userspace. */
36762306a36Sopenharmony_ci	if (ls->ls_device.name)
36862306a36Sopenharmony_ci		return 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	error = -ENOMEM;
37162306a36Sopenharmony_ci	len = strlen(name) + strlen(name_prefix) + 2;
37262306a36Sopenharmony_ci	ls->ls_device.name = kzalloc(len, GFP_NOFS);
37362306a36Sopenharmony_ci	if (!ls->ls_device.name)
37462306a36Sopenharmony_ci		goto fail;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix,
37762306a36Sopenharmony_ci		 name);
37862306a36Sopenharmony_ci	ls->ls_device.fops = &device_fops;
37962306a36Sopenharmony_ci	ls->ls_device.minor = MISC_DYNAMIC_MINOR;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	error = misc_register(&ls->ls_device);
38262306a36Sopenharmony_ci	if (error) {
38362306a36Sopenharmony_ci		kfree(ls->ls_device.name);
38462306a36Sopenharmony_ci		/* this has to be set to NULL
38562306a36Sopenharmony_ci		 * to avoid a double-free in dlm_device_deregister
38662306a36Sopenharmony_ci		 */
38762306a36Sopenharmony_ci		ls->ls_device.name = NULL;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_cifail:
39062306a36Sopenharmony_ci	return error;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciint dlm_device_deregister(struct dlm_ls *ls)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	/* The device is not registered.  This happens when the lockspace
39662306a36Sopenharmony_ci	   was never used from userspace, or when device_create_lockspace()
39762306a36Sopenharmony_ci	   calls dlm_release_lockspace() after the register fails. */
39862306a36Sopenharmony_ci	if (!ls->ls_device.name)
39962306a36Sopenharmony_ci		return 0;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	misc_deregister(&ls->ls_device);
40262306a36Sopenharmony_ci	kfree(ls->ls_device.name);
40362306a36Sopenharmony_ci	return 0;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int device_user_purge(struct dlm_user_proc *proc,
40762306a36Sopenharmony_ci			     struct dlm_purge_params *params)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct dlm_ls *ls;
41062306a36Sopenharmony_ci	int error;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	ls = dlm_find_lockspace_local(proc->lockspace);
41362306a36Sopenharmony_ci	if (!ls)
41462306a36Sopenharmony_ci		return -ENOENT;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	error = dlm_user_purge(ls, proc, params->nodeid, params->pid);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	dlm_put_lockspace(ls);
41962306a36Sopenharmony_ci	return error;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int device_create_lockspace(struct dlm_lspace_params *params)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	dlm_lockspace_t *lockspace;
42562306a36Sopenharmony_ci	struct dlm_ls *ls;
42662306a36Sopenharmony_ci	int error;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
42962306a36Sopenharmony_ci		return -EPERM;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	error = dlm_new_user_lockspace(params->name, dlm_config.ci_cluster_name,
43262306a36Sopenharmony_ci				       params->flags, DLM_USER_LVB_LEN, NULL,
43362306a36Sopenharmony_ci				       NULL, NULL, &lockspace);
43462306a36Sopenharmony_ci	if (error)
43562306a36Sopenharmony_ci		return error;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	ls = dlm_find_lockspace_local(lockspace);
43862306a36Sopenharmony_ci	if (!ls)
43962306a36Sopenharmony_ci		return -ENOENT;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	error = dlm_device_register(ls, params->name);
44262306a36Sopenharmony_ci	dlm_put_lockspace(ls);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if (error)
44562306a36Sopenharmony_ci		dlm_release_lockspace(lockspace, 0);
44662306a36Sopenharmony_ci	else
44762306a36Sopenharmony_ci		error = ls->ls_device.minor;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return error;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int device_remove_lockspace(struct dlm_lspace_params *params)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	dlm_lockspace_t *lockspace;
45562306a36Sopenharmony_ci	struct dlm_ls *ls;
45662306a36Sopenharmony_ci	int error, force = 0;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
45962306a36Sopenharmony_ci		return -EPERM;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	ls = dlm_find_lockspace_device(params->minor);
46262306a36Sopenharmony_ci	if (!ls)
46362306a36Sopenharmony_ci		return -ENOENT;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (params->flags & DLM_USER_LSFLG_FORCEFREE)
46662306a36Sopenharmony_ci		force = 2;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	lockspace = ls->ls_local_handle;
46962306a36Sopenharmony_ci	dlm_put_lockspace(ls);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* The final dlm_release_lockspace waits for references to go to
47262306a36Sopenharmony_ci	   zero, so all processes will need to close their device for the
47362306a36Sopenharmony_ci	   ls before the release will proceed.  release also calls the
47462306a36Sopenharmony_ci	   device_deregister above.  Converting a positive return value
47562306a36Sopenharmony_ci	   from release to zero means that userspace won't know when its
47662306a36Sopenharmony_ci	   release was the final one, but it shouldn't need to know. */
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	error = dlm_release_lockspace(lockspace, force);
47962306a36Sopenharmony_ci	if (error > 0)
48062306a36Sopenharmony_ci		error = 0;
48162306a36Sopenharmony_ci	return error;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci/* Check the user's version matches ours */
48562306a36Sopenharmony_cistatic int check_version(struct dlm_write_request *req)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	if (req->version[0] != DLM_DEVICE_VERSION_MAJOR ||
48862306a36Sopenharmony_ci	    (req->version[0] == DLM_DEVICE_VERSION_MAJOR &&
48962306a36Sopenharmony_ci	     req->version[1] > DLM_DEVICE_VERSION_MINOR)) {
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		printk(KERN_DEBUG "dlm: process %s (%d) version mismatch "
49262306a36Sopenharmony_ci		       "user (%d.%d.%d) kernel (%d.%d.%d)\n",
49362306a36Sopenharmony_ci		       current->comm,
49462306a36Sopenharmony_ci		       task_pid_nr(current),
49562306a36Sopenharmony_ci		       req->version[0],
49662306a36Sopenharmony_ci		       req->version[1],
49762306a36Sopenharmony_ci		       req->version[2],
49862306a36Sopenharmony_ci		       DLM_DEVICE_VERSION_MAJOR,
49962306a36Sopenharmony_ci		       DLM_DEVICE_VERSION_MINOR,
50062306a36Sopenharmony_ci		       DLM_DEVICE_VERSION_PATCH);
50162306a36Sopenharmony_ci		return -EINVAL;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci	return 0;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/*
50762306a36Sopenharmony_ci * device_write
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci *   device_user_lock
51062306a36Sopenharmony_ci *     dlm_user_request -> request_lock
51162306a36Sopenharmony_ci *     dlm_user_convert -> convert_lock
51262306a36Sopenharmony_ci *
51362306a36Sopenharmony_ci *   device_user_unlock
51462306a36Sopenharmony_ci *     dlm_user_unlock -> unlock_lock
51562306a36Sopenharmony_ci *     dlm_user_cancel -> cancel_lock
51662306a36Sopenharmony_ci *
51762306a36Sopenharmony_ci *   device_create_lockspace
51862306a36Sopenharmony_ci *     dlm_new_lockspace
51962306a36Sopenharmony_ci *
52062306a36Sopenharmony_ci *   device_remove_lockspace
52162306a36Sopenharmony_ci *     dlm_release_lockspace
52262306a36Sopenharmony_ci */
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci/* a write to a lockspace device is a lock or unlock request, a write
52562306a36Sopenharmony_ci   to the control device is to create/remove a lockspace */
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic ssize_t device_write(struct file *file, const char __user *buf,
52862306a36Sopenharmony_ci			    size_t count, loff_t *ppos)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct dlm_user_proc *proc = file->private_data;
53162306a36Sopenharmony_ci	struct dlm_write_request *kbuf;
53262306a36Sopenharmony_ci	int error;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
53562306a36Sopenharmony_ci	if (count < sizeof(struct dlm_write_request32))
53662306a36Sopenharmony_ci#else
53762306a36Sopenharmony_ci	if (count < sizeof(struct dlm_write_request))
53862306a36Sopenharmony_ci#endif
53962306a36Sopenharmony_ci		return -EINVAL;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/*
54262306a36Sopenharmony_ci	 * can't compare against COMPAT/dlm_write_request32 because
54362306a36Sopenharmony_ci	 * we don't yet know if is64bit is zero
54462306a36Sopenharmony_ci	 */
54562306a36Sopenharmony_ci	if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN)
54662306a36Sopenharmony_ci		return -EINVAL;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	kbuf = memdup_user_nul(buf, count);
54962306a36Sopenharmony_ci	if (IS_ERR(kbuf))
55062306a36Sopenharmony_ci		return PTR_ERR(kbuf);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (check_version(kbuf)) {
55362306a36Sopenharmony_ci		error = -EBADE;
55462306a36Sopenharmony_ci		goto out_free;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
55862306a36Sopenharmony_ci	if (!kbuf->is64bit) {
55962306a36Sopenharmony_ci		struct dlm_write_request32 *k32buf;
56062306a36Sopenharmony_ci		int namelen = 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		if (count > sizeof(struct dlm_write_request32))
56362306a36Sopenharmony_ci			namelen = count - sizeof(struct dlm_write_request32);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci		k32buf = (struct dlm_write_request32 *)kbuf;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		/* add 1 after namelen so that the name string is terminated */
56862306a36Sopenharmony_ci		kbuf = kzalloc(sizeof(struct dlm_write_request) + namelen + 1,
56962306a36Sopenharmony_ci			       GFP_NOFS);
57062306a36Sopenharmony_ci		if (!kbuf) {
57162306a36Sopenharmony_ci			kfree(k32buf);
57262306a36Sopenharmony_ci			return -ENOMEM;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		if (proc)
57662306a36Sopenharmony_ci			set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		compat_input(kbuf, k32buf, namelen);
57962306a36Sopenharmony_ci		kfree(k32buf);
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci#endif
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* do we really need this? can a write happen after a close? */
58462306a36Sopenharmony_ci	if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) &&
58562306a36Sopenharmony_ci	    (proc && test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))) {
58662306a36Sopenharmony_ci		error = -EINVAL;
58762306a36Sopenharmony_ci		goto out_free;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	error = -EINVAL;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	switch (kbuf->cmd)
59362306a36Sopenharmony_ci	{
59462306a36Sopenharmony_ci	case DLM_USER_LOCK:
59562306a36Sopenharmony_ci		if (!proc) {
59662306a36Sopenharmony_ci			log_print("no locking on control device");
59762306a36Sopenharmony_ci			goto out_free;
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci		error = device_user_lock(proc, &kbuf->i.lock);
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	case DLM_USER_UNLOCK:
60362306a36Sopenharmony_ci		if (!proc) {
60462306a36Sopenharmony_ci			log_print("no locking on control device");
60562306a36Sopenharmony_ci			goto out_free;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci		error = device_user_unlock(proc, &kbuf->i.lock);
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	case DLM_USER_DEADLOCK:
61162306a36Sopenharmony_ci		if (!proc) {
61262306a36Sopenharmony_ci			log_print("no locking on control device");
61362306a36Sopenharmony_ci			goto out_free;
61462306a36Sopenharmony_ci		}
61562306a36Sopenharmony_ci		error = device_user_deadlock(proc, &kbuf->i.lock);
61662306a36Sopenharmony_ci		break;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	case DLM_USER_CREATE_LOCKSPACE:
61962306a36Sopenharmony_ci		if (proc) {
62062306a36Sopenharmony_ci			log_print("create/remove only on control device");
62162306a36Sopenharmony_ci			goto out_free;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci		error = device_create_lockspace(&kbuf->i.lspace);
62462306a36Sopenharmony_ci		break;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	case DLM_USER_REMOVE_LOCKSPACE:
62762306a36Sopenharmony_ci		if (proc) {
62862306a36Sopenharmony_ci			log_print("create/remove only on control device");
62962306a36Sopenharmony_ci			goto out_free;
63062306a36Sopenharmony_ci		}
63162306a36Sopenharmony_ci		error = device_remove_lockspace(&kbuf->i.lspace);
63262306a36Sopenharmony_ci		break;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	case DLM_USER_PURGE:
63562306a36Sopenharmony_ci		if (!proc) {
63662306a36Sopenharmony_ci			log_print("no locking on control device");
63762306a36Sopenharmony_ci			goto out_free;
63862306a36Sopenharmony_ci		}
63962306a36Sopenharmony_ci		error = device_user_purge(proc, &kbuf->i.purge);
64062306a36Sopenharmony_ci		break;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	default:
64362306a36Sopenharmony_ci		log_print("Unknown command passed to DLM device : %d\n",
64462306a36Sopenharmony_ci			  kbuf->cmd);
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci out_free:
64862306a36Sopenharmony_ci	kfree(kbuf);
64962306a36Sopenharmony_ci	return error;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci/* Every process that opens the lockspace device has its own "proc" structure
65362306a36Sopenharmony_ci   hanging off the open file that's used to keep track of locks owned by the
65462306a36Sopenharmony_ci   process and asts that need to be delivered to the process. */
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic int device_open(struct inode *inode, struct file *file)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct dlm_user_proc *proc;
65962306a36Sopenharmony_ci	struct dlm_ls *ls;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	ls = dlm_find_lockspace_device(iminor(inode));
66262306a36Sopenharmony_ci	if (!ls)
66362306a36Sopenharmony_ci		return -ENOENT;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	proc = kzalloc(sizeof(struct dlm_user_proc), GFP_NOFS);
66662306a36Sopenharmony_ci	if (!proc) {
66762306a36Sopenharmony_ci		dlm_put_lockspace(ls);
66862306a36Sopenharmony_ci		return -ENOMEM;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	proc->lockspace = ls->ls_local_handle;
67262306a36Sopenharmony_ci	INIT_LIST_HEAD(&proc->asts);
67362306a36Sopenharmony_ci	INIT_LIST_HEAD(&proc->locks);
67462306a36Sopenharmony_ci	INIT_LIST_HEAD(&proc->unlocking);
67562306a36Sopenharmony_ci	spin_lock_init(&proc->asts_spin);
67662306a36Sopenharmony_ci	spin_lock_init(&proc->locks_spin);
67762306a36Sopenharmony_ci	init_waitqueue_head(&proc->wait);
67862306a36Sopenharmony_ci	file->private_data = proc;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int device_close(struct inode *inode, struct file *file)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct dlm_user_proc *proc = file->private_data;
68662306a36Sopenharmony_ci	struct dlm_ls *ls;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	ls = dlm_find_lockspace_local(proc->lockspace);
68962306a36Sopenharmony_ci	if (!ls)
69062306a36Sopenharmony_ci		return -ENOENT;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	dlm_clear_proc_locks(ls, proc);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	/* at this point no more lkb's should exist for this lockspace,
69762306a36Sopenharmony_ci	   so there's no chance of dlm_user_add_ast() being called and
69862306a36Sopenharmony_ci	   looking for lkb->ua->proc */
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	kfree(proc);
70162306a36Sopenharmony_ci	file->private_data = NULL;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	dlm_put_lockspace(ls);
70462306a36Sopenharmony_ci	dlm_put_lockspace(ls);  /* for the find in device_open() */
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	/* FIXME: AUTOFREE: if this ls is no longer used do
70762306a36Sopenharmony_ci	   device_remove_lockspace() */
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	return 0;
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic int copy_result_to_user(struct dlm_user_args *ua, int compat,
71362306a36Sopenharmony_ci			       uint32_t flags, int mode, int copy_lvb,
71462306a36Sopenharmony_ci			       char __user *buf, size_t count)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
71762306a36Sopenharmony_ci	struct dlm_lock_result32 result32;
71862306a36Sopenharmony_ci#endif
71962306a36Sopenharmony_ci	struct dlm_lock_result result;
72062306a36Sopenharmony_ci	void *resultptr;
72162306a36Sopenharmony_ci	int error=0;
72262306a36Sopenharmony_ci	int len;
72362306a36Sopenharmony_ci	int struct_len;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	memset(&result, 0, sizeof(struct dlm_lock_result));
72662306a36Sopenharmony_ci	result.version[0] = DLM_DEVICE_VERSION_MAJOR;
72762306a36Sopenharmony_ci	result.version[1] = DLM_DEVICE_VERSION_MINOR;
72862306a36Sopenharmony_ci	result.version[2] = DLM_DEVICE_VERSION_PATCH;
72962306a36Sopenharmony_ci	memcpy(&result.lksb, &ua->lksb, offsetof(struct dlm_lksb, sb_lvbptr));
73062306a36Sopenharmony_ci	result.user_lksb = ua->user_lksb;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/* FIXME: dlm1 provides for the user's bastparam/addr to not be updated
73362306a36Sopenharmony_ci	   in a conversion unless the conversion is successful.  See code
73462306a36Sopenharmony_ci	   in dlm_user_convert() for updating ua from ua_tmp.  OpenVMS, though,
73562306a36Sopenharmony_ci	   notes that a new blocking AST address and parameter are set even if
73662306a36Sopenharmony_ci	   the conversion fails, so maybe we should just do that. */
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (flags & DLM_CB_BAST) {
73962306a36Sopenharmony_ci		result.user_astaddr = ua->bastaddr;
74062306a36Sopenharmony_ci		result.user_astparam = ua->bastparam;
74162306a36Sopenharmony_ci		result.bast_mode = mode;
74262306a36Sopenharmony_ci	} else {
74362306a36Sopenharmony_ci		result.user_astaddr = ua->castaddr;
74462306a36Sopenharmony_ci		result.user_astparam = ua->castparam;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
74862306a36Sopenharmony_ci	if (compat)
74962306a36Sopenharmony_ci		len = sizeof(struct dlm_lock_result32);
75062306a36Sopenharmony_ci	else
75162306a36Sopenharmony_ci#endif
75262306a36Sopenharmony_ci		len = sizeof(struct dlm_lock_result);
75362306a36Sopenharmony_ci	struct_len = len;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* copy lvb to userspace if there is one, it's been updated, and
75662306a36Sopenharmony_ci	   the user buffer has space for it */
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (copy_lvb && ua->lksb.sb_lvbptr && count >= len + DLM_USER_LVB_LEN) {
75962306a36Sopenharmony_ci		if (copy_to_user(buf+len, ua->lksb.sb_lvbptr,
76062306a36Sopenharmony_ci				 DLM_USER_LVB_LEN)) {
76162306a36Sopenharmony_ci			error = -EFAULT;
76262306a36Sopenharmony_ci			goto out;
76362306a36Sopenharmony_ci		}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci		result.lvb_offset = len;
76662306a36Sopenharmony_ci		len += DLM_USER_LVB_LEN;
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	result.length = len;
77062306a36Sopenharmony_ci	resultptr = &result;
77162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
77262306a36Sopenharmony_ci	if (compat) {
77362306a36Sopenharmony_ci		compat_output(&result, &result32);
77462306a36Sopenharmony_ci		resultptr = &result32;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci#endif
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (copy_to_user(buf, resultptr, struct_len))
77962306a36Sopenharmony_ci		error = -EFAULT;
78062306a36Sopenharmony_ci	else
78162306a36Sopenharmony_ci		error = len;
78262306a36Sopenharmony_ci out:
78362306a36Sopenharmony_ci	return error;
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic int copy_version_to_user(char __user *buf, size_t count)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	struct dlm_device_version ver;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	memset(&ver, 0, sizeof(struct dlm_device_version));
79162306a36Sopenharmony_ci	ver.version[0] = DLM_DEVICE_VERSION_MAJOR;
79262306a36Sopenharmony_ci	ver.version[1] = DLM_DEVICE_VERSION_MINOR;
79362306a36Sopenharmony_ci	ver.version[2] = DLM_DEVICE_VERSION_PATCH;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (copy_to_user(buf, &ver, sizeof(struct dlm_device_version)))
79662306a36Sopenharmony_ci		return -EFAULT;
79762306a36Sopenharmony_ci	return sizeof(struct dlm_device_version);
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci/* a read returns a single ast described in a struct dlm_lock_result */
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic ssize_t device_read(struct file *file, char __user *buf, size_t count,
80362306a36Sopenharmony_ci			   loff_t *ppos)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct dlm_user_proc *proc = file->private_data;
80662306a36Sopenharmony_ci	struct dlm_lkb *lkb;
80762306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
80862306a36Sopenharmony_ci	struct dlm_callback *cb;
80962306a36Sopenharmony_ci	int rv, copy_lvb = 0;
81062306a36Sopenharmony_ci	int old_mode, new_mode;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	if (count == sizeof(struct dlm_device_version)) {
81362306a36Sopenharmony_ci		rv = copy_version_to_user(buf, count);
81462306a36Sopenharmony_ci		return rv;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (!proc) {
81862306a36Sopenharmony_ci		log_print("non-version read from control device %zu", count);
81962306a36Sopenharmony_ci		return -EINVAL;
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
82362306a36Sopenharmony_ci	if (count < sizeof(struct dlm_lock_result32))
82462306a36Sopenharmony_ci#else
82562306a36Sopenharmony_ci	if (count < sizeof(struct dlm_lock_result))
82662306a36Sopenharmony_ci#endif
82762306a36Sopenharmony_ci		return -EINVAL;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci try_another:
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/* do we really need this? can a read happen after a close? */
83262306a36Sopenharmony_ci	if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
83362306a36Sopenharmony_ci		return -EINVAL;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	spin_lock(&proc->asts_spin);
83662306a36Sopenharmony_ci	if (list_empty(&proc->asts)) {
83762306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
83862306a36Sopenharmony_ci			spin_unlock(&proc->asts_spin);
83962306a36Sopenharmony_ci			return -EAGAIN;
84062306a36Sopenharmony_ci		}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		add_wait_queue(&proc->wait, &wait);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	repeat:
84562306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
84662306a36Sopenharmony_ci		if (list_empty(&proc->asts) && !signal_pending(current)) {
84762306a36Sopenharmony_ci			spin_unlock(&proc->asts_spin);
84862306a36Sopenharmony_ci			schedule();
84962306a36Sopenharmony_ci			spin_lock(&proc->asts_spin);
85062306a36Sopenharmony_ci			goto repeat;
85162306a36Sopenharmony_ci		}
85262306a36Sopenharmony_ci		set_current_state(TASK_RUNNING);
85362306a36Sopenharmony_ci		remove_wait_queue(&proc->wait, &wait);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		if (signal_pending(current)) {
85662306a36Sopenharmony_ci			spin_unlock(&proc->asts_spin);
85762306a36Sopenharmony_ci			return -ERESTARTSYS;
85862306a36Sopenharmony_ci		}
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	/* if we empty lkb_callbacks, we don't want to unlock the spinlock
86262306a36Sopenharmony_ci	   without removing lkb_cb_list; so empty lkb_cb_list is always
86362306a36Sopenharmony_ci	   consistent with empty lkb_callbacks */
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	lkb = list_first_entry(&proc->asts, struct dlm_lkb, lkb_cb_list);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	/* rem_lkb_callback sets a new lkb_last_cast */
86862306a36Sopenharmony_ci	old_mode = lkb->lkb_last_cast->mode;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	rv = dlm_dequeue_lkb_callback(lkb, &cb);
87162306a36Sopenharmony_ci	switch (rv) {
87262306a36Sopenharmony_ci	case DLM_DEQUEUE_CALLBACK_EMPTY:
87362306a36Sopenharmony_ci		/* this shouldn't happen; lkb should have been removed from
87462306a36Sopenharmony_ci		 * list when last item was dequeued
87562306a36Sopenharmony_ci		 */
87662306a36Sopenharmony_ci		log_print("dlm_rem_lkb_callback empty %x", lkb->lkb_id);
87762306a36Sopenharmony_ci		list_del_init(&lkb->lkb_cb_list);
87862306a36Sopenharmony_ci		spin_unlock(&proc->asts_spin);
87962306a36Sopenharmony_ci		/* removes ref for proc->asts, may cause lkb to be freed */
88062306a36Sopenharmony_ci		dlm_put_lkb(lkb);
88162306a36Sopenharmony_ci		WARN_ON_ONCE(1);
88262306a36Sopenharmony_ci		goto try_another;
88362306a36Sopenharmony_ci	case DLM_DEQUEUE_CALLBACK_LAST:
88462306a36Sopenharmony_ci		list_del_init(&lkb->lkb_cb_list);
88562306a36Sopenharmony_ci		clear_bit(DLM_IFL_CB_PENDING_BIT, &lkb->lkb_iflags);
88662306a36Sopenharmony_ci		break;
88762306a36Sopenharmony_ci	case DLM_DEQUEUE_CALLBACK_SUCCESS:
88862306a36Sopenharmony_ci		break;
88962306a36Sopenharmony_ci	default:
89062306a36Sopenharmony_ci		WARN_ON_ONCE(1);
89162306a36Sopenharmony_ci		break;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci	spin_unlock(&proc->asts_spin);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (cb->flags & DLM_CB_BAST) {
89662306a36Sopenharmony_ci		trace_dlm_bast(lkb->lkb_resource->res_ls, lkb, cb->mode);
89762306a36Sopenharmony_ci	} else if (cb->flags & DLM_CB_CAST) {
89862306a36Sopenharmony_ci		new_mode = cb->mode;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		if (!cb->sb_status && lkb->lkb_lksb->sb_lvbptr &&
90162306a36Sopenharmony_ci		    dlm_lvb_operations[old_mode + 1][new_mode + 1])
90262306a36Sopenharmony_ci			copy_lvb = 1;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		lkb->lkb_lksb->sb_status = cb->sb_status;
90562306a36Sopenharmony_ci		lkb->lkb_lksb->sb_flags = cb->sb_flags;
90662306a36Sopenharmony_ci		trace_dlm_ast(lkb->lkb_resource->res_ls, lkb);
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	rv = copy_result_to_user(lkb->lkb_ua,
91062306a36Sopenharmony_ci				 test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
91162306a36Sopenharmony_ci				 cb->flags, cb->mode, copy_lvb, buf, count);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	kref_put(&cb->ref, dlm_release_callback);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* removes ref for proc->asts, may cause lkb to be freed */
91662306a36Sopenharmony_ci	if (rv == DLM_DEQUEUE_CALLBACK_LAST)
91762306a36Sopenharmony_ci		dlm_put_lkb(lkb);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	return rv;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic __poll_t device_poll(struct file *file, poll_table *wait)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct dlm_user_proc *proc = file->private_data;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	poll_wait(file, &proc->wait, wait);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	spin_lock(&proc->asts_spin);
92962306a36Sopenharmony_ci	if (!list_empty(&proc->asts)) {
93062306a36Sopenharmony_ci		spin_unlock(&proc->asts_spin);
93162306a36Sopenharmony_ci		return EPOLLIN | EPOLLRDNORM;
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci	spin_unlock(&proc->asts_spin);
93462306a36Sopenharmony_ci	return 0;
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ciint dlm_user_daemon_available(void)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	/* dlm_controld hasn't started (or, has started, but not
94062306a36Sopenharmony_ci	   properly populated configfs) */
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (!dlm_our_nodeid())
94362306a36Sopenharmony_ci		return 0;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	/* This is to deal with versions of dlm_controld that don't
94662306a36Sopenharmony_ci	   know about the monitor device.  We assume that if the
94762306a36Sopenharmony_ci	   dlm_controld was started (above), but the monitor device
94862306a36Sopenharmony_ci	   was never opened, that it's an old version.  dlm_controld
94962306a36Sopenharmony_ci	   should open the monitor device before populating configfs. */
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if (dlm_monitor_unused)
95262306a36Sopenharmony_ci		return 1;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	return atomic_read(&dlm_monitor_opened) ? 1 : 0;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_cistatic int ctl_device_open(struct inode *inode, struct file *file)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	file->private_data = NULL;
96062306a36Sopenharmony_ci	return 0;
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic int ctl_device_close(struct inode *inode, struct file *file)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	return 0;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int monitor_device_open(struct inode *inode, struct file *file)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	atomic_inc(&dlm_monitor_opened);
97162306a36Sopenharmony_ci	dlm_monitor_unused = 0;
97262306a36Sopenharmony_ci	return 0;
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic int monitor_device_close(struct inode *inode, struct file *file)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	if (atomic_dec_and_test(&dlm_monitor_opened))
97862306a36Sopenharmony_ci		dlm_stop_lockspaces();
97962306a36Sopenharmony_ci	return 0;
98062306a36Sopenharmony_ci}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistatic const struct file_operations device_fops = {
98362306a36Sopenharmony_ci	.open    = device_open,
98462306a36Sopenharmony_ci	.release = device_close,
98562306a36Sopenharmony_ci	.read    = device_read,
98662306a36Sopenharmony_ci	.write   = device_write,
98762306a36Sopenharmony_ci	.poll    = device_poll,
98862306a36Sopenharmony_ci	.owner   = THIS_MODULE,
98962306a36Sopenharmony_ci	.llseek  = noop_llseek,
99062306a36Sopenharmony_ci};
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_cistatic const struct file_operations ctl_device_fops = {
99362306a36Sopenharmony_ci	.open    = ctl_device_open,
99462306a36Sopenharmony_ci	.release = ctl_device_close,
99562306a36Sopenharmony_ci	.read    = device_read,
99662306a36Sopenharmony_ci	.write   = device_write,
99762306a36Sopenharmony_ci	.owner   = THIS_MODULE,
99862306a36Sopenharmony_ci	.llseek  = noop_llseek,
99962306a36Sopenharmony_ci};
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic struct miscdevice ctl_device = {
100262306a36Sopenharmony_ci	.name  = "dlm-control",
100362306a36Sopenharmony_ci	.fops  = &ctl_device_fops,
100462306a36Sopenharmony_ci	.minor = MISC_DYNAMIC_MINOR,
100562306a36Sopenharmony_ci};
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic const struct file_operations monitor_device_fops = {
100862306a36Sopenharmony_ci	.open    = monitor_device_open,
100962306a36Sopenharmony_ci	.release = monitor_device_close,
101062306a36Sopenharmony_ci	.owner   = THIS_MODULE,
101162306a36Sopenharmony_ci	.llseek  = noop_llseek,
101262306a36Sopenharmony_ci};
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cistatic struct miscdevice monitor_device = {
101562306a36Sopenharmony_ci	.name  = "dlm-monitor",
101662306a36Sopenharmony_ci	.fops  = &monitor_device_fops,
101762306a36Sopenharmony_ci	.minor = MISC_DYNAMIC_MINOR,
101862306a36Sopenharmony_ci};
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ciint __init dlm_user_init(void)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	int error;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	atomic_set(&dlm_monitor_opened, 0);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	error = misc_register(&ctl_device);
102762306a36Sopenharmony_ci	if (error) {
102862306a36Sopenharmony_ci		log_print("misc_register failed for control device");
102962306a36Sopenharmony_ci		goto out;
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	error = misc_register(&monitor_device);
103362306a36Sopenharmony_ci	if (error) {
103462306a36Sopenharmony_ci		log_print("misc_register failed for monitor device");
103562306a36Sopenharmony_ci		misc_deregister(&ctl_device);
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci out:
103862306a36Sopenharmony_ci	return error;
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_civoid dlm_user_exit(void)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	misc_deregister(&ctl_device);
104462306a36Sopenharmony_ci	misc_deregister(&monitor_device);
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
1047