Files
linux-apfs/fs/dlm/plock.c
T

440 lines
9.7 KiB
C
Raw Normal View History

2006-01-17 08:47:12 +00:00
/*
2008-03-14 15:09:15 -05:00
* Copyright (C) 2005-2008 Red Hat, Inc. All rights reserved.
2006-01-17 08:47:12 +00:00
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
2006-09-01 11:05:15 -04:00
* of the GNU General Public License version 2.
2006-01-17 08:47:12 +00:00
*/
2008-03-14 15:09:15 -05:00
#include <linux/fs.h>
2006-01-17 08:47:12 +00:00
#include <linux/miscdevice.h>
2006-10-19 17:23:57 -04:00
#include <linux/poll.h>
2008-03-14 15:09:15 -05:00
#include <linux/dlm.h>
#include <linux/dlm_plock.h>
2006-01-17 08:47:12 +00:00
2008-03-14 15:09:15 -05:00
#include "dlm_internal.h"
#include "lockspace.h"
2006-01-17 08:47:12 +00:00
static spinlock_t ops_lock;
static struct list_head send_list;
static struct list_head recv_list;
static wait_queue_head_t send_wq;
static wait_queue_head_t recv_wq;
struct plock_op {
struct list_head list;
int done;
2008-03-14 15:09:15 -05:00
struct dlm_plock_info info;
2006-01-17 08:47:12 +00:00
};
2006-11-14 16:37:25 -05:00
struct plock_xop {
struct plock_op xop;
void *callback;
void *fl;
void *file;
struct file_lock flc;
};
2008-03-14 15:09:15 -05:00
static inline void set_version(struct dlm_plock_info *info)
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
info->version[0] = DLM_PLOCK_VERSION_MAJOR;
info->version[1] = DLM_PLOCK_VERSION_MINOR;
info->version[2] = DLM_PLOCK_VERSION_PATCH;
2006-01-17 08:47:12 +00:00
}
2008-03-14 15:09:15 -05:00
static int check_version(struct dlm_plock_info *info)
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
if ((DLM_PLOCK_VERSION_MAJOR != info->version[0]) ||
(DLM_PLOCK_VERSION_MINOR < info->version[1])) {
log_print("plock device version mismatch: "
2006-01-17 08:47:12 +00:00
"kernel (%u.%u.%u), user (%u.%u.%u)",
2008-03-14 15:09:15 -05:00
DLM_PLOCK_VERSION_MAJOR,
DLM_PLOCK_VERSION_MINOR,
DLM_PLOCK_VERSION_PATCH,
2006-01-17 08:47:12 +00:00
info->version[0],
info->version[1],
info->version[2]);
return -EINVAL;
}
return 0;
}
static void send_op(struct plock_op *op)
{
set_version(&op->info);
INIT_LIST_HEAD(&op->list);
spin_lock(&ops_lock);
list_add_tail(&op->list, &send_list);
spin_unlock(&ops_lock);
wake_up(&send_wq);
}
2008-03-14 15:09:15 -05:00
int dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
int cmd, struct file_lock *fl)
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
struct dlm_ls *ls;
2006-01-17 08:47:12 +00:00
struct plock_op *op;
2006-11-14 16:37:25 -05:00
struct plock_xop *xop;
2006-01-17 08:47:12 +00:00
int rv;
2008-03-14 15:09:15 -05:00
ls = dlm_find_lockspace_local(lockspace);
if (!ls)
return -EINVAL;
2006-11-14 16:37:25 -05:00
xop = kzalloc(sizeof(*xop), GFP_KERNEL);
2008-03-14 15:09:15 -05:00
if (!xop) {
rv = -ENOMEM;
goto out;
}
2006-01-17 08:47:12 +00:00
2006-11-14 16:37:25 -05:00
op = &xop->xop;
2008-03-14 15:09:15 -05:00
op->info.optype = DLM_PLOCK_OP_LOCK;
op->info.pid = fl->fl_pid;
2006-01-17 08:47:12 +00:00
op->info.ex = (fl->fl_type == F_WRLCK);
op->info.wait = IS_SETLKW(cmd);
2008-03-14 15:09:15 -05:00
op->info.fsid = ls->ls_global_id;
op->info.number = number;
2006-01-17 08:47:12 +00:00
op->info.start = fl->fl_start;
op->info.end = fl->fl_end;
2006-11-14 16:37:25 -05:00
if (fl->fl_lmops && fl->fl_lmops->fl_grant) {
/* fl_owner is lockd which doesn't distinguish
processes on the nfs client */
op->info.owner = (__u64) fl->fl_pid;
2006-11-14 16:37:25 -05:00
xop->callback = fl->fl_lmops->fl_grant;
locks_init_lock(&xop->flc);
locks_copy_lock(&xop->flc, fl);
xop->fl = fl;
xop->file = file;
} else {
op->info.owner = (__u64)(long) fl->fl_owner;
2006-11-14 16:37:25 -05:00
xop->callback = NULL;
}
2006-01-17 08:47:12 +00:00
send_op(op);
2006-11-14 16:37:25 -05:00
if (xop->callback == NULL)
wait_event(recv_wq, (op->done != 0));
2008-03-14 15:09:15 -05:00
else {
rv = FILE_LOCK_DEFERRED;
2008-03-14 15:09:15 -05:00
goto out;
}
2006-01-17 08:47:12 +00:00
spin_lock(&ops_lock);
if (!list_empty(&op->list)) {
2008-03-14 15:09:15 -05:00
log_error(ls, "dlm_posix_lock: op on list %llx",
(unsigned long long)number);
2006-01-17 08:47:12 +00:00
list_del(&op->list);
}
spin_unlock(&ops_lock);
rv = op->info.rv;
if (!rv) {
if (posix_lock_file_wait(file, fl) < 0)
2008-03-14 15:09:15 -05:00
log_error(ls, "dlm_posix_lock: vfs lock error %llx",
(unsigned long long)number);
2006-01-17 08:47:12 +00:00
}
2006-11-14 16:37:25 -05:00
kfree(xop);
2008-03-14 15:09:15 -05:00
out:
dlm_put_lockspace(ls);
2006-11-14 16:37:25 -05:00
return rv;
}
2008-03-14 15:09:15 -05:00
EXPORT_SYMBOL_GPL(dlm_posix_lock);
2006-11-14 16:37:25 -05:00
/* Returns failure iff a succesful lock operation should be canceled */
2008-03-14 15:09:15 -05:00
static int dlm_plock_callback(struct plock_op *op)
2006-11-14 16:37:25 -05:00
{
struct file *file;
struct file_lock *fl;
struct file_lock *flc;
int (*notify)(void *, void *, int) = NULL;
struct plock_xop *xop = (struct plock_xop *)op;
int rv = 0;
spin_lock(&ops_lock);
if (!list_empty(&op->list)) {
2008-03-14 15:09:15 -05:00
log_print("dlm_plock_callback: op on list %llx",
(unsigned long long)op->info.number);
2006-11-14 16:37:25 -05:00
list_del(&op->list);
}
spin_unlock(&ops_lock);
/* check if the following 2 are still valid or make a copy */
file = xop->file;
flc = &xop->flc;
fl = xop->fl;
notify = xop->callback;
if (op->info.rv) {
notify(flc, NULL, op->info.rv);
goto out;
}
/* got fs lock; bookkeep locally as well: */
flc->fl_flags &= ~FL_SLEEP;
if (posix_lock_file(file, flc, NULL)) {
/*
* This can only happen in the case of kmalloc() failure.
* The filesystem's own lock is the authoritative lock,
* so a failure to get the lock locally is not a disaster.
2008-03-14 15:09:15 -05:00
* As long as the fs cannot reliably cancel locks (especially
2006-11-14 16:37:25 -05:00
* in a low-memory situation), we're better off ignoring
* this failure than trying to recover.
*/
2008-03-14 15:09:15 -05:00
log_print("dlm_plock_callback: vfs lock error %llx file %p fl %p",
(unsigned long long)op->info.number, file, fl);
2006-11-14 16:37:25 -05:00
}
rv = notify(flc, NULL, 0);
if (rv) {
/* XXX: We need to cancel the fs lock here: */
2008-03-14 15:09:15 -05:00
log_print("dlm_plock_callback: lock granted after lock request "
"failed; dangling lock!\n");
2006-11-14 16:37:25 -05:00
goto out;
}
out:
kfree(xop);
2006-01-17 08:47:12 +00:00
return rv;
}
2008-03-14 15:09:15 -05:00
int dlm_posix_unlock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
struct file_lock *fl)
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
struct dlm_ls *ls;
2006-01-17 08:47:12 +00:00
struct plock_op *op;
int rv;
2008-03-14 15:09:15 -05:00
ls = dlm_find_lockspace_local(lockspace);
if (!ls)
return -EINVAL;
2006-01-17 08:47:12 +00:00
op = kzalloc(sizeof(*op), GFP_KERNEL);
2008-03-14 15:09:15 -05:00
if (!op) {
rv = -ENOMEM;
goto out;
}
2006-01-17 08:47:12 +00:00
if (posix_lock_file_wait(file, fl) < 0)
2008-03-14 15:09:15 -05:00
log_error(ls, "dlm_posix_unlock: vfs unlock error %llx",
(unsigned long long)number);
2006-01-17 08:47:12 +00:00
2008-03-14 15:09:15 -05:00
op->info.optype = DLM_PLOCK_OP_UNLOCK;
op->info.pid = fl->fl_pid;
2008-03-14 15:09:15 -05:00
op->info.fsid = ls->ls_global_id;
op->info.number = number;
2006-01-17 08:47:12 +00:00
op->info.start = fl->fl_start;
op->info.end = fl->fl_end;
if (fl->fl_lmops && fl->fl_lmops->fl_grant)
op->info.owner = (__u64) fl->fl_pid;
else
op->info.owner = (__u64)(long) fl->fl_owner;
2006-01-17 08:47:12 +00:00
send_op(op);
wait_event(recv_wq, (op->done != 0));
spin_lock(&ops_lock);
if (!list_empty(&op->list)) {
2008-03-14 15:09:15 -05:00
log_error(ls, "dlm_posix_unlock: op on list %llx",
(unsigned long long)number);
2006-01-17 08:47:12 +00:00
list_del(&op->list);
}
spin_unlock(&ops_lock);
rv = op->info.rv;
2006-11-14 16:37:25 -05:00
if (rv == -ENOENT)
rv = 0;
2006-01-17 08:47:12 +00:00
kfree(op);
2008-03-14 15:09:15 -05:00
out:
dlm_put_lockspace(ls);
2006-01-17 08:47:12 +00:00
return rv;
}
2008-03-14 15:09:15 -05:00
EXPORT_SYMBOL_GPL(dlm_posix_unlock);
2006-01-17 08:47:12 +00:00
2008-03-14 15:09:15 -05:00
int dlm_posix_get(dlm_lockspace_t *lockspace, u64 number, struct file *file,
struct file_lock *fl)
2006-01-17 08:47:12 +00:00
{
2008-03-14 15:09:15 -05:00
struct dlm_ls *ls;
2006-01-17 08:47:12 +00:00
struct plock_op *op;
int rv;
2008-03-14 15:09:15 -05:00
ls = dlm_find_lockspace_local(lockspace);
if (!ls)
return -EINVAL;
2006-01-17 08:47:12 +00:00
2008-03-14 15:09:15 -05:00
op = kzalloc(sizeof(*op), GFP_KERNEL);
if (!op) {
rv = -ENOMEM;
goto out;
}
op->info.optype = DLM_PLOCK_OP_GET;
op->info.pid = fl->fl_pid;
2006-01-17 08:47:12 +00:00
op->info.ex = (fl->fl_type == F_WRLCK);
2008-03-14 15:09:15 -05:00
op->info.fsid = ls->ls_global_id;
op->info.number = number;
2006-01-17 08:47:12 +00:00
op->info.start = fl->fl_start;
op->info.end = fl->fl_end;
if (fl->fl_lmops && fl->fl_lmops->fl_grant)
op->info.owner = (__u64) fl->fl_pid;
else
op->info.owner = (__u64)(long) fl->fl_owner;
2006-11-14 16:37:25 -05:00
2006-01-17 08:47:12 +00:00
send_op(op);
wait_event(recv_wq, (op->done != 0));
spin_lock(&ops_lock);
if (!list_empty(&op->list)) {
2008-03-14 15:09:15 -05:00
log_error(ls, "dlm_posix_get: op on list %llx",
(unsigned long long)number);
2006-01-17 08:47:12 +00:00
list_del(&op->list);
}
spin_unlock(&ops_lock);
2007-06-08 17:01:40 -05:00
/* info.rv from userspace is 1 for conflict, 0 for no-conflict,
-ENOENT if there are no locks on the file */
2006-01-17 08:47:12 +00:00
rv = op->info.rv;
2006-11-14 16:37:25 -05:00
fl->fl_type = F_UNLCK;
if (rv == -ENOENT)
rv = 0;
2007-06-08 17:01:40 -05:00
else if (rv > 0) {
2006-01-17 08:47:12 +00:00
fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK;
fl->fl_pid = op->info.pid;
fl->fl_start = op->info.start;
fl->fl_end = op->info.end;
2007-06-08 17:01:40 -05:00
rv = 0;
2006-01-17 08:47:12 +00:00
}
kfree(op);
2008-03-14 15:09:15 -05:00
out:
dlm_put_lockspace(ls);
2006-01-17 08:47:12 +00:00
return rv;
}
2008-03-14 15:09:15 -05:00
EXPORT_SYMBOL_GPL(dlm_posix_get);
2006-01-17 08:47:12 +00:00
/* a read copies out one plock request from the send list */
static ssize_t dev_read(struct file *file, char __user *u, size_t count,
loff_t *ppos)
{
2008-03-14 15:09:15 -05:00
struct dlm_plock_info info;
2006-01-17 08:47:12 +00:00
struct plock_op *op = NULL;
if (count < sizeof(info))
return -EINVAL;
spin_lock(&ops_lock);
if (!list_empty(&send_list)) {
op = list_entry(send_list.next, struct plock_op, list);
list_move(&op->list, &recv_list);
memcpy(&info, &op->info, sizeof(info));
}
spin_unlock(&ops_lock);
if (!op)
return -EAGAIN;
if (copy_to_user(u, &info, sizeof(info)))
return -EFAULT;
return sizeof(info);
}
/* a write copies in one plock result that should match a plock_op
on the recv list */
static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
loff_t *ppos)
{
2008-03-14 15:09:15 -05:00
struct dlm_plock_info info;
2006-01-17 08:47:12 +00:00
struct plock_op *op;
int found = 0;
if (count != sizeof(info))
return -EINVAL;
if (copy_from_user(&info, u, sizeof(info)))
return -EFAULT;
if (check_version(&info))
return -EINVAL;
spin_lock(&ops_lock);
list_for_each_entry(op, &recv_list, list) {
if (op->info.fsid == info.fsid && op->info.number == info.number &&
op->info.owner == info.owner) {
2006-01-17 08:47:12 +00:00
list_del_init(&op->list);
found = 1;
op->done = 1;
memcpy(&op->info, &info, sizeof(info));
break;
}
}
spin_unlock(&ops_lock);
2006-11-14 16:37:25 -05:00
if (found) {
struct plock_xop *xop;
xop = (struct plock_xop *)op;
if (xop->callback)
2008-05-13 14:28:26 -05:00
dlm_plock_callback(op);
2006-11-14 16:37:25 -05:00
else
wake_up(&recv_wq);
} else
2008-03-14 15:09:15 -05:00
log_print("dev_write no op %x %llx", info.fsid,
(unsigned long long)info.number);
2006-01-17 08:47:12 +00:00
return count;
}
static unsigned int dev_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
2006-01-17 08:47:12 +00:00
poll_wait(file, &send_wq, wait);
spin_lock(&ops_lock);
if (!list_empty(&send_list))
mask = POLLIN | POLLRDNORM;
2006-01-17 08:47:12 +00:00
spin_unlock(&ops_lock);
return mask;
2006-01-17 08:47:12 +00:00
}
2007-02-12 00:55:34 -08:00
static const struct file_operations dev_fops = {
2006-01-17 08:47:12 +00:00
.read = dev_read,
.write = dev_write,
.poll = dev_poll,
.owner = THIS_MODULE
};
static struct miscdevice plock_dev_misc = {
.minor = MISC_DYNAMIC_MINOR,
2008-03-14 15:09:15 -05:00
.name = DLM_PLOCK_MISC_NAME,
2006-01-17 08:47:12 +00:00
.fops = &dev_fops
};
2008-03-14 15:09:15 -05:00
int dlm_plock_init(void)
2006-01-17 08:47:12 +00:00
{
int rv;
spin_lock_init(&ops_lock);
INIT_LIST_HEAD(&send_list);
INIT_LIST_HEAD(&recv_list);
init_waitqueue_head(&send_wq);
init_waitqueue_head(&recv_wq);
rv = misc_register(&plock_dev_misc);
if (rv)
2008-03-14 15:09:15 -05:00
log_print("dlm_plock_init: misc_register failed %d", rv);
2006-01-17 08:47:12 +00:00
return rv;
}
2008-03-14 15:09:15 -05:00
void dlm_plock_exit(void)
2006-01-17 08:47:12 +00:00
{
if (misc_deregister(&plock_dev_misc) < 0)
2008-03-14 15:09:15 -05:00
log_print("dlm_plock_exit: misc_deregister failed");
2006-01-17 08:47:12 +00:00
}