/** * nlmclnt_proc - Perform a single client-side lock request * @host: address of a valid nlm_host context representing the NLM server * @cmd: fcntl-style file lock operation to perform * @fl: address of arguments for the lock operation * @data: address of data to be sent to callback operations *
*/ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data)
{ struct nlm_rqst *call; int status; conststruct nlmclnt_operations *nlmclnt_ops = host->h_nlmclnt_ops;
call = nlm_alloc_call(host); if (call == NULL) return -ENOMEM;
if (nlmclnt_ops && nlmclnt_ops->nlmclnt_alloc_call)
nlmclnt_ops->nlmclnt_alloc_call(data);
nlmclnt_locks_init_private(fl, host); if (!fl->fl_u.nfs_fl.owner) { /* lockowner allocation has failed */
nlmclnt_release_call(call); return -ENOMEM;
} /* Set up the argument struct */
nlmclnt_setlockargs(call, fl);
call->a_callback_data = data;
if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { if (fl->c.flc_type != F_UNLCK) {
call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
status = nlmclnt_lock(call, fl);
} else
status = nlmclnt_unlock(call, fl);
} elseif (IS_GETLK(cmd))
status = nlmclnt_test(call, fl); else
status = -EINVAL;
fl->fl_ops->fl_release_private(fl);
fl->fl_ops = NULL;
dprintk("lockd: call procedure %d on %s\n",
(int)proc, host->h_name);
do { if (host->h_reclaiming && !argp->reclaim) goto in_grace_period;
/* If we have no RPC client yet, create one. */ if ((clnt = nlm_bind_host(host)) == NULL) return -ENOLCK;
msg.rpc_proc = &clnt->cl_procinfo[proc];
/* Perform the RPC call. If an error occurs, try again */ if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) {
dprintk("lockd: rpc_call returned error %d\n", -status); switch (status) { case -EPROTONOSUPPORT:
status = -EINVAL; break; case -ECONNREFUSED: case -ETIMEDOUT: case -ENOTCONN:
nlm_rebind_host(host);
status = -EAGAIN; break; case -ERESTARTSYS: return signalled () ? -EINTR : status; default: break;
} break;
} else if (resp->status == nlm_lck_denied_grace_period) {
dprintk("lockd: server in grace period\n"); if (argp->reclaim) {
printk(KERN_WARNING "lockd: spurious grace period reject?!\n"); return -ENOLCK;
}
} else { if (!argp->reclaim) { /* We appear to be out of the grace period */
wake_up_all(&host->h_gracewait);
}
dprintk("lockd: server returns status %d\n",
ntohl(resp->status)); return 0; /* Okay, call complete */
}
in_grace_period: /* * The server has rebooted and appears to be in the grace * period during which locks are only allowed to be * reclaimed. * We can only back off and try again later.
*/
status = nlm_wait_on_grace(&host->h_gracewait);
} while (status == 0);
/* * NLM client asynchronous call. * * Note that although the calls are asynchronous, and are therefore * guaranteed to complete, we still always attempt to wait for * completion in order to be able to correctly track the lock * state.
*/ staticint nlmclnt_async_call(conststruct cred *cred, struct nlm_rqst *req, u32 proc, conststruct rpc_call_ops *tk_ops)
{ struct rpc_message msg = {
.rpc_argp = &req->a_args,
.rpc_resp = &req->a_res,
.rpc_cred = cred,
}; struct rpc_task *task; int err;
/* * LOCK: Try to create a lock * * Programmer Harassment Alert * * When given a blocking lock request in a sync RPC call, the HPUX lockd * will faithfully return LCK_BLOCKED but never cares to notify us when * the lock could be granted. This way, our local process could hang * around forever waiting for the callback. * * Solution A: Implement busy-waiting * Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES}) * * For now I am implementing solution A, because I hate the idea of * re-implementing lockd for a third time in two months. The async * calls shouldn't be too hard to do, however. * * This is one of the lovely things about standards in the NFS area: * they're so soft and squishy you can't really blame HP for doing this.
*/ staticint
nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
{ conststruct cred *cred = nfs_file_cred(fl->c.flc_file); struct nlm_host *host = req->a_host; struct nlm_res *resp = &req->a_res; struct nlm_wait block; unsignedchar flags = fl->c.flc_flags; unsignedchar type;
__be32 b_status; int status = -ENOLCK;
if (nsm_monitor(host) < 0) goto out;
req->a_args.state = nsm_local_state;
fl->c.flc_flags |= FL_ACCESS;
status = do_vfs_lock(fl);
fl->c.flc_flags = flags; if (status < 0) goto out;
nlmclnt_prepare_block(&block, host, fl);
again: /* * Initialise resp->status to a valid non-zero value, * since 0 == nlm_lck_granted
*/
resp->status = nlm_lck_blocked;
/* * A GRANTED callback can come at any time -- even before the reply * to the LOCK request arrives, so we queue the wait before * requesting the lock.
*/
nlmclnt_queue_block(&block); for (;;) { /* Reboot protection */
fl->fl_u.nfs_fl.state = host->h_state;
status = nlmclnt_call(cred, req, NLMPROC_LOCK); if (status < 0) break; /* Did a reclaimer thread notify us of a server reboot? */ if (resp->status == nlm_lck_denied_grace_period) continue; if (resp->status != nlm_lck_blocked) break; /* Wait on an NLM blocking lock */
status = nlmclnt_wait(&block, req, NLMCLNT_POLL_TIMEOUT); if (status < 0) break; if (block.b_status != nlm_lck_blocked) break;
}
b_status = nlmclnt_dequeue_block(&block); if (resp->status == nlm_lck_blocked)
resp->status = b_status;
/* if we were interrupted while blocking, then cancel the lock request * and exit
*/ if (resp->status == nlm_lck_blocked) { if (!req->a_args.block) goto out_unlock; if (nlmclnt_cancel(host, req->a_args.block, fl) == 0) goto out;
}
if (resp->status == nlm_granted) {
down_read(&host->h_rwsem); /* Check whether or not the server has rebooted */ if (fl->fl_u.nfs_fl.state != host->h_state) {
up_read(&host->h_rwsem); goto again;
} /* Ensure the resulting lock will get added to granted list */
fl->c.flc_flags |= FL_SLEEP; if (do_vfs_lock(fl) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
up_read(&host->h_rwsem);
fl->c.flc_flags = flags;
status = 0;
} if (status < 0) goto out_unlock; /* * EAGAIN doesn't make sense for sleeping locks, and in some * cases NLM_LCK_DENIED is returned for a permanent error. So * turn it into an ENOLCK.
*/ if (resp->status == nlm_lck_denied && (flags & FL_SLEEP))
status = -ENOLCK; else
status = nlm_stat_to_errno(resp->status);
out:
trace_nlmclnt_lock(&req->a_args.lock,
(conststruct sockaddr *)&req->a_host->h_addr,
req->a_host->h_addrlen, req->a_res.status);
nlmclnt_release_call(req); return status;
out_unlock: /* Fatal error: ensure that we remove the lock altogether */
trace_nlmclnt_lock(&req->a_args.lock,
(conststruct sockaddr *)&req->a_host->h_addr,
req->a_host->h_addrlen, req->a_res.status);
dprintk("lockd: lock attempt ended in fatal error.\n" " Attempting to unlock.\n");
type = fl->c.flc_type;
fl->c.flc_type = F_UNLCK;
down_read(&host->h_rwsem);
do_vfs_lock(fl);
up_read(&host->h_rwsem);
fl->c.flc_type = type;
fl->c.flc_flags = flags;
nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops); return status;
}
/* * RECLAIM: Try to reclaim a lock
*/ int
nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl, struct nlm_rqst *req)
{ int status;
/* Set up the argument struct */
nlmclnt_setlockargs(req, fl);
req->a_args.reclaim = 1;
status = nlmclnt_call(nfs_file_cred(fl->c.flc_file), req,
NLMPROC_LOCK); if (status >= 0 && req->a_res.status == nlm_granted) return 0;
printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d " "(errno %d, status %d)\n",
fl->c.flc_pid,
status, ntohl(req->a_res.status));
/* * FIXME: This is a serious failure. We can * * a. Ignore the problem * b. Send the owning process some signal (Linux doesn't have * SIGLOST, though...) * c. Retry the operation * * Until someone comes up with a simple implementation * for b or c, I'll choose option a.
*/
/* * Note: the server is supposed to either grant us the unlock * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either * case, we want to unlock.
*/
fl->c.flc_flags |= FL_EXISTS;
down_read(&host->h_rwsem);
status = do_vfs_lock(fl);
up_read(&host->h_rwsem);
fl->c.flc_flags = flags; if (status == -ENOENT) {
status = 0; goto out;
}
refcount_inc(&req->a_count);
status = nlmclnt_async_call(nfs_file_cred(fl->c.flc_file), req,
NLMPROC_UNLOCK, &nlmclnt_unlock_ops); if (status < 0) goto out;
if (resp->status == nlm_granted) goto out;
if (resp->status != nlm_lck_denied_nolocks)
printk("lockd: unexpected unlock status: %d\n",
ntohl(resp->status)); /* What to do now? I'm out of my depth... */
status = -ENOLCK;
out:
trace_nlmclnt_unlock(&req->a_args.lock,
(conststruct sockaddr *)&req->a_host->h_addr,
req->a_host->h_addrlen, req->a_res.status);
nlmclnt_release_call(req); return status;
}
/* * Cancel a blocked lock request. * We always use an async RPC call for this in order not to hang a * process that has been Ctrl-C'ed.
*/ staticint nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl)
{ struct nlm_rqst *req; int status;
dprintk("lockd: blocking lock attempt was interrupted by a signal.\n" " Attempting to cancel lock.\n");
req = nlm_alloc_call(host); if (!req) return -ENOMEM;
req->a_flags = RPC_TASK_ASYNC;
switch (status) { case NLM_LCK_GRANTED: case NLM_LCK_DENIED_GRACE_PERIOD: case NLM_LCK_DENIED: /* Everything's good */ break; case NLM_LCK_DENIED_NOLOCKS:
dprintk("lockd: CANCEL failed (server has no locks)\n"); goto retry_cancel; default:
printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n",
status);
}
die: return;
retry_cancel: /* Don't ever retry more than 3 times */ if (req->a_retries++ >= NLMCLNT_MAX_RETRIES) goto die;
nlm_rebind_host(req->a_host);
rpc_restart_call(task);
rpc_delay(task, 30 * HZ);
}
/* * Convert an NLM status code to a generic kernel errno
*/ staticint
nlm_stat_to_errno(__be32 status)
{ switch(ntohl(status)) { case NLM_LCK_GRANTED: return 0; case NLM_LCK_DENIED: return -EAGAIN; case NLM_LCK_DENIED_NOLOCKS: case NLM_LCK_DENIED_GRACE_PERIOD: return -ENOLCK; case NLM_LCK_BLOCKED:
printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n"); return -ENOLCK; #ifdef CONFIG_LOCKD_V4 case NLM_DEADLCK: return -EDEADLK; case NLM_ROFS: return -EROFS; case NLM_STALE_FH: return -ESTALE; case NLM_FBIG: return -EOVERFLOW; case NLM_FAILED: return -ENOLCK; #endif
}
printk(KERN_NOTICE "lockd: unexpected server status %d\n",
ntohl(status)); return -ENOLCK;
}
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.