fs: dlm: fix mismatch of plock results from userspace
When a waiting plock request (F_SETLKW) is sent to userspace for processing (dlm_controld), the result is returned at a later time. That result could be incorrectly matched to a different waiting request in cases where the owner field is the same (e.g. different threads in a process.) This is fixed by comparing all the properties in the request and reply. The results for non-waiting plock requests are now matched based on list order because the results are returned in the same order they were sent. Cc: stable@vger.kernel.org Signed-off-by: Alexander Aring <aahringo@redhat.com> Signed-off-by: David Teigland <teigland@redhat.com>
This commit is contained in:
parent
0f2b1cb89c
commit
57e2c2f2d9
@ -395,7 +395,7 @@ static ssize_t dev_read(struct file *file, char __user *u, size_t count,
|
||||
if (op->info.flags & DLM_PLOCK_FL_CLOSE)
|
||||
list_del(&op->list);
|
||||
else
|
||||
list_move(&op->list, &recv_list);
|
||||
list_move_tail(&op->list, &recv_list);
|
||||
memcpy(&info, &op->info, sizeof(info));
|
||||
}
|
||||
spin_unlock(&ops_lock);
|
||||
@ -433,20 +433,52 @@ static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
|
||||
if (check_version(&info))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The results for waiting ops (SETLKW) can be returned in any
|
||||
* order, so match all fields to find the op. The results for
|
||||
* non-waiting ops are returned in the order that they were sent
|
||||
* to userspace, so match the result with the first non-waiting op.
|
||||
*/
|
||||
spin_lock(&ops_lock);
|
||||
list_for_each_entry(iter, &recv_list, list) {
|
||||
if (iter->info.fsid == info.fsid &&
|
||||
iter->info.number == info.number &&
|
||||
iter->info.owner == info.owner) {
|
||||
list_del_init(&iter->list);
|
||||
memcpy(&iter->info, &info, sizeof(info));
|
||||
if (iter->data)
|
||||
do_callback = 1;
|
||||
else
|
||||
iter->done = 1;
|
||||
op = iter;
|
||||
break;
|
||||
if (info.wait) {
|
||||
list_for_each_entry(iter, &recv_list, list) {
|
||||
if (iter->info.fsid == info.fsid &&
|
||||
iter->info.number == info.number &&
|
||||
iter->info.owner == info.owner &&
|
||||
iter->info.pid == info.pid &&
|
||||
iter->info.start == info.start &&
|
||||
iter->info.end == info.end &&
|
||||
iter->info.ex == info.ex &&
|
||||
iter->info.wait) {
|
||||
op = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
list_for_each_entry(iter, &recv_list, list) {
|
||||
if (!iter->info.wait) {
|
||||
op = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op) {
|
||||
/* Sanity check that op and info match. */
|
||||
if (info.wait)
|
||||
WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK);
|
||||
else
|
||||
WARN_ON(op->info.fsid != info.fsid ||
|
||||
op->info.number != info.number ||
|
||||
op->info.owner != info.owner ||
|
||||
op->info.optype != info.optype);
|
||||
|
||||
list_del_init(&op->list);
|
||||
memcpy(&op->info, &info, sizeof(info));
|
||||
if (op->data)
|
||||
do_callback = 1;
|
||||
else
|
||||
op->done = 1;
|
||||
}
|
||||
spin_unlock(&ops_lock);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user