⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 clntproc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
static intnlmclnt_test(struct nlm_rqst *req, struct file_lock *fl){	int	status;	status = nlmclnt_call(req, NLMPROC_TEST);	if (status < 0)		goto out;	switch (req->a_res.status) {		case nlm_granted:			fl->fl_type = F_UNLCK;			break;		case nlm_lck_denied:			/*			 * Report the conflicting lock back to the application.			 */			fl->fl_start = req->a_res.lock.fl.fl_start;			fl->fl_end = req->a_res.lock.fl.fl_start;			fl->fl_type = req->a_res.lock.fl.fl_type;			fl->fl_pid = 0;			break;		default:			status = nlm_stat_to_errno(req->a_res.status);	}out:	nlm_release_call(req);	return status;}static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl){	new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state;	new->fl_u.nfs_fl.owner = nlm_get_lockowner(fl->fl_u.nfs_fl.owner);	list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted);}static void nlmclnt_locks_release_private(struct file_lock *fl){	list_del(&fl->fl_u.nfs_fl.list);	nlm_put_lockowner(fl->fl_u.nfs_fl.owner);}static struct file_lock_operations nlmclnt_lock_ops = {	.fl_copy_lock = nlmclnt_locks_copy_lock,	.fl_release_private = nlmclnt_locks_release_private,};static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host){	BUG_ON(fl->fl_ops != NULL);	fl->fl_u.nfs_fl.state = 0;	fl->fl_u.nfs_fl.owner = nlm_find_lockowner(host, fl->fl_owner);	INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list);	fl->fl_ops = &nlmclnt_lock_ops;}static int do_vfs_lock(struct file_lock *fl){	int res = 0;	switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {		case FL_POSIX:			res = posix_lock_file_wait(fl->fl_file, fl);			break;		case FL_FLOCK:			res = flock_lock_file_wait(fl->fl_file, fl);			break;		default:			BUG();	}	return res;}/* * 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. */static intnlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl){	struct nlm_host	*host = req->a_host;	struct nlm_res	*resp = &req->a_res;	struct nlm_wait *block = NULL;	unsigned char fl_flags = fl->fl_flags;	int status = -ENOLCK;	if (nsm_monitor(host) < 0) {		printk(KERN_NOTICE "lockd: failed to monitor %s\n",					host->h_name);		goto out;	}	fl->fl_flags |= FL_ACCESS;	status = do_vfs_lock(fl);	if (status < 0)		goto out;	block = nlmclnt_prepare_block(host, fl);again:	for(;;) {		/* Reboot protection */		fl->fl_u.nfs_fl.state = host->h_state;		status = nlmclnt_call(req, NLMPROC_LOCK);		if (status < 0)			goto out_unblock;		if (!req->a_args.block)			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_block(block, req, NLMCLNT_POLL_TIMEOUT);		/* if we were interrupted. Send a CANCEL request to the server		 * and exit		 */		if (status < 0)			goto out_unblock;		if (resp->status != nlm_lck_blocked)			break;	}	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->fl_flags = fl_flags | FL_SLEEP;		if (do_vfs_lock(fl) < 0)			printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);		up_read(&host->h_rwsem);	}	status = nlm_stat_to_errno(resp->status);out_unblock:	nlmclnt_finish_block(block);	/* Cancel the blocked request if it is still pending */	if (resp->status == nlm_lck_blocked)		nlmclnt_cancel(host, req->a_args.block, fl);out:	nlm_release_call(req);	fl->fl_flags = fl_flags;	return status;}/* * RECLAIM: Try to reclaim a lock */intnlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl){	struct nlm_rqst reqst, *req;	int		status;	req = &reqst;	memset(req, 0, sizeof(*req));	locks_init_lock(&req->a_args.lock.fl);	locks_init_lock(&req->a_res.lock.fl);	req->a_host  = host;	req->a_flags = 0;	/* Set up the argument struct */	nlmclnt_setlockargs(req, fl);	req->a_args.reclaim = 1;	if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 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->fl_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.	 */	return -ENOLCK;}/* * UNLOCK: remove an existing lock */static intnlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl){	struct nlm_host	*host = req->a_host;	struct nlm_res	*resp = &req->a_res;	int status = 0;	/*	 * 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->fl_flags |= FL_EXISTS;	down_read(&host->h_rwsem);	if (do_vfs_lock(fl) == -ENOENT) {		up_read(&host->h_rwsem);		goto out;	}	up_read(&host->h_rwsem);	if (req->a_flags & RPC_TASK_ASYNC)		return nlm_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);	status = nlmclnt_call(req, NLMPROC_UNLOCK);	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", resp->status);	/* What to do now? I'm out of my depth... */	status = -ENOLCK;out:	nlm_release_call(req);	return status;}static void nlmclnt_unlock_callback(struct rpc_task *task, void *data){	struct nlm_rqst	*req = data;	u32 status = ntohl(req->a_res.status);	if (RPC_ASSASSINATED(task))		goto die;	if (task->tk_status < 0) {		dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);		goto retry_rebind;	}	if (status == NLM_LCK_DENIED_GRACE_PERIOD) {		rpc_delay(task, NLMCLNT_GRACE_WAIT);		goto retry_unlock;	}	if (status != NLM_LCK_GRANTED)		printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status);die:	return; retry_rebind:	nlm_rebind_host(req->a_host); retry_unlock:	rpc_restart_call(task);}static const struct rpc_call_ops nlmclnt_unlock_ops = {	.rpc_call_done = nlmclnt_unlock_callback,	.rpc_release = nlmclnt_rpc_release,};/* * 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. */static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl){	struct nlm_rqst	*req;	unsigned long	flags;	sigset_t	oldset;	int		status;	/* Block all signals while setting up call */	spin_lock_irqsave(&current->sighand->siglock, flags);	oldset = current->blocked;	sigfillset(&current->blocked);	recalc_sigpending();	spin_unlock_irqrestore(&current->sighand->siglock, flags);	req = nlm_alloc_call(nlm_get_host(host));	if (!req)		return -ENOMEM;	req->a_flags = RPC_TASK_ASYNC;	nlmclnt_setlockargs(req, fl);	req->a_args.block = block;	status = nlm_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops);	spin_lock_irqsave(&current->sighand->siglock, flags);	current->blocked = oldset;	recalc_sigpending();	spin_unlock_irqrestore(&current->sighand->siglock, flags);	return status;}static void nlmclnt_cancel_callback(struct rpc_task *task, void *data){	struct nlm_rqst	*req = data;	u32 status = ntohl(req->a_res.status);	if (RPC_ASSASSINATED(task))		goto die;	if (task->tk_status < 0) {		dprintk("lockd: CANCEL call error %d, retrying.\n",					task->tk_status);		goto retry_cancel;	}	dprintk("lockd: cancel status %u (task %u)\n",			status, task->tk_pid);	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);}static const struct rpc_call_ops nlmclnt_cancel_ops = {	.rpc_call_done = nlmclnt_cancel_callback,	.rpc_release = nlmclnt_rpc_release,};/* * Convert an NLM status code to a generic kernel errno */static intnlm_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", status);	return -ENOLCK;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -