if (op->info.wait) {
rv = wait_event_interruptible(recv_wq, (op->done != 0)); if (rv == -ERESTARTSYS) {
spin_lock(&ops_lock); /* recheck under ops_lock if we got a done != 0, * if so this interrupt case should be ignored
*/ if (op->done != 0) {
spin_unlock(&ops_lock); goto do_lock_wait;
}
spin_unlock(&ops_lock);
rv = do_lock_cancel(&op->info); switch (rv) { case 0: /* waiter was deleted in user space, answer will never come * remove original request. The original request must be * on recv_list because the answer of do_lock_cancel() * synchronized it.
*/
spin_lock(&ops_lock);
list_del(&op->list);
spin_unlock(&ops_lock);
rv = -EINTR; break; case -ENOENT: /* cancellation wasn't successful but op should be done */
fallthrough; default: /* internal error doing cancel we need to wait */ goto wait;
}
/* Returns failure iff a successful lock operation should be canceled */ staticint dlm_plock_callback(struct plock_op *op)
{ struct plock_async_data *op_data = op->data; struct file *file; struct file_lock *fl; struct file_lock *flc; int (*notify)(struct file_lock *fl, int result) = NULL; int rv = 0;
WARN_ON(!list_empty(&op->list));
/* check if the following 2 are still valid or make a copy */
file = op_data->file;
flc = &op_data->flc;
fl = op_data->fl;
notify = op_data->callback;
if (op->info.rv) {
notify(fl, op->info.rv); goto out;
}
/* got fs lock; bookkeep locally as well: */
flc->c.flc_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. * As long as the fs cannot reliably cancel locks (especially * in a low-memory situation), we're better off ignoring * this failure than trying to recover.
*/
log_print("dlm_plock_callback: vfs lock error %llx file %p fl %p",
(unsignedlonglong)op->info.number, file, fl);
}
rv = notify(fl, 0); if (rv) { /* XXX: We need to cancel the fs lock here: */
log_print("%s: lock granted after lock request failed; dangling lock!",
__func__); goto out;
}
/* * NOTE: This implementation can only handle async lock requests as nfs * do it. It cannot handle cancellation of a pending lock request sitting * in wait_event(), but for now only nfs is the only user local kernel * user.
*/ int dlm_posix_cancel(dlm_lockspace_t *lockspace, u64 number, struct file *file, struct file_lock *fl)
{ struct dlm_plock_info info; struct plock_op *op; struct dlm_ls *ls; int rv;
/* this only works for async request for now and nfs is the only * kernel user right now.
*/ if (WARN_ON_ONCE(!fl->fl_lmops || !fl->fl_lmops->lm_grant)) return -EOPNOTSUPP;
ls = dlm_find_lockspace_local(lockspace); if (!ls) return -EINVAL;
rv = do_lock_cancel(&info); switch (rv) { case 0:
spin_lock(&ops_lock); /* lock request to cancel must be on recv_list because * do_lock_cancel() synchronizes it.
*/
op = plock_lookup_waiter(&info); if (WARN_ON_ONCE(!op)) {
spin_unlock(&ops_lock);
rv = -ENOLCK; break;
}
list_del(&op->list);
spin_unlock(&ops_lock);
WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK);
op->data->callback(op->data->fl, -EINTR);
dlm_release_plock_op(op);
rv = -EINTR; break; case -ENOENT: /* if cancel wasn't successful we probably were to late * or it was a non-blocking lock request, so just unlock it.
*/
rv = dlm_posix_unlock(lockspace, number, file, fl); break; default: break;
}
return rv;
}
EXPORT_SYMBOL_GPL(dlm_posix_cancel);
int dlm_posix_get(dlm_lockspace_t *lockspace, u64 number, struct file *file, struct file_lock *fl)
{ struct dlm_ls *ls; struct plock_op *op; int rv;
ls = dlm_find_lockspace_local(lockspace); if (!ls) return -EINVAL;
op = kzalloc(sizeof(*op), GFP_NOFS); if (!op) {
rv = -ENOMEM; goto out;
}
/* 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)
{ struct dlm_plock_info info; struct plock_op *op = NULL;
if (count < sizeof(info)) return -EINVAL;
spin_lock(&ops_lock); if (!list_empty(&send_list)) {
op = list_first_entry(&send_list, struct plock_op, list); if (op->info.flags & DLM_PLOCK_FL_CLOSE)
list_del(&op->list); else
list_move_tail(&op->list, &recv_list);
memcpy(&info, &op->info, sizeof(info));
}
spin_unlock(&ops_lock);
if (!op) return -EAGAIN;
trace_dlm_plock_read(&info);
/* there is no need to get a reply from userspace for unlocks that were generated by the vfs cleaning up for a close
(the process did not make an unlock call). */
if (op->info.flags & DLM_PLOCK_FL_CLOSE)
dlm_release_plock_op(op);
if (copy_to_user(u, &info, sizeof(info))) return -EFAULT; returnsizeof(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, constchar __user *u, size_t count,
loff_t *ppos)
{ struct plock_op *op = NULL, *iter; struct dlm_plock_info info; int do_callback = 0;
if (count != sizeof(info)) return -EINVAL;
if (copy_from_user(&info, u, sizeof(info))) return -EFAULT;
trace_dlm_plock_write(&info);
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); if (info.wait) {
op = plock_lookup_waiter(&info);
} else {
list_for_each_entry(iter, &recv_list, list) { if (!iter->info.wait &&
iter->info.fsid == info.fsid) {
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.number != info.number ||
op->info.owner != info.owner ||
op->info.optype != info.optype);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.