projects/linux·blob master · refs

642 lines · 14.3 KiB

1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2005-2008 Red Hat, Inc. All rights reserved.
4 */
5
6#include <linux/fs.h>
7#include <linux/capability.h>
8#include <linux/filelock.h>
9#include <linux/miscdevice.h>
10#include <linux/poll.h>
11#include <linux/dlm.h>
12#include <linux/dlm_plock.h>
13#include <linux/slab.h>
14
15#include <trace/events/dlm.h>
16
17#include "dlm_internal.h"
18#include "lockspace.h"
19
20static DEFINE_SPINLOCK(ops_lock);
21static LIST_HEAD(send_list);
22static LIST_HEAD(recv_list);
23static DECLARE_WAIT_QUEUE_HEAD(send_wq);
24static DECLARE_WAIT_QUEUE_HEAD(recv_wq);
25
26struct plock_async_data {
27 void *fl;
28 void *file;
29 struct file_lock flc;
30 int (*callback)(struct file_lock *fl, int result);
31};
32
33struct plock_op {
34 struct list_head list;
35 int done;
36 struct dlm_plock_info info;
37 /* if set indicates async handling */
38 struct plock_async_data *data;
39};
40
41static inline void set_version(struct dlm_plock_info *info)
42{
43 info->version[0] = DLM_PLOCK_VERSION_MAJOR;
44 info->version[1] = DLM_PLOCK_VERSION_MINOR;
45 info->version[2] = DLM_PLOCK_VERSION_PATCH;
46}
47
48static struct plock_op *plock_lookup_waiter(const struct dlm_plock_info *info)
49{
50 struct plock_op *op = NULL, *iter;
51
52 list_for_each_entry(iter, &recv_list, list) {
53 if (iter->info.fsid == info->fsid &&
54 iter->info.number == info->number &&
55 iter->info.owner == info->owner &&
56 iter->info.pid == info->pid &&
57 iter->info.start == info->start &&
58 iter->info.end == info->end &&
59 iter->info.ex == info->ex &&
60 iter->info.wait) {
61 op = iter;
62 break;
63 }
64 }
65
66 return op;
67}
68
69static int check_version(struct dlm_plock_info *info)
70{
71 if ((DLM_PLOCK_VERSION_MAJOR != info->version[0]) ||
72 (DLM_PLOCK_VERSION_MINOR < info->version[1])) {
73 log_print("plock device version mismatch: "
74 "kernel (%u.%u.%u), user (%u.%u.%u)",
75 DLM_PLOCK_VERSION_MAJOR,
76 DLM_PLOCK_VERSION_MINOR,
77 DLM_PLOCK_VERSION_PATCH,
78 info->version[0],
79 info->version[1],
80 info->version[2]);
81 return -EINVAL;
82 }
83 return 0;
84}
85
86static void dlm_release_plock_op(struct plock_op *op)
87{
88 kfree(op->data);
89 kfree(op);
90}
91
92static void send_op(struct plock_op *op)
93{
94 set_version(&op->info);
95 spin_lock(&ops_lock);
96 list_add_tail(&op->list, &send_list);
97 spin_unlock(&ops_lock);
98 wake_up(&send_wq);
99}
100
101static int do_lock_cancel(const struct dlm_plock_info *orig_info)
102{
103 struct plock_op *op;
104 int rv;
105
106 op = kzalloc_obj(*op, GFP_NOFS);
107 if (!op)
108 return -ENOMEM;
109
110 op->info = *orig_info;
111 op->info.optype = DLM_PLOCK_OP_CANCEL;
112 op->info.wait = 0;
113
114 send_op(op);
115 wait_event(recv_wq, (op->done != 0));
116
117 rv = op->info.rv;
118
119 dlm_release_plock_op(op);
120 return rv;
121}
122
123int dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
124 int cmd, struct file_lock *fl)
125{
126 struct plock_async_data *op_data;
127 struct dlm_ls *ls;
128 struct plock_op *op;
129 int rv;
130
131 ls = dlm_find_lockspace_local(lockspace);
132 if (!ls)
133 return -EINVAL;
134
135 op = kzalloc_obj(*op, GFP_NOFS);
136 if (!op) {
137 rv = -ENOMEM;
138 goto out;
139 }
140
141 op->info.optype = DLM_PLOCK_OP_LOCK;
142 op->info.pid = fl->c.flc_pid;
143 op->info.ex = lock_is_write(fl);
144 op->info.wait = !!(fl->c.flc_flags & FL_SLEEP);
145 op->info.fsid = ls->ls_global_id;
146 op->info.number = number;
147 op->info.start = fl->fl_start;
148 op->info.end = fl->fl_end;
149 op->info.owner = (__u64)(long) fl->c.flc_owner;
150 /* async handling */
151 if (fl->fl_lmops && fl->fl_lmops->lm_grant) {
152 op_data = kzalloc_obj(*op_data, GFP_NOFS);
153 if (!op_data) {
154 dlm_release_plock_op(op);
155 rv = -ENOMEM;
156 goto out;
157 }
158
159 op_data->callback = fl->fl_lmops->lm_grant;
160 locks_init_lock(&op_data->flc);
161 locks_copy_lock(&op_data->flc, fl);
162 op_data->fl = fl;
163 op_data->file = file;
164
165 op->data = op_data;
166
167 send_op(op);
168 rv = FILE_LOCK_DEFERRED;
169 goto out;
170 }
171
172 send_op(op);
173
174 if (op->info.wait) {
175 rv = wait_event_interruptible(recv_wq, (op->done != 0));
176 if (rv == -ERESTARTSYS) {
177 spin_lock(&ops_lock);
178 /* recheck under ops_lock if we got a done != 0,
179 * if so this interrupt case should be ignored
180 */
181 if (op->done != 0) {
182 spin_unlock(&ops_lock);
183 goto do_lock_wait;
184 }
185 spin_unlock(&ops_lock);
186
187 rv = do_lock_cancel(&op->info);
188 switch (rv) {
189 case 0:
190 /* waiter was deleted in user space, answer will never come
191 * remove original request. The original request must be
192 * on recv_list because the answer of do_lock_cancel()
193 * synchronized it.
194 */
195 spin_lock(&ops_lock);
196 list_del(&op->list);
197 spin_unlock(&ops_lock);
198 rv = -EINTR;
199 break;
200 case -ENOENT:
201 /* cancellation wasn't successful but op should be done */
202 fallthrough;
203 default:
204 /* internal error doing cancel we need to wait */
205 goto wait;
206 }
207
208 log_debug(ls, "%s: wait interrupted %x %llx pid %d",
209 __func__, ls->ls_global_id,
210 (unsigned long long)number, op->info.pid);
211 dlm_release_plock_op(op);
212 goto out;
213 }
214 } else {
215wait:
216 wait_event(recv_wq, (op->done != 0));
217 }
218
219do_lock_wait:
220
221 WARN_ON(!list_empty(&op->list));
222
223 rv = op->info.rv;
224
225 if (!rv) {
226 if (locks_lock_file_wait(file, fl) < 0)
227 log_error(ls, "dlm_posix_lock: vfs lock error %llx",
228 (unsigned long long)number);
229 }
230
231 dlm_release_plock_op(op);
232out:
233 dlm_put_lockspace(ls);
234 return rv;
235}
236EXPORT_SYMBOL_GPL(dlm_posix_lock);
237
238/* Returns failure iff a successful lock operation should be canceled */
239static int dlm_plock_callback(struct plock_op *op)
240{
241 struct plock_async_data *op_data = op->data;
242 struct file *file;
243 struct file_lock *fl;
244 struct file_lock *flc;
245 int (*notify)(struct file_lock *fl, int result) = NULL;
246 int rv = 0;
247
248 WARN_ON(!list_empty(&op->list));
249
250 /* check if the following 2 are still valid or make a copy */
251 file = op_data->file;
252 flc = &op_data->flc;
253 fl = op_data->fl;
254 notify = op_data->callback;
255
256 if (op->info.rv) {
257 notify(fl, op->info.rv);
258 goto out;
259 }
260
261 /* got fs lock; bookkeep locally as well: */
262 flc->c.flc_flags &= ~FL_SLEEP;
263 if (posix_lock_file(file, flc, NULL)) {
264 /*
265 * This can only happen in the case of kmalloc() failure.
266 * The filesystem's own lock is the authoritative lock,
267 * so a failure to get the lock locally is not a disaster.
268 * As long as the fs cannot reliably cancel locks (especially
269 * in a low-memory situation), we're better off ignoring
270 * this failure than trying to recover.
271 */
272 log_print("dlm_plock_callback: vfs lock error %llx file %p fl %p",
273 (unsigned long long)op->info.number, file, fl);
274 }
275
276 rv = notify(fl, 0);
277 if (rv) {
278 /* XXX: We need to cancel the fs lock here: */
279 log_print("%s: lock granted after lock request failed; dangling lock!",
280 __func__);
281 goto out;
282 }
283
284out:
285 dlm_release_plock_op(op);
286 return rv;
287}
288
289int dlm_posix_unlock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
290 struct file_lock *fl)
291{
292 struct dlm_ls *ls;
293 struct plock_op *op;
294 int rv;
295 unsigned char saved_flags = fl->c.flc_flags;
296
297 ls = dlm_find_lockspace_local(lockspace);
298 if (!ls)
299 return -EINVAL;
300
301 op = kzalloc_obj(*op, GFP_NOFS);
302 if (!op) {
303 rv = -ENOMEM;
304 goto out;
305 }
306
307 /* cause the vfs unlock to return ENOENT if lock is not found */
308 fl->c.flc_flags |= FL_EXISTS;
309
310 rv = locks_lock_file_wait(file, fl);
311 if (rv == -ENOENT) {
312 rv = 0;
313 goto out_free;
314 }
315 if (rv < 0) {
316 log_error(ls, "dlm_posix_unlock: vfs unlock error %d %llx",
317 rv, (unsigned long long)number);
318 }
319
320 op->info.optype = DLM_PLOCK_OP_UNLOCK;
321 op->info.pid = fl->c.flc_pid;
322 op->info.fsid = ls->ls_global_id;
323 op->info.number = number;
324 op->info.start = fl->fl_start;
325 op->info.end = fl->fl_end;
326 op->info.owner = (__u64)(long) fl->c.flc_owner;
327
328 if (fl->c.flc_flags & FL_CLOSE) {
329 op->info.flags |= DLM_PLOCK_FL_CLOSE;
330 send_op(op);
331 rv = 0;
332 goto out;
333 }
334
335 send_op(op);
336 wait_event(recv_wq, (op->done != 0));
337
338 WARN_ON(!list_empty(&op->list));
339
340 rv = op->info.rv;
341
342 if (rv == -ENOENT)
343 rv = 0;
344
345out_free:
346 dlm_release_plock_op(op);
347out:
348 dlm_put_lockspace(ls);
349 fl->c.flc_flags = saved_flags;
350 return rv;
351}
352EXPORT_SYMBOL_GPL(dlm_posix_unlock);
353
354/*
355 * NOTE: This implementation can only handle async lock requests as nfs
356 * do it. It cannot handle cancellation of a pending lock request sitting
357 * in wait_event(), but for now only nfs is the only user local kernel
358 * user.
359 */
360int dlm_posix_cancel(dlm_lockspace_t *lockspace, u64 number, struct file *file,
361 struct file_lock *fl)
362{
363 struct dlm_plock_info info;
364 struct plock_op *op;
365 struct dlm_ls *ls;
366 int rv;
367
368 /* this only works for async request for now and nfs is the only
369 * kernel user right now.
370 */
371 if (WARN_ON_ONCE(!fl->fl_lmops || !fl->fl_lmops->lm_grant))
372 return -EOPNOTSUPP;
373
374 ls = dlm_find_lockspace_local(lockspace);
375 if (!ls)
376 return -EINVAL;
377
378 memset(&info, 0, sizeof(info));
379 info.pid = fl->c.flc_pid;
380 info.ex = lock_is_write(fl);
381 info.fsid = ls->ls_global_id;
382 dlm_put_lockspace(ls);
383 info.number = number;
384 info.start = fl->fl_start;
385 info.end = fl->fl_end;
386 info.owner = (__u64)(long) fl->c.flc_owner;
387
388 rv = do_lock_cancel(&info);
389 switch (rv) {
390 case 0:
391 spin_lock(&ops_lock);
392 /* lock request to cancel must be on recv_list because
393 * do_lock_cancel() synchronizes it.
394 */
395 op = plock_lookup_waiter(&info);
396 if (WARN_ON_ONCE(!op)) {
397 spin_unlock(&ops_lock);
398 rv = -ENOLCK;
399 break;
400 }
401
402 list_del(&op->list);
403 spin_unlock(&ops_lock);
404 WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK);
405 op->data->callback(op->data->fl, -EINTR);
406 dlm_release_plock_op(op);
407 rv = -EINTR;
408 break;
409 case -ENOENT:
410 /* if cancel wasn't successful we probably were to late
411 * or it was a non-blocking lock request, so just unlock it.
412 */
413 rv = dlm_posix_unlock(lockspace, number, file, fl);
414 break;
415 default:
416 break;
417 }
418
419 return rv;
420}
421EXPORT_SYMBOL_GPL(dlm_posix_cancel);
422
423int dlm_posix_get(dlm_lockspace_t *lockspace, u64 number, struct file *file,
424 struct file_lock *fl)
425{
426 struct dlm_ls *ls;
427 struct plock_op *op;
428 int rv;
429
430 ls = dlm_find_lockspace_local(lockspace);
431 if (!ls)
432 return -EINVAL;
433
434 op = kzalloc_obj(*op, GFP_NOFS);
435 if (!op) {
436 rv = -ENOMEM;
437 goto out;
438 }
439
440 op->info.optype = DLM_PLOCK_OP_GET;
441 op->info.pid = fl->c.flc_pid;
442 op->info.ex = lock_is_write(fl);
443 op->info.fsid = ls->ls_global_id;
444 op->info.number = number;
445 op->info.start = fl->fl_start;
446 op->info.end = fl->fl_end;
447 op->info.owner = (__u64)(long) fl->c.flc_owner;
448
449 send_op(op);
450 wait_event(recv_wq, (op->done != 0));
451
452 WARN_ON(!list_empty(&op->list));
453
454 /* info.rv from userspace is 1 for conflict, 0 for no-conflict,
455 -ENOENT if there are no locks on the file */
456
457 rv = op->info.rv;
458
459 fl->c.flc_type = F_UNLCK;
460 if (rv == -ENOENT)
461 rv = 0;
462 else if (rv > 0) {
463 locks_init_lock(fl);
464 fl->c.flc_type = (op->info.ex) ? F_WRLCK : F_RDLCK;
465 fl->c.flc_flags = FL_POSIX;
466 fl->c.flc_pid = op->info.pid;
467 if (op->info.nodeid != dlm_our_nodeid())
468 fl->c.flc_pid = -fl->c.flc_pid;
469 fl->fl_start = op->info.start;
470 fl->fl_end = op->info.end;
471 rv = 0;
472 }
473
474 dlm_release_plock_op(op);
475out:
476 dlm_put_lockspace(ls);
477 return rv;
478}
479EXPORT_SYMBOL_GPL(dlm_posix_get);
480
481static int dev_open(struct inode *inode, struct file *file)
482{
483 /* Userspace plock daemon is a privileged cluster component. */
484 if (!capable(CAP_SYS_ADMIN))
485 return -EPERM;
486
487 return 0;
488}
489
490/* a read copies out one plock request from the send list */
491static ssize_t dev_read(struct file *file, char __user *u, size_t count,
492 loff_t *ppos)
493{
494 struct dlm_plock_info info;
495 struct plock_op *op = NULL;
496
497 if (count < sizeof(info))
498 return -EINVAL;
499
500 spin_lock(&ops_lock);
501 if (!list_empty(&send_list)) {
502 op = list_first_entry(&send_list, struct plock_op, list);
503 if (op->info.flags & DLM_PLOCK_FL_CLOSE)
504 list_del(&op->list);
505 else
506 list_move_tail(&op->list, &recv_list);
507 memcpy(&info, &op->info, sizeof(info));
508 }
509 spin_unlock(&ops_lock);
510
511 if (!op)
512 return -EAGAIN;
513
514 trace_dlm_plock_read(&info);
515
516 /* there is no need to get a reply from userspace for unlocks
517 that were generated by the vfs cleaning up for a close
518 (the process did not make an unlock call). */
519
520 if (op->info.flags & DLM_PLOCK_FL_CLOSE)
521 dlm_release_plock_op(op);
522
523 if (copy_to_user(u, &info, sizeof(info)))
524 return -EFAULT;
525 return sizeof(info);
526}
527
528/* a write copies in one plock result that should match a plock_op
529 on the recv list */
530static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
531 loff_t *ppos)
532{
533 struct plock_op *op = NULL, *iter;
534 struct dlm_plock_info info;
535 int do_callback = 0;
536
537 if (count != sizeof(info))
538 return -EINVAL;
539
540 if (copy_from_user(&info, u, sizeof(info)))
541 return -EFAULT;
542
543 trace_dlm_plock_write(&info);
544
545 if (check_version(&info))
546 return -EINVAL;
547
548 /*
549 * The results for waiting ops (SETLKW) can be returned in any
550 * order, so match all fields to find the op. The results for
551 * non-waiting ops are returned in the order that they were sent
552 * to userspace, so match the result with the first non-waiting op.
553 */
554 spin_lock(&ops_lock);
555 if (info.wait) {
556 op = plock_lookup_waiter(&info);
557 } else {
558 list_for_each_entry(iter, &recv_list, list) {
559 if (!iter->info.wait &&
560 iter->info.fsid == info.fsid) {
561 op = iter;
562 break;
563 }
564 }
565 }
566
567 if (op) {
568 /* Sanity check that op and info match. */
569 if (info.wait)
570 WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK);
571 else
572 WARN_ON(op->info.number != info.number ||
573 op->info.owner != info.owner ||
574 op->info.optype != info.optype);
575
576 list_del_init(&op->list);
577 memcpy(&op->info, &info, sizeof(info));
578 if (op->data)
579 do_callback = 1;
580 else
581 op->done = 1;
582 }
583 spin_unlock(&ops_lock);
584
585 if (op) {
586 if (do_callback)
587 dlm_plock_callback(op);
588 else
589 wake_up(&recv_wq);
590 } else
591 pr_debug("%s: no op %x %llx", __func__,
592 info.fsid, (unsigned long long)info.number);
593 return count;
594}
595
596static __poll_t dev_poll(struct file *file, poll_table *wait)
597{
598 __poll_t mask = 0;
599
600 poll_wait(file, &send_wq, wait);
601
602 spin_lock(&ops_lock);
603 if (!list_empty(&send_list))
604 mask = EPOLLIN | EPOLLRDNORM;
605 spin_unlock(&ops_lock);
606
607 return mask;
608}
609
610static const struct file_operations dev_fops = {
611 .open = dev_open,
612 .read = dev_read,
613 .write = dev_write,
614 .poll = dev_poll,
615 .owner = THIS_MODULE,
616 .llseek = noop_llseek,
617};
618
619static struct miscdevice plock_dev_misc = {
620 .minor = MISC_DYNAMIC_MINOR,
621 .name = DLM_PLOCK_MISC_NAME,
622 .fops = &dev_fops,
623 .mode = 0600,
624};
625
626int dlm_plock_init(void)
627{
628 int rv;
629
630 rv = misc_register(&plock_dev_misc);
631 if (rv)
632 log_print("dlm_plock_init: misc_register failed %d", rv);
633 return rv;
634}
635
636void dlm_plock_exit(void)
637{
638 misc_deregister(&plock_dev_misc);
639 WARN_ON(!list_empty(&send_list));
640 WARN_ON(!list_empty(&recv_list));
641}
642