Beispiel #1
0
/* The use of tmp_list is necessary because pnfs_curr_ld->free_lseg
 * could sleep, so must be called outside of the lock.
 * Returns 1 if object was removed, otherwise return 0.
 */
static int
put_lseg_locked(struct pnfs_layout_segment *lseg,
		struct list_head *tmp_list)
{
	dprintk("%s: lseg %p ref %d valid %d\n", __func__, lseg,
		atomic_read(&lseg->pls_refcount),
		test_bit(NFS_LSEG_VALID, &lseg->pls_flags));
	if (atomic_dec_and_test(&lseg->pls_refcount)) {
		struct inode *ino = lseg->pls_layout->plh_inode;

		BUG_ON(test_bit(NFS_LSEG_VALID, &lseg->pls_flags));
		list_del(&lseg->pls_list);
		if (list_empty(&lseg->pls_layout->plh_segs)) {
			struct nfs_client *clp;

			clp = NFS_SERVER(ino)->nfs_client;
			spin_lock(&clp->cl_lock);
			/* List does not take a reference, so no need for put here */
			list_del_init(&lseg->pls_layout->plh_layouts);
			spin_unlock(&clp->cl_lock);
			clear_bit(NFS_LAYOUT_BULK_RECALL, &lseg->pls_layout->plh_flags);
		}
		rpc_wake_up(&NFS_SERVER(ino)->roc_rpcwaitq);
		list_add(&lseg->pls_list, tmp_list);
		return 1;
	}
	return 0;
}
Beispiel #2
0
static void
put_lseg_common(struct pnfs_layout_segment *lseg)
{
	struct inode *inode = lseg->pls_layout->plh_inode;

	WARN_ON(test_bit(NFS_LSEG_VALID, &lseg->pls_flags));
	list_del_init(&lseg->pls_list);
	if (list_empty(&lseg->pls_layout->plh_segs)) {
		set_bit(NFS_LAYOUT_DESTROYED, &lseg->pls_layout->plh_flags);
		/* Matched by initial refcount set in alloc_init_layout_hdr */
		put_layout_hdr_locked(lseg->pls_layout);
	}
	rpc_wake_up(&NFS_SERVER(inode)->roc_rpcwaitq);
}
Beispiel #3
0
static void
pnfs_layout_remove_lseg(struct pnfs_layout_hdr *lo,
		struct pnfs_layout_segment *lseg)
{
	struct inode *inode = lo->plh_inode;

	WARN_ON(test_bit(NFS_LSEG_VALID, &lseg->pls_flags));
	list_del_init(&lseg->pls_list);
	/* Matched by pnfs_get_layout_hdr in pnfs_layout_insert_lseg */
	atomic_dec(&lo->plh_refcount);
	if (list_empty(&lo->plh_segs))
		clear_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
	rpc_wake_up(&NFS_SERVER(inode)->roc_rpcwaitq);
}
/*
 * Obtain the port for a given RPC service on a given host. This one can
 * be called for an ongoing RPC request.
 */
void
rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt)
{
	struct rpc_portmap *map = clnt->cl_pmap;
	struct sockaddr_in *sap = &clnt->cl_xprt->addr;
	struct rpc_message msg = {
		.rpc_proc	= &pmap_procedures[PMAP_GETPORT],
		.rpc_argp	= map,
		.rpc_resp	= &clnt->cl_port,
		.rpc_cred	= NULL
	};
	struct rpc_clnt	*pmap_clnt;
	struct rpc_task	*child;

	dprintk("RPC: %4d rpc_getport(%s, %d, %d, %d)\n",
			task->tk_pid, clnt->cl_server,
			map->pm_prog, map->pm_vers, map->pm_prot);

	/* Autobind on cloned rpc clients is discouraged */
	BUG_ON(clnt->cl_parent != clnt);

	spin_lock(&pmap_lock);
	if (map->pm_binding) {
		rpc_sleep_on(&map->pm_bindwait, task, NULL, NULL);
		spin_unlock(&pmap_lock);
		return;
	}
	map->pm_binding = 1;
	spin_unlock(&pmap_lock);

	pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot, 0);
	if (IS_ERR(pmap_clnt)) {
		task->tk_status = PTR_ERR(pmap_clnt);
		goto bailout;
	}
	task->tk_status = 0;

	/*
	 * Note: rpc_new_child will release client after a failure.
	 */
	if (!(child = rpc_new_child(pmap_clnt, task)))
		goto bailout;

	/* Setup the call info struct */
	rpc_call_setup(child, &msg, 0);

	/* ... and run the child task */
	rpc_run_child(task, child, pmap_getport_done);
	return;

