Ejemplo n.º 1
0
/*
 * Reclaim all locks on server host. We do this by spawning a separate
 * reclaimer thread.
 * FIXME: should bump MOD_USE_COUNT while reclaiming
 */
void
nlmclnt_recovery(struct nlm_host *host, u32 newstate)
{
	if (!host->h_reclaiming++) {
		if (host->h_nsmstate == newstate)
			return;
		printk(KERN_WARNING
			"lockd: Uh-oh! Interfering reclaims for host %s",
			host->h_name);
		host->h_monitored = 0;
		host->h_nsmstate = newstate;
		host->h_state++;
		nlm_release_host(host);
	} else {
		host->h_monitored = 0;
		host->h_nsmstate = newstate;
		host->h_state++;
		nlm_get_host(host);
		kernel_thread(reclaimer, host, 0);
	}
}
Ejemplo n.º 2
0
struct nlm_host *
nlm_find_client(void)
{
	/* find a nlm_host for a client for which h_killed == 0.
	 * and return it
	 */
	int hash;
	down(&nlm_host_sema);
	for (hash = 0 ; hash < NLM_HOST_NRHASH; hash++) {
		struct nlm_host *host, **hp;
		for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) {
			if (host->h_server &&
			    host->h_killed == 0) {
				nlm_get_host(host);
				up(&nlm_host_sema);
				return host;
			}
		}
	}
	up(&nlm_host_sema);
	return NULL;
}
Ejemplo n.º 3
0
/*
 * Common host lookup routine for server & client
 */
struct nlm_host *
nlm_lookup_host(int server, struct sockaddr_in *sin,
					int proto, int version)
{
	struct nlm_host	*host, **hp;
	u32		addr;
	int		hash;

	dprintk("lockd: nlm_lookup_host(%08x, p=%d, v=%d)\n",
			(unsigned)(sin? ntohl(sin->sin_addr.s_addr) : 0), proto, version);

	hash = NLM_ADDRHASH(sin->sin_addr.s_addr);

	/* Lock hash table */
	down(&nlm_host_sema);

	if (time_after_eq(jiffies, next_gc))
		nlm_gc_hosts();

	for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) {
		if (host->h_proto != proto)
			continue;
		if (host->h_version != version)
			continue;
		if (host->h_server != server)
			continue;

		if (nlm_cmp_addr(&host->h_addr, sin)) {
			if (hp != nlm_hosts + hash) {
				*hp = host->h_next;
				host->h_next = nlm_hosts[hash];
				nlm_hosts[hash] = host;
			}
			nlm_get_host(host);
			up(&nlm_host_sema);
			return host;
		}
	}

	/* Ooops, no host found, create it */
	dprintk("lockd: creating host entry\n");

	if (!(host = (struct nlm_host *) kmalloc(sizeof(*host), GFP_KERNEL)))
		goto nohost;
	memset(host, 0, sizeof(*host));

	addr = sin->sin_addr.s_addr;
	sprintf(host->h_name, "%u.%u.%u.%u", NIPQUAD(addr));

	host->h_addr       = *sin;
	host->h_addr.sin_port = 0;	/* ouch! */
	host->h_version    = version;
	host->h_proto      = proto;
	host->h_authflavor = RPC_AUTH_UNIX;
	host->h_rpcclnt    = NULL;
	init_MUTEX(&host->h_sema);
	host->h_nextrebind = jiffies + NLM_HOST_REBIND;
	host->h_expires    = jiffies + NLM_HOST_EXPIRE;
	atomic_set(&host->h_count, 1);
	init_waitqueue_head(&host->h_gracewait);
	host->h_state      = 0;			/* pseudo NSM state */
	host->h_nsmstate   = 0;			/* real NSM state */
	host->h_server	   = server;
	host->h_next       = nlm_hosts[hash];
	nlm_hosts[hash]    = host;
	INIT_LIST_HEAD(&host->h_lockowners);
	spin_lock_init(&host->h_lock);

	if (++nrhosts > NLM_HOST_MAX)
		next_gc = 0;

nohost:
	up(&nlm_host_sema);
	return host;
}
Ejemplo n.º 4
0
/*
 * Try to claim a lock that was previously blocked.
 *
 * Note that we use both the RPC_GRANTED_MSG call _and_ an async
 * RPC thread when notifying the client. This seems like overkill...
 * Here's why:
 *  -	we don't want to use a synchronous RPC thread, otherwise
 *	we might find ourselves hanging on a dead portmapper.
 *  -	Some lockd implementations (e.g. HP) don't react to
 *	RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls.
 */
