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

📄 clntproc.c

📁 Linux中关于远程文件锁定的支持的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	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_end;			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 rpc_cred *cred = nfs_file_cred(fl->fl_file);	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;	unsigned char fl_type;	int status = -ENOLCK;	if (nsm_monitor(host) < 0)		goto out;	fl->fl_flags |= FL_ACCESS;	status = do_vfs_lock(fl);	fl->fl_flags = fl_flags;	if (status < 0)		goto out;	block = nlmclnt_prepare_block(host, fl);again:	/*	 * Initialise resp->status to a valid non-zero value,	 * since 0 == nlm_lck_granted	 */	resp->status = nlm_lck_blocked;	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_block(block, req, NLMCLNT_POLL_TIMEOUT);		if (status < 0)			break;		if (resp->status != nlm_lck_blocked)			break;	}	/* 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_unblock;	}	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_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->fl_flags = fl_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 && (fl_flags & FL_SLEEP))		status = -ENOLCK;	else		status = nlm_stat_to_errno(resp->status);out_unblock:	nlmclnt_finish_block(block);out:	nlm_release_call(req);	return status;out_unlock:	/* Fatal error: ensure that we remove the lock altogether */	dprintk("lockd: lock attempt ended in fatal error.\n"		"       Attempting to unlock.\n");	nlmclnt_finish_block(block);	fl_type = fl->fl_type;	fl->fl_type = F_UNLCK;	down_read(&host->h_rwsem);	do_vfs_lock(fl);	up_read(&host->h_rwsem);	fl->fl_type = fl_type;	fl->fl_flags = fl_flags;	nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);	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;	status = nlmclnt_call(nfs_file_cred(fl->fl_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->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;	unsigned char fl_flags = fl->fl_flags;	/*	 * 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);	status = do_vfs_lock(fl);	up_read(&host->h_rwsem);	fl->fl_flags = fl_flags;	if (status == -ENOENT) {		status = 0;		goto out;	}	atomic_inc(&req->a_count);	status = nlmclnt_async_call(nfs_file_cred(fl->fl_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", 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:	lock_kernel();	nlm_rebind_host(req->a_host);	unlock_kernel(); 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;	int status;	dprintk("lockd: blocking lock attempt was interrupted by a signal.\n"		"       Attempting to cancel lock.\n");	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;	atomic_inc(&req->a_count);	status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req,			NLMPROC_CANCEL, &nlmclnt_cancel_ops);	if (status == 0 && req->a_res.status == nlm_lck_denied)		status = -ENOLCK;	nlm_release_call(req);	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;	lock_kernel();	nlm_rebind_host(req->a_host);	unlock_kernel();	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 + -