xref: /openbmc/linux/fs/dlm/plock.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
17336d0e6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22402211aSDavid Teigland /*
32402211aSDavid Teigland  * Copyright (C) 2005-2008 Red Hat, Inc.  All rights reserved.
42402211aSDavid Teigland  */
52402211aSDavid Teigland 
62402211aSDavid Teigland #include <linux/fs.h>
75970e15dSJeff Layton #include <linux/filelock.h>
82402211aSDavid Teigland #include <linux/miscdevice.h>
92402211aSDavid Teigland #include <linux/poll.h>
102402211aSDavid Teigland #include <linux/dlm.h>
112402211aSDavid Teigland #include <linux/dlm_plock.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
132402211aSDavid Teigland 
148c95006dSAlexander Aring #include <trace/events/dlm.h>
158c95006dSAlexander Aring 
162402211aSDavid Teigland #include "dlm_internal.h"
172402211aSDavid Teigland #include "lockspace.h"
182402211aSDavid Teigland 
19314a5540SAlexander Aring static DEFINE_SPINLOCK(ops_lock);
20314a5540SAlexander Aring static LIST_HEAD(send_list);
21314a5540SAlexander Aring static LIST_HEAD(recv_list);
22314a5540SAlexander Aring static DECLARE_WAIT_QUEUE_HEAD(send_wq);
23314a5540SAlexander Aring static DECLARE_WAIT_QUEUE_HEAD(recv_wq);
242402211aSDavid Teigland 
25bcbb4ba6SAlexander Aring struct plock_async_data {
26bcbb4ba6SAlexander Aring 	void *fl;
27bcbb4ba6SAlexander Aring 	void *file;
28bcbb4ba6SAlexander Aring 	struct file_lock flc;
29bcbb4ba6SAlexander Aring 	int (*callback)(struct file_lock *fl, int result);
30bcbb4ba6SAlexander Aring };
31bcbb4ba6SAlexander Aring 
322402211aSDavid Teigland struct plock_op {
332402211aSDavid Teigland 	struct list_head list;
342402211aSDavid Teigland 	int done;
352402211aSDavid Teigland 	struct dlm_plock_info info;
36bcbb4ba6SAlexander Aring 	/* if set indicates async handling */
37bcbb4ba6SAlexander Aring 	struct plock_async_data *data;
382402211aSDavid Teigland };
392402211aSDavid Teigland 
set_version(struct dlm_plock_info * info)402402211aSDavid Teigland static inline void set_version(struct dlm_plock_info *info)
412402211aSDavid Teigland {
422402211aSDavid Teigland 	info->version[0] = DLM_PLOCK_VERSION_MAJOR;
432402211aSDavid Teigland 	info->version[1] = DLM_PLOCK_VERSION_MINOR;
442402211aSDavid Teigland 	info->version[2] = DLM_PLOCK_VERSION_PATCH;
452402211aSDavid Teigland }
462402211aSDavid Teigland 
plock_lookup_waiter(const struct dlm_plock_info * info)47dc52cd2eSAlexander Aring static struct plock_op *plock_lookup_waiter(const struct dlm_plock_info *info)
48dc52cd2eSAlexander Aring {
49dc52cd2eSAlexander Aring 	struct plock_op *op = NULL, *iter;
50dc52cd2eSAlexander Aring 
51dc52cd2eSAlexander Aring 	list_for_each_entry(iter, &recv_list, list) {
52dc52cd2eSAlexander Aring 		if (iter->info.fsid == info->fsid &&
53dc52cd2eSAlexander Aring 		    iter->info.number == info->number &&
54dc52cd2eSAlexander Aring 		    iter->info.owner == info->owner &&
55dc52cd2eSAlexander Aring 		    iter->info.pid == info->pid &&
56dc52cd2eSAlexander Aring 		    iter->info.start == info->start &&
57dc52cd2eSAlexander Aring 		    iter->info.end == info->end &&
58dc52cd2eSAlexander Aring 		    iter->info.ex == info->ex &&
59dc52cd2eSAlexander Aring 		    iter->info.wait) {
60dc52cd2eSAlexander Aring 			op = iter;
61dc52cd2eSAlexander Aring 			break;
62dc52cd2eSAlexander Aring 		}
63dc52cd2eSAlexander Aring 	}
64dc52cd2eSAlexander Aring 
65dc52cd2eSAlexander Aring 	return op;
66dc52cd2eSAlexander Aring }
67dc52cd2eSAlexander Aring 
check_version(struct dlm_plock_info * info)682402211aSDavid Teigland static int check_version(struct dlm_plock_info *info)
692402211aSDavid Teigland {
702402211aSDavid Teigland 	if ((DLM_PLOCK_VERSION_MAJOR != info->version[0]) ||
712402211aSDavid Teigland 	    (DLM_PLOCK_VERSION_MINOR < info->version[1])) {
722402211aSDavid Teigland 		log_print("plock device version mismatch: "
732402211aSDavid Teigland 			  "kernel (%u.%u.%u), user (%u.%u.%u)",
742402211aSDavid Teigland 			  DLM_PLOCK_VERSION_MAJOR,
752402211aSDavid Teigland 			  DLM_PLOCK_VERSION_MINOR,
762402211aSDavid Teigland 			  DLM_PLOCK_VERSION_PATCH,
772402211aSDavid Teigland 			  info->version[0],
782402211aSDavid Teigland 			  info->version[1],
792402211aSDavid Teigland 			  info->version[2]);
802402211aSDavid Teigland 		return -EINVAL;
812402211aSDavid Teigland 	}
822402211aSDavid Teigland 	return 0;
832402211aSDavid Teigland }
842402211aSDavid Teigland 
dlm_release_plock_op(struct plock_op * op)85bcbb4ba6SAlexander Aring static void dlm_release_plock_op(struct plock_op *op)
86bcbb4ba6SAlexander Aring {
87bcbb4ba6SAlexander Aring 	kfree(op->data);
88bcbb4ba6SAlexander Aring 	kfree(op);
89bcbb4ba6SAlexander Aring }
90bcbb4ba6SAlexander Aring 
send_op(struct plock_op * op)912402211aSDavid Teigland static void send_op(struct plock_op *op)
922402211aSDavid Teigland {
932402211aSDavid Teigland 	set_version(&op->info);
942402211aSDavid Teigland 	spin_lock(&ops_lock);
952402211aSDavid Teigland 	list_add_tail(&op->list, &send_list);
962402211aSDavid Teigland 	spin_unlock(&ops_lock);
972402211aSDavid Teigland 	wake_up(&send_wq);
982402211aSDavid Teigland }
992402211aSDavid Teigland 
do_lock_cancel(const struct dlm_plock_info * orig_info)100568f9156SAlexander Aring static int do_lock_cancel(const struct dlm_plock_info *orig_info)
101901025d2SDavid Teigland {
102901025d2SDavid Teigland 	struct plock_op *op;
103568f9156SAlexander Aring 	int rv;
104901025d2SDavid Teigland 
105901025d2SDavid Teigland 	op = kzalloc(sizeof(*op), GFP_NOFS);
106901025d2SDavid Teigland 	if (!op)
107568f9156SAlexander Aring 		return -ENOMEM;
108901025d2SDavid Teigland 
109568f9156SAlexander Aring 	op->info = *orig_info;
110568f9156SAlexander Aring 	op->info.optype = DLM_PLOCK_OP_CANCEL;
111568f9156SAlexander Aring 	op->info.wait = 0;
112901025d2SDavid Teigland 
113901025d2SDavid Teigland 	send_op(op);
114568f9156SAlexander Aring 	wait_event(recv_wq, (op->done != 0));
115568f9156SAlexander Aring 
116568f9156SAlexander Aring 	rv = op->info.rv;
117568f9156SAlexander Aring 
118568f9156SAlexander Aring 	dlm_release_plock_op(op);
119568f9156SAlexander Aring 	return rv;
120901025d2SDavid Teigland }
121901025d2SDavid Teigland 
dlm_posix_lock(dlm_lockspace_t * lockspace,u64 number,struct file * file,int cmd,struct file_lock * fl)1222402211aSDavid Teigland int dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
1232402211aSDavid Teigland 		   int cmd, struct file_lock *fl)
1242402211aSDavid Teigland {
125bcbb4ba6SAlexander Aring 	struct plock_async_data *op_data;
1262402211aSDavid Teigland 	struct dlm_ls *ls;
1272402211aSDavid Teigland 	struct plock_op *op;
1282402211aSDavid Teigland 	int rv;
1292402211aSDavid Teigland 
1302402211aSDavid Teigland 	ls = dlm_find_lockspace_local(lockspace);
1312402211aSDavid Teigland 	if (!ls)
1322402211aSDavid Teigland 		return -EINVAL;
1332402211aSDavid Teigland 
134bcbb4ba6SAlexander Aring 	op = kzalloc(sizeof(*op), GFP_NOFS);
135bcbb4ba6SAlexander Aring 	if (!op) {
1362402211aSDavid Teigland 		rv = -ENOMEM;
1372402211aSDavid Teigland 		goto out;
1382402211aSDavid Teigland 	}
1392402211aSDavid Teigland 
1402402211aSDavid Teigland 	op->info.optype		= DLM_PLOCK_OP_LOCK;
1412402211aSDavid Teigland 	op->info.pid		= fl->fl_pid;
1422402211aSDavid Teigland 	op->info.ex		= (fl->fl_type == F_WRLCK);
1432402211aSDavid Teigland 	op->info.wait		= IS_SETLKW(cmd);
1442402211aSDavid Teigland 	op->info.fsid		= ls->ls_global_id;
1452402211aSDavid Teigland 	op->info.number		= number;
1462402211aSDavid Teigland 	op->info.start		= fl->fl_start;
1472402211aSDavid Teigland 	op->info.end		= fl->fl_end;
148bcbb4ba6SAlexander Aring 	/* async handling */
1498fb47a4fSJ. Bruce Fields 	if (fl->fl_lmops && fl->fl_lmops->lm_grant) {
150bcbb4ba6SAlexander Aring 		op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
151bcbb4ba6SAlexander Aring 		if (!op_data) {
152bcbb4ba6SAlexander Aring 			dlm_release_plock_op(op);
153bcbb4ba6SAlexander Aring 			rv = -ENOMEM;
154bcbb4ba6SAlexander Aring 			goto out;
155bcbb4ba6SAlexander Aring 		}
156bcbb4ba6SAlexander Aring 
1572402211aSDavid Teigland 		/* fl_owner is lockd which doesn't distinguish
1582402211aSDavid Teigland 		   processes on the nfs client */
1592402211aSDavid Teigland 		op->info.owner	= (__u64) fl->fl_pid;
160bcbb4ba6SAlexander Aring 		op_data->callback = fl->fl_lmops->lm_grant;
161bcbb4ba6SAlexander Aring 		locks_init_lock(&op_data->flc);
162bcbb4ba6SAlexander Aring 		locks_copy_lock(&op_data->flc, fl);
163bcbb4ba6SAlexander Aring 		op_data->fl		= fl;
164bcbb4ba6SAlexander Aring 		op_data->file	= file;
165bcbb4ba6SAlexander Aring 
166bcbb4ba6SAlexander Aring 		op->data = op_data;
167a800ba77SAlexander Aring 
168a800ba77SAlexander Aring 		send_op(op);
169a800ba77SAlexander Aring 		rv = FILE_LOCK_DEFERRED;
170a800ba77SAlexander Aring 		goto out;
1712402211aSDavid Teigland 	} else {
1722402211aSDavid Teigland 		op->info.owner	= (__u64)(long) fl->fl_owner;
1732402211aSDavid Teigland 	}
1742402211aSDavid Teigland 
1752402211aSDavid Teigland 	send_op(op);
1762402211aSDavid Teigland 
1770f2b1cb8SAlexander Aring 	if (op->info.wait) {
178568f9156SAlexander Aring 		rv = wait_event_interruptible(recv_wq, (op->done != 0));
179901025d2SDavid Teigland 		if (rv == -ERESTARTSYS) {
180901025d2SDavid Teigland 			spin_lock(&ops_lock);
181b92a4e3fSAlexander Aring 			/* recheck under ops_lock if we got a done != 0,
182b92a4e3fSAlexander Aring 			 * if so this interrupt case should be ignored
183b92a4e3fSAlexander Aring 			 */
184b92a4e3fSAlexander Aring 			if (op->done != 0) {
185b92a4e3fSAlexander Aring 				spin_unlock(&ops_lock);
186b92a4e3fSAlexander Aring 				goto do_lock_wait;
187b92a4e3fSAlexander Aring 			}
188568f9156SAlexander Aring 			spin_unlock(&ops_lock);
189568f9156SAlexander Aring 
190568f9156SAlexander Aring 			rv = do_lock_cancel(&op->info);
191568f9156SAlexander Aring 			switch (rv) {
192568f9156SAlexander Aring 			case 0:
193568f9156SAlexander Aring 				/* waiter was deleted in user space, answer will never come
194568f9156SAlexander Aring 				 * remove original request. The original request must be
195568f9156SAlexander Aring 				 * on recv_list because the answer of do_lock_cancel()
196568f9156SAlexander Aring 				 * synchronized it.
197568f9156SAlexander Aring 				 */
198568f9156SAlexander Aring 				spin_lock(&ops_lock);
199c847f4e2SAlexander Aring 				list_del(&op->list);
200901025d2SDavid Teigland 				spin_unlock(&ops_lock);
201568f9156SAlexander Aring 				rv = -EINTR;
202568f9156SAlexander Aring 				break;
203568f9156SAlexander Aring 			case -ENOENT:
204568f9156SAlexander Aring 				/* cancellation wasn't successful but op should be done */
205568f9156SAlexander Aring 				fallthrough;
206568f9156SAlexander Aring 			default:
207568f9156SAlexander Aring 				/* internal error doing cancel we need to wait */
208568f9156SAlexander Aring 				goto wait;
209568f9156SAlexander Aring 			}
210c847f4e2SAlexander Aring 
211ea06d4caSAlexander Aring 			log_debug(ls, "%s: wait interrupted %x %llx pid %d",
212bcfad426SAlexander Aring 				  __func__, ls->ls_global_id,
21319d7ca05SAlexander Aring 				  (unsigned long long)number, op->info.pid);
214c847f4e2SAlexander Aring 			dlm_release_plock_op(op);
215901025d2SDavid Teigland 			goto out;
216901025d2SDavid Teigland 		}
2170f2b1cb8SAlexander Aring 	} else {
218568f9156SAlexander Aring wait:
2190f2b1cb8SAlexander Aring 		wait_event(recv_wq, (op->done != 0));
2200f2b1cb8SAlexander Aring 	}
2212402211aSDavid Teigland 
222b92a4e3fSAlexander Aring do_lock_wait:
223b92a4e3fSAlexander Aring 
224a559790cSAlexander Aring 	WARN_ON(!list_empty(&op->list));
2252402211aSDavid Teigland 
2262402211aSDavid Teigland 	rv = op->info.rv;
2272402211aSDavid Teigland 
2282402211aSDavid Teigland 	if (!rv) {
2294f656367SBenjamin Coddington 		if (locks_lock_file_wait(file, fl) < 0)
2302402211aSDavid Teigland 			log_error(ls, "dlm_posix_lock: vfs lock error %llx",
2312402211aSDavid Teigland 				  (unsigned long long)number);
2322402211aSDavid Teigland 	}
2332402211aSDavid Teigland 
234bcbb4ba6SAlexander Aring 	dlm_release_plock_op(op);
2352402211aSDavid Teigland out:
2362402211aSDavid Teigland 	dlm_put_lockspace(ls);
2372402211aSDavid Teigland 	return rv;
2382402211aSDavid Teigland }
2392402211aSDavid Teigland EXPORT_SYMBOL_GPL(dlm_posix_lock);
2402402211aSDavid Teigland 
241af901ca1SAndré Goddard Rosa /* Returns failure iff a successful lock operation should be canceled */
dlm_plock_callback(struct plock_op * op)2422402211aSDavid Teigland static int dlm_plock_callback(struct plock_op *op)
2432402211aSDavid Teigland {
244bcbb4ba6SAlexander Aring 	struct plock_async_data *op_data = op->data;
2452402211aSDavid Teigland 	struct file *file;
2462402211aSDavid Teigland 	struct file_lock *fl;
2472402211aSDavid Teigland 	struct file_lock *flc;
248d0449b90SJoe Perches 	int (*notify)(struct file_lock *fl, int result) = NULL;
2492402211aSDavid Teigland 	int rv = 0;
2502402211aSDavid Teigland 
251a559790cSAlexander Aring 	WARN_ON(!list_empty(&op->list));
2522402211aSDavid Teigland 
2532402211aSDavid Teigland 	/* check if the following 2 are still valid or make a copy */
254bcbb4ba6SAlexander Aring 	file = op_data->file;
255bcbb4ba6SAlexander Aring 	flc = &op_data->flc;
256bcbb4ba6SAlexander Aring 	fl = op_data->fl;
257bcbb4ba6SAlexander Aring 	notify = op_data->callback;
2582402211aSDavid Teigland 
2592402211aSDavid Teigland 	if (op->info.rv) {
260d0449b90SJoe Perches 		notify(fl, op->info.rv);
2612402211aSDavid Teigland 		goto out;
2622402211aSDavid Teigland 	}
2632402211aSDavid Teigland 
2642402211aSDavid Teigland 	/* got fs lock; bookkeep locally as well: */
2652402211aSDavid Teigland 	flc->fl_flags &= ~FL_SLEEP;
2662402211aSDavid Teigland 	if (posix_lock_file(file, flc, NULL)) {
2672402211aSDavid Teigland 		/*
2682402211aSDavid Teigland 		 * This can only happen in the case of kmalloc() failure.
2692402211aSDavid Teigland 		 * The filesystem's own lock is the authoritative lock,
2702402211aSDavid Teigland 		 * so a failure to get the lock locally is not a disaster.
2712402211aSDavid Teigland 		 * As long as the fs cannot reliably cancel locks (especially
2722402211aSDavid Teigland 		 * in a low-memory situation), we're better off ignoring
2732402211aSDavid Teigland 		 * this failure than trying to recover.
2742402211aSDavid Teigland 		 */
2752402211aSDavid Teigland 		log_print("dlm_plock_callback: vfs lock error %llx file %p fl %p",
2762402211aSDavid Teigland 			  (unsigned long long)op->info.number, file, fl);
2772402211aSDavid Teigland 	}
2782402211aSDavid Teigland 
279d0449b90SJoe Perches 	rv = notify(fl, 0);
2802402211aSDavid Teigland 	if (rv) {
2812402211aSDavid Teigland 		/* XXX: We need to cancel the fs lock here: */
28299c58d64SAlexander Aring 		log_print("%s: lock granted after lock request failed; dangling lock!",
28399c58d64SAlexander Aring 			  __func__);
2842402211aSDavid Teigland 		goto out;
2852402211aSDavid Teigland 	}
2862402211aSDavid Teigland 
2872402211aSDavid Teigland out:
288bcbb4ba6SAlexander Aring 	dlm_release_plock_op(op);
2892402211aSDavid Teigland 	return rv;
2902402211aSDavid Teigland }
2912402211aSDavid Teigland 
dlm_posix_unlock(dlm_lockspace_t * lockspace,u64 number,struct file * file,struct file_lock * fl)2922402211aSDavid Teigland int dlm_posix_unlock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
2932402211aSDavid Teigland 		     struct file_lock *fl)
2942402211aSDavid Teigland {
2952402211aSDavid Teigland 	struct dlm_ls *ls;
2962402211aSDavid Teigland 	struct plock_op *op;
2972402211aSDavid Teigland 	int rv;
29890008318SDavid Teigland 	unsigned char fl_flags = fl->fl_flags;
2992402211aSDavid Teigland 
3002402211aSDavid Teigland 	ls = dlm_find_lockspace_local(lockspace);
3012402211aSDavid Teigland 	if (!ls)
3022402211aSDavid Teigland 		return -EINVAL;
3032402211aSDavid Teigland 
304573c24c4SDavid Teigland 	op = kzalloc(sizeof(*op), GFP_NOFS);
3052402211aSDavid Teigland 	if (!op) {
3062402211aSDavid Teigland 		rv = -ENOMEM;
3072402211aSDavid Teigland 		goto out;
3082402211aSDavid Teigland 	}
3092402211aSDavid Teigland 
31090008318SDavid Teigland 	/* cause the vfs unlock to return ENOENT if lock is not found */
31190008318SDavid Teigland 	fl->fl_flags |= FL_EXISTS;
31290008318SDavid Teigland 
3134f656367SBenjamin Coddington 	rv = locks_lock_file_wait(file, fl);
31490008318SDavid Teigland 	if (rv == -ENOENT) {
31590008318SDavid Teigland 		rv = 0;
31690008318SDavid Teigland 		goto out_free;
31790008318SDavid Teigland 	}
31890008318SDavid Teigland 	if (rv < 0) {
31990008318SDavid Teigland 		log_error(ls, "dlm_posix_unlock: vfs unlock error %d %llx",
32090008318SDavid Teigland 			  rv, (unsigned long long)number);
32190008318SDavid Teigland 	}
3222402211aSDavid Teigland 
3232402211aSDavid Teigland 	op->info.optype		= DLM_PLOCK_OP_UNLOCK;
3242402211aSDavid Teigland 	op->info.pid		= fl->fl_pid;
3252402211aSDavid Teigland 	op->info.fsid		= ls->ls_global_id;
3262402211aSDavid Teigland 	op->info.number		= number;
3272402211aSDavid Teigland 	op->info.start		= fl->fl_start;
3282402211aSDavid Teigland 	op->info.end		= fl->fl_end;
3298fb47a4fSJ. Bruce Fields 	if (fl->fl_lmops && fl->fl_lmops->lm_grant)
3302402211aSDavid Teigland 		op->info.owner	= (__u64) fl->fl_pid;
3312402211aSDavid Teigland 	else
3322402211aSDavid Teigland 		op->info.owner	= (__u64)(long) fl->fl_owner;
3332402211aSDavid Teigland 
334901025d2SDavid Teigland 	if (fl->fl_flags & FL_CLOSE) {
335901025d2SDavid Teigland 		op->info.flags |= DLM_PLOCK_FL_CLOSE;
336901025d2SDavid Teigland 		send_op(op);
337901025d2SDavid Teigland 		rv = 0;
338901025d2SDavid Teigland 		goto out;
339901025d2SDavid Teigland 	}
340901025d2SDavid Teigland 
3412402211aSDavid Teigland 	send_op(op);
3422402211aSDavid Teigland 	wait_event(recv_wq, (op->done != 0));
3432402211aSDavid Teigland 
344a559790cSAlexander Aring 	WARN_ON(!list_empty(&op->list));
3452402211aSDavid Teigland 
3462402211aSDavid Teigland 	rv = op->info.rv;
3472402211aSDavid Teigland 
3482402211aSDavid Teigland 	if (rv == -ENOENT)
3492402211aSDavid Teigland 		rv = 0;
3502402211aSDavid Teigland 
35190008318SDavid Teigland out_free:
352bcbb4ba6SAlexander Aring 	dlm_release_plock_op(op);
3532402211aSDavid Teigland out:
3542402211aSDavid Teigland 	dlm_put_lockspace(ls);
35590008318SDavid Teigland 	fl->fl_flags = fl_flags;
3562402211aSDavid Teigland 	return rv;
3572402211aSDavid Teigland }
3582402211aSDavid Teigland EXPORT_SYMBOL_GPL(dlm_posix_unlock);
3592402211aSDavid Teigland 
360dc52cd2eSAlexander Aring /*
361dc52cd2eSAlexander Aring  * NOTE: This implementation can only handle async lock requests as nfs
362dc52cd2eSAlexander Aring  * do it. It cannot handle cancellation of a pending lock request sitting
363dc52cd2eSAlexander Aring  * in wait_event(), but for now only nfs is the only user local kernel
364dc52cd2eSAlexander Aring  * user.
365dc52cd2eSAlexander Aring  */
dlm_posix_cancel(dlm_lockspace_t * lockspace,u64 number,struct file * file,struct file_lock * fl)366dc52cd2eSAlexander Aring int dlm_posix_cancel(dlm_lockspace_t *lockspace, u64 number, struct file *file,
367dc52cd2eSAlexander Aring 		     struct file_lock *fl)
368dc52cd2eSAlexander Aring {
369dc52cd2eSAlexander Aring 	struct dlm_plock_info info;
370dc52cd2eSAlexander Aring 	struct plock_op *op;
371dc52cd2eSAlexander Aring 	struct dlm_ls *ls;
372dc52cd2eSAlexander Aring 	int rv;
373dc52cd2eSAlexander Aring 
374dc52cd2eSAlexander Aring 	/* this only works for async request for now and nfs is the only
375dc52cd2eSAlexander Aring 	 * kernel user right now.
376dc52cd2eSAlexander Aring 	 */
377dc52cd2eSAlexander Aring 	if (WARN_ON_ONCE(!fl->fl_lmops || !fl->fl_lmops->lm_grant))
378dc52cd2eSAlexander Aring 		return -EOPNOTSUPP;
379dc52cd2eSAlexander Aring 
380dc52cd2eSAlexander Aring 	ls = dlm_find_lockspace_local(lockspace);
381dc52cd2eSAlexander Aring 	if (!ls)
382dc52cd2eSAlexander Aring 		return -EINVAL;
383dc52cd2eSAlexander Aring 
384dc52cd2eSAlexander Aring 	memset(&info, 0, sizeof(info));
385dc52cd2eSAlexander Aring 	info.pid = fl->fl_pid;
386dc52cd2eSAlexander Aring 	info.ex = (fl->fl_type == F_WRLCK);
387dc52cd2eSAlexander Aring 	info.fsid = ls->ls_global_id;
388dc52cd2eSAlexander Aring 	dlm_put_lockspace(ls);
389dc52cd2eSAlexander Aring 	info.number = number;
390dc52cd2eSAlexander Aring 	info.start = fl->fl_start;
391dc52cd2eSAlexander Aring 	info.end = fl->fl_end;
392dc52cd2eSAlexander Aring 	info.owner = (__u64)fl->fl_pid;
393dc52cd2eSAlexander Aring 
394dc52cd2eSAlexander Aring 	rv = do_lock_cancel(&info);
395dc52cd2eSAlexander Aring 	switch (rv) {
396dc52cd2eSAlexander Aring 	case 0:
397dc52cd2eSAlexander Aring 		spin_lock(&ops_lock);
398dc52cd2eSAlexander Aring 		/* lock request to cancel must be on recv_list because
399dc52cd2eSAlexander Aring 		 * do_lock_cancel() synchronizes it.
400dc52cd2eSAlexander Aring 		 */
401dc52cd2eSAlexander Aring 		op = plock_lookup_waiter(&info);
402dc52cd2eSAlexander Aring 		if (WARN_ON_ONCE(!op)) {
403e717f2e8SAlexander Aring 			spin_unlock(&ops_lock);
404dc52cd2eSAlexander Aring 			rv = -ENOLCK;
405dc52cd2eSAlexander Aring 			break;
406dc52cd2eSAlexander Aring 		}
407dc52cd2eSAlexander Aring 
408dc52cd2eSAlexander Aring 		list_del(&op->list);
409dc52cd2eSAlexander Aring 		spin_unlock(&ops_lock);
410dc52cd2eSAlexander Aring 		WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK);
411dc52cd2eSAlexander Aring 		op->data->callback(op->data->fl, -EINTR);
412dc52cd2eSAlexander Aring 		dlm_release_plock_op(op);
413dc52cd2eSAlexander Aring 		rv = -EINTR;
414dc52cd2eSAlexander Aring 		break;
415dc52cd2eSAlexander Aring 	case -ENOENT:
416dc52cd2eSAlexander Aring 		/* if cancel wasn't successful we probably were to late
417dc52cd2eSAlexander Aring 		 * or it was a non-blocking lock request, so just unlock it.
418dc52cd2eSAlexander Aring 		 */
419dc52cd2eSAlexander Aring 		rv = dlm_posix_unlock(lockspace, number, file, fl);
420dc52cd2eSAlexander Aring 		break;
421dc52cd2eSAlexander Aring 	default:
422dc52cd2eSAlexander Aring 		break;
423dc52cd2eSAlexander Aring 	}
424dc52cd2eSAlexander Aring 
425dc52cd2eSAlexander Aring 	return rv;
426dc52cd2eSAlexander Aring }
427dc52cd2eSAlexander Aring EXPORT_SYMBOL_GPL(dlm_posix_cancel);
428dc52cd2eSAlexander Aring 
dlm_posix_get(dlm_lockspace_t * lockspace,u64 number,struct file * file,struct file_lock * fl)4292402211aSDavid Teigland int dlm_posix_get(dlm_lockspace_t *lockspace, u64 number, struct file *file,
4302402211aSDavid Teigland 		  struct file_lock *fl)
4312402211aSDavid Teigland {
4322402211aSDavid Teigland 	struct dlm_ls *ls;
4332402211aSDavid Teigland 	struct plock_op *op;
4342402211aSDavid Teigland 	int rv;
4352402211aSDavid Teigland 
4362402211aSDavid Teigland 	ls = dlm_find_lockspace_local(lockspace);
4372402211aSDavid Teigland 	if (!ls)
4382402211aSDavid Teigland 		return -EINVAL;
4392402211aSDavid Teigland 
440573c24c4SDavid Teigland 	op = kzalloc(sizeof(*op), GFP_NOFS);
4412402211aSDavid Teigland 	if (!op) {
4422402211aSDavid Teigland 		rv = -ENOMEM;
4432402211aSDavid Teigland 		goto out;
4442402211aSDavid Teigland 	}
4452402211aSDavid Teigland 
4462402211aSDavid Teigland 	op->info.optype		= DLM_PLOCK_OP_GET;
4472402211aSDavid Teigland 	op->info.pid		= fl->fl_pid;
4482402211aSDavid Teigland 	op->info.ex		= (fl->fl_type == F_WRLCK);
4492402211aSDavid Teigland 	op->info.fsid		= ls->ls_global_id;
4502402211aSDavid Teigland 	op->info.number		= number;
4512402211aSDavid Teigland 	op->info.start		= fl->fl_start;
4522402211aSDavid Teigland 	op->info.end		= fl->fl_end;
4538fb47a4fSJ. Bruce Fields 	if (fl->fl_lmops && fl->fl_lmops->lm_grant)
4542402211aSDavid Teigland 		op->info.owner	= (__u64) fl->fl_pid;
4552402211aSDavid Teigland 	else
4562402211aSDavid Teigland 		op->info.owner	= (__u64)(long) fl->fl_owner;
4572402211aSDavid Teigland 
4582402211aSDavid Teigland 	send_op(op);
4592402211aSDavid Teigland 	wait_event(recv_wq, (op->done != 0));
4602402211aSDavid Teigland 
461a559790cSAlexander Aring 	WARN_ON(!list_empty(&op->list));
4622402211aSDavid Teigland 
4632402211aSDavid Teigland 	/* info.rv from userspace is 1 for conflict, 0 for no-conflict,
4642402211aSDavid Teigland 	   -ENOENT if there are no locks on the file */
4652402211aSDavid Teigland 
4662402211aSDavid Teigland 	rv = op->info.rv;
4672402211aSDavid Teigland 
4682402211aSDavid Teigland 	fl->fl_type = F_UNLCK;
4692402211aSDavid Teigland 	if (rv == -ENOENT)
4702402211aSDavid Teigland 		rv = 0;
4712402211aSDavid Teigland 	else if (rv > 0) {
47220d5a399SJeff Layton 		locks_init_lock(fl);
4732402211aSDavid Teigland 		fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK;
47420d5a399SJeff Layton 		fl->fl_flags = FL_POSIX;
47592655fbdSAlexander Aring 		fl->fl_pid = op->info.pid;
47692655fbdSAlexander Aring 		if (op->info.nodeid != dlm_our_nodeid())
47792655fbdSAlexander Aring 			fl->fl_pid = -fl->fl_pid;
4782402211aSDavid Teigland 		fl->fl_start = op->info.start;
4792402211aSDavid Teigland 		fl->fl_end = op->info.end;
4802402211aSDavid Teigland 		rv = 0;
4812402211aSDavid Teigland 	}
4822402211aSDavid Teigland 
483bcbb4ba6SAlexander Aring 	dlm_release_plock_op(op);
4842402211aSDavid Teigland out:
4852402211aSDavid Teigland 	dlm_put_lockspace(ls);
4862402211aSDavid Teigland 	return rv;
4872402211aSDavid Teigland }
4882402211aSDavid Teigland EXPORT_SYMBOL_GPL(dlm_posix_get);
4892402211aSDavid Teigland 
4902402211aSDavid Teigland /* a read copies out one plock request from the send list */
dev_read(struct file * file,char __user * u,size_t count,loff_t * ppos)4912402211aSDavid Teigland static ssize_t dev_read(struct file *file, char __user *u, size_t count,
4922402211aSDavid Teigland 			loff_t *ppos)
4932402211aSDavid Teigland {
4942402211aSDavid Teigland 	struct dlm_plock_info info;
4952402211aSDavid Teigland 	struct plock_op *op = NULL;
4962402211aSDavid Teigland 
4972402211aSDavid Teigland 	if (count < sizeof(info))
4982402211aSDavid Teigland 		return -EINVAL;
4992402211aSDavid Teigland 
5002402211aSDavid Teigland 	spin_lock(&ops_lock);
5012402211aSDavid Teigland 	if (!list_empty(&send_list)) {
502976a0624SAlexander Aring 		op = list_first_entry(&send_list, struct plock_op, list);
503901025d2SDavid Teigland 		if (op->info.flags & DLM_PLOCK_FL_CLOSE)
504901025d2SDavid Teigland 			list_del(&op->list);
505901025d2SDavid Teigland 		else
50657e2c2f2SAlexander Aring 			list_move_tail(&op->list, &recv_list);
5072402211aSDavid Teigland 		memcpy(&info, &op->info, sizeof(info));
5082402211aSDavid Teigland 	}
5092402211aSDavid Teigland 	spin_unlock(&ops_lock);
5102402211aSDavid Teigland 
5112402211aSDavid Teigland 	if (!op)
5122402211aSDavid Teigland 		return -EAGAIN;
5132402211aSDavid Teigland 
5148c95006dSAlexander Aring 	trace_dlm_plock_read(&info);
5158c95006dSAlexander Aring 
516901025d2SDavid Teigland 	/* there is no need to get a reply from userspace for unlocks
517901025d2SDavid Teigland 	   that were generated by the vfs cleaning up for a close
518901025d2SDavid Teigland 	   (the process did not make an unlock call). */
519901025d2SDavid Teigland 
520901025d2SDavid Teigland 	if (op->info.flags & DLM_PLOCK_FL_CLOSE)
521bcbb4ba6SAlexander Aring 		dlm_release_plock_op(op);
522901025d2SDavid Teigland 
5232402211aSDavid Teigland 	if (copy_to_user(u, &info, sizeof(info)))
5242402211aSDavid Teigland 		return -EFAULT;
5252402211aSDavid Teigland 	return sizeof(info);
5262402211aSDavid Teigland }
5272402211aSDavid Teigland 
5282402211aSDavid Teigland /* a write copies in one plock result that should match a plock_op
5292402211aSDavid Teigland    on the recv list */
dev_write(struct file * file,const char __user * u,size_t count,loff_t * ppos)5302402211aSDavid Teigland static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
5312402211aSDavid Teigland 			 loff_t *ppos)
5322402211aSDavid Teigland {
533dc1acd5cSJakob Koschel 	struct plock_op *op = NULL, *iter;
5342402211aSDavid Teigland 	struct dlm_plock_info info;
535dc1acd5cSJakob Koschel 	int do_callback = 0;
5362402211aSDavid Teigland 
5372402211aSDavid Teigland 	if (count != sizeof(info))
5382402211aSDavid Teigland 		return -EINVAL;
5392402211aSDavid Teigland 
5402402211aSDavid Teigland 	if (copy_from_user(&info, u, sizeof(info)))
5412402211aSDavid Teigland 		return -EFAULT;
5422402211aSDavid Teigland 
5438c95006dSAlexander Aring 	trace_dlm_plock_write(&info);
5448c95006dSAlexander Aring 
5452402211aSDavid Teigland 	if (check_version(&info))
5462402211aSDavid Teigland 		return -EINVAL;
5472402211aSDavid Teigland 
54857e2c2f2SAlexander Aring 	/*
54957e2c2f2SAlexander Aring 	 * The results for waiting ops (SETLKW) can be returned in any
55057e2c2f2SAlexander Aring 	 * order, so match all fields to find the op.  The results for
55157e2c2f2SAlexander Aring 	 * non-waiting ops are returned in the order that they were sent
55257e2c2f2SAlexander Aring 	 * to userspace, so match the result with the first non-waiting op.
55357e2c2f2SAlexander Aring 	 */
5542402211aSDavid Teigland 	spin_lock(&ops_lock);
55557e2c2f2SAlexander Aring 	if (info.wait) {
556dc52cd2eSAlexander Aring 		op = plock_lookup_waiter(&info);
55757e2c2f2SAlexander Aring 	} else {
55857e2c2f2SAlexander Aring 		list_for_each_entry(iter, &recv_list, list) {
559*7c53e847SAlexander Aring 			if (!iter->info.wait &&
560*7c53e847SAlexander Aring 			    iter->info.fsid == info.fsid) {
56157e2c2f2SAlexander Aring 				op = iter;
56257e2c2f2SAlexander Aring 				break;
56357e2c2f2SAlexander Aring 			}
56457e2c2f2SAlexander Aring 		}
56557e2c2f2SAlexander Aring 	}
56657e2c2f2SAlexander Aring 
56757e2c2f2SAlexander Aring 	if (op) {
56857e2c2f2SAlexander Aring 		/* Sanity check that op and info match. */
56957e2c2f2SAlexander Aring 		if (info.wait)
57057e2c2f2SAlexander Aring 			WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK);
57157e2c2f2SAlexander Aring 		else
572*7c53e847SAlexander Aring 			WARN_ON(op->info.number != info.number ||
57357e2c2f2SAlexander Aring 				op->info.owner != info.owner ||
57457e2c2f2SAlexander Aring 				op->info.optype != info.optype);
57557e2c2f2SAlexander Aring 
57657e2c2f2SAlexander Aring 		list_del_init(&op->list);
57757e2c2f2SAlexander Aring 		memcpy(&op->info, &info, sizeof(info));
57857e2c2f2SAlexander Aring 		if (op->data)
57957e2c2f2SAlexander Aring 			do_callback = 1;
58057e2c2f2SAlexander Aring 		else
58157e2c2f2SAlexander Aring 			op->done = 1;
58257e2c2f2SAlexander Aring 	}
5832402211aSDavid Teigland 	spin_unlock(&ops_lock);
5842402211aSDavid Teigland 
585dc1acd5cSJakob Koschel 	if (op) {
586c78a87d0SDavid Teigland 		if (do_callback)
587817d10baSDavid Teigland 			dlm_plock_callback(op);
5882402211aSDavid Teigland 		else
5892402211aSDavid Teigland 			wake_up(&recv_wq);
5902402211aSDavid Teigland 	} else
591c847f4e2SAlexander Aring 		pr_debug("%s: no op %x %llx", __func__,
592bcfad426SAlexander Aring 			 info.fsid, (unsigned long long)info.number);
5932402211aSDavid Teigland 	return count;
5942402211aSDavid Teigland }
5952402211aSDavid Teigland 
dev_poll(struct file * file,poll_table * wait)596076ccb76SAl Viro static __poll_t dev_poll(struct file *file, poll_table *wait)
5972402211aSDavid Teigland {
598076ccb76SAl Viro 	__poll_t mask = 0;
5992402211aSDavid Teigland 
6002402211aSDavid Teigland 	poll_wait(file, &send_wq, wait);
6012402211aSDavid Teigland 
6022402211aSDavid Teigland 	spin_lock(&ops_lock);
6032402211aSDavid Teigland 	if (!list_empty(&send_list))
604a9a08845SLinus Torvalds 		mask = EPOLLIN | EPOLLRDNORM;
6052402211aSDavid Teigland 	spin_unlock(&ops_lock);
6062402211aSDavid Teigland 
6072402211aSDavid Teigland 	return mask;
6082402211aSDavid Teigland }
6092402211aSDavid Teigland 
6102402211aSDavid Teigland static const struct file_operations dev_fops = {
6112402211aSDavid Teigland 	.read    = dev_read,
6122402211aSDavid Teigland 	.write   = dev_write,
6132402211aSDavid Teigland 	.poll    = dev_poll,
6146038f373SArnd Bergmann 	.owner   = THIS_MODULE,
6156038f373SArnd Bergmann 	.llseek  = noop_llseek,
6162402211aSDavid Teigland };
6172402211aSDavid Teigland 
6182402211aSDavid Teigland static struct miscdevice plock_dev_misc = {
6192402211aSDavid Teigland 	.minor = MISC_DYNAMIC_MINOR,
6202402211aSDavid Teigland 	.name = DLM_PLOCK_MISC_NAME,
6212402211aSDavid Teigland 	.fops = &dev_fops
6222402211aSDavid Teigland };
6232402211aSDavid Teigland 
dlm_plock_init(void)6242402211aSDavid Teigland int dlm_plock_init(void)
6252402211aSDavid Teigland {
6262402211aSDavid Teigland 	int rv;
6272402211aSDavid Teigland 
6282402211aSDavid Teigland 	rv = misc_register(&plock_dev_misc);
6292402211aSDavid Teigland 	if (rv)
6302402211aSDavid Teigland 		log_print("dlm_plock_init: misc_register failed %d", rv);
6312402211aSDavid Teigland 	return rv;
6322402211aSDavid Teigland }
6332402211aSDavid Teigland 
dlm_plock_exit(void)6342402211aSDavid Teigland void dlm_plock_exit(void)
6352402211aSDavid Teigland {
636f368ed60SGreg Kroah-Hartman 	misc_deregister(&plock_dev_misc);
63767b5da9aSAlexander Aring 	WARN_ON(!list_empty(&send_list));
63867b5da9aSAlexander Aring 	WARN_ON(!list_empty(&recv_list));
6392402211aSDavid Teigland }
6402402211aSDavid Teigland 
641