static void
nlmsvc_grant_blocked(struct nlm_block *block)
{
	struct nlm_file		*file = block->b_file;
	struct nlm_lock		*lock = &block->b_call.a_args.lock;
	struct file_lock	*conflock;
	int			error;

	dprintk("lockd: grant blocked lock %p\n", block);

	/* First thing is lock the file */
	down(&file->f_sema);

	/* Unlink block request from list */
	nlmsvc_remove_block(block);

	/* If b_granted is true this means we've been here before.
	 * Just retry the grant callback, possibly refreshing the RPC
	 * binding */
	if (block->b_granted) {
		nlm_rebind_host(block->b_call.a_host);
		goto callback;
	}

	/* Try the lock operation again */
	if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
		/* Bummer, we blocked again */
		dprintk("lockd: lock still blocked\n");
		nlmsvc_insert_block(block, NLM_NEVER);
		posix_block_lock(conflock, &lock->fl);
		up(&file->f_sema);
		return;
	}

	/* Alright, no conflicting lock. Now lock it for real. If the
	 * following yields an error, this is most probably due to low
	 * memory. Retry the lock in a few seconds.
	 */
	if ((error = posix_lock_file(&file->f_file, &lock->fl, 0)) < 0) {
		printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
				-error, __FUNCTION__);
		nlmsvc_insert_block(block, 10 * HZ);
		up(&file->f_sema);
		return;
	}

callback:
	/* Lock was granted by VFS. */
	dprintk("lockd: GRANTing blocked lock.\n");
	block->b_granted = 1;
	block->b_incall  = 1;

	/* Schedule next grant callback in 30 seconds */
	nlmsvc_insert_block(block, 30 * HZ);

	/* Call the client */
	nlm_get_host(block->b_call.a_host);
	if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
						nlmsvc_grant_callback) < 0)
		nlm_release_host(block->b_call.a_host);
	up(&file->f_sema);
}
static
void nlmclnt_insert_lock_callback(struct file_lock *fl)
{
	nlm_get_host(fl->fl_u.nfs_fl.host);
}
/*
 * Generic NLM call, async version.
 */
int
nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
{
	struct nlm_host	*host = req->a_host;
	struct rpc_clnt	*clnt;
	struct rpc_message msg = {
		.rpc_argp	= &req->a_args,
		.rpc_resp	= &req->a_res,
	};
	int		status;

	dprintk("lockd: call procedure %d on %s (async)\n",
			(int)proc, host->h_name);

	/* 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];

        /* bootstrap and kick off the async RPC call */
        status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);

	return status;
}

int
nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
{
	struct nlm_host	*host = req->a_host;
	struct rpc_clnt	*clnt;
	struct nlm_args	*argp = &req->a_args;
	struct nlm_res	*resp = &req->a_res;
	struct file	*file = argp->lock.fl.fl_file;
	struct rpc_message msg = {
		.rpc_argp	= argp,
		.rpc_resp	= resp,
	};
	int		status;

	dprintk("lockd: call procedure %d on %s (async)\n",
			(int)proc, host->h_name);

	/* 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];

        /* bootstrap and kick off the async RPC call */
	if (file)
		msg.rpc_cred = nfs_file_cred(file);
	/* Increment host refcount */
	nlm_get_host(host);
        status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
	if (status < 0)
		nlm_release_host(host);
	return status;
}

/*
 * TEST for the presence of a conflicting lock
 */
static int
nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
{
	int	status;

	if ((status = nlmclnt_call(req, NLMPROC_TEST)) < 0)
		return status;

	status = req->a_res.status;
	if (status == NLM_LCK_GRANTED) {
		fl->fl_type = F_UNLCK;
	} if (status == NLM_LCK_DENIED) {
		/*
		 * Report the conflicting lock back to the application.
		 * FIXME: Is it OK to report the pid back as well?
		 */
		locks_copy_lock(fl, &req->a_res.lock.fl);
		/* fl->fl_pid = 0; */
	} else {
		return nlm_stat_to_errno(req->a_res.status);
	}

	return 0;
}