bailout:
	spin_lock(&pmap_lock);
	map->pm_binding = 0;
	rpc_wake_up(&map->pm_bindwait);
	spin_unlock(&pmap_lock);
	rpc_exit(task, -EIO);
}

#ifdef CONFIG_ROOT_NFS
int
rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot)
{
	struct rpc_portmap map = {
		.pm_prog	= prog,
		.pm_vers	= vers,
		.pm_prot	= prot,
		.pm_port	= 0
	};
	struct rpc_clnt	*pmap_clnt;
	char		hostname[32];
	int		status;

	dprintk("RPC:      rpc_getport_external(%u.%u.%u.%u, %d, %d, %d)\n",
			NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);

	sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr));
	pmap_clnt = pmap_create(hostname, sin, prot, 0);
	if (IS_ERR(pmap_clnt))
		return PTR_ERR(pmap_clnt);

	/* Setup the call info struct */
	status = rpc_call(pmap_clnt, PMAP_GETPORT, &map, &map.pm_port, 0);

	if (status >= 0) {
		if (map.pm_port != 0)
			return map.pm_port;
		status = -EACCES;
	}
	return status;
}
#endif

static void
pmap_getport_done(struct rpc_task *task)
{
	struct rpc_clnt	*clnt = task->tk_client;
	struct rpc_xprt *xprt = task->tk_xprt;
	struct rpc_portmap *map = clnt->cl_pmap;

	dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n",
			task->tk_pid, task->tk_status, clnt->cl_port);

	xprt->ops->set_port(xprt, 0);
	if (task->tk_status < 0) {
		/* Make the calling task exit with an error */
		task->tk_action = rpc_exit_task;
	} else if (clnt->cl_port == 0) {
		/* Program not registered */
		rpc_exit(task, -EACCES);
	} else {
		xprt->ops->set_port(xprt, clnt->cl_port);
		clnt->cl_port = htons(clnt->cl_port);
	}
	spin_lock(&pmap_lock);
	map->pm_binding = 0;
	rpc_wake_up(&map->pm_bindwait);
	spin_unlock(&pmap_lock);
}

/*
 * Set or unset a port registration with the local portmapper.
 * port == 0 means unregister, port != 0 means register.
 */
int
rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
{
	struct sockaddr_in	sin;
	struct rpc_portmap	map;
	struct rpc_clnt		*pmap_clnt;
	int error = 0;

	dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n",
			prog, vers, prot, port);

	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP, 1);
	if (IS_ERR(pmap_clnt)) {
		error = PTR_ERR(pmap_clnt);
		dprintk("RPC: couldn't create pmap client. Error = %d\n", error);
		return error;
	}

	map.pm_prog = prog;
	map.pm_vers = vers;
	map.pm_prot = prot;
	map.pm_port = port;

	error = rpc_call(pmap_clnt, port? PMAP_SET : PMAP_UNSET,
					&map, okay, 0);

	if (error < 0) {
		printk(KERN_WARNING
			"RPC: failed to contact portmap (errno %d).\n",
			error);
	}
	dprintk("RPC: registration status %d/%d\n", error, *okay);

	/* Client deleted automatically because cl_oneshot == 1 */
	return error;
}

static struct rpc_clnt *
pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto, int privileged)
{
	struct rpc_xprt	*xprt;
	struct rpc_clnt	*clnt;

	/* printk("pmap: create xprt\n"); */
	xprt = xprt_create_proto(proto, srvaddr, NULL);
	if (IS_ERR(xprt))
		return (struct rpc_clnt *)xprt;
	xprt->ops->set_port(xprt, RPC_PMAP_PORT);
	if (!privileged)
		xprt->resvport = 0;

	/* printk("pmap: create clnt\n"); */
	clnt = rpc_new_client(xprt, hostname,
				&pmap_program, RPC_PMAP_VERSION,
				RPC_AUTH_UNIX);
	if (!IS_ERR(clnt)) {
		clnt->cl_softrtry = 1;
		clnt->cl_oneshot  = 1;
	}
	return clnt;
}

/*
 * XDR encode/decode functions for PMAP
 */
static int
xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map)
{
	dprintk("RPC: xdr_encode_mapping(%d, %d, %d, %d)\n",
		map->pm_prog, map->pm_vers, map->pm_prot, map->pm_port);
	*p++ = htonl(map->pm_prog);
	*p++ = htonl(map->pm_vers);
	*p++ = htonl(map->pm_prot);
	*p++ = htonl(map->pm_port);

	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
	return 0;
}

static int
xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp)
{
	*portp = (unsigned short) ntohl(*p++);
	return 0;
}