/* $FreeBSD$ */
/* $Id: rpc_dev.c,v 1.8 2004/06/14 19:38:56 marius Exp $ */

/*
 * copyright (c) 2003
 * the regents of the university of michigan
 * all rights reserved
 * 
 * Weston Andros Adamson <muzzle@citi.umich.edu>
 * 
 * permission is granted to use, copy, create derivative works and redistribute
 * this software and such derivative works for any purpose, so long as the name
 * of the university of michigan is not used in any advertising or publicity
 * pertaining to the use or distribution of this software without specific,
 * written prior authorization.  if the above copyright notice or any other
 * identification of the university of michigan is included in any copy of any
 * portion of this software, then the disclaimer below must also be included.
 * 
 * this software is provided as is, without representation from the university
 * of michigan as to its fitness for any purpose, and without warranty by the
 * university of michigan of any kind, either express or implied, including
 * without limitation the implied warranties of merchantability and fitness for
 * a particular purpose. the regents of the university of michigan shall not be
 * liable for any damages, including special, indirect, incidental, or
 * consequential damages, with respect to any claim arising out of or in
 * connection with the use of the software, even if it has been or is hereafter
 * advised of the possibility of such damages.
 */

#include <sys/param.h>
#include <sys/conf.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/poll.h>
#include <sys/selinfo.h>

#ifndef __APPLE__
#include <sys/mutex.h>
#endif

#if defined(__APPLE__)
#include <miscfs/devfs/devfs.h>
#endif

#include <sys/stat.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/wait.h>
#include <sys/signalvar.h>

#include <nfs4client/nfs4_glue.h>


/* XXX */
#ifdef __APPLE__
#include <rpc/rpc_dev.h>
#else
#include <rpcx/rpc_dev.h>
#endif

#ifdef RPCDEVVERBOSE
#define RPCDEV_DEBUG(X...) do { \
  printf("%s> ", __FUNCTION__ ); \
  printf(X); \
  printf(".\n"); \
} while(0)
#else
#define RPCDEV_DEBUG(X...)
#endif

#if defined(__FreeBSD__)
MALLOC_DEFINE(M_RPCDEV, "RPC dev", "RPC device");
#else
#define M_RPCDEV M_TEMP

#endif

#define rpcdev_dev2type(X) (minor(X)-1)
#define rpcdev_type2devminor(X) ((X)+1)

struct rpcdev_upcall {
  	uint32_t  up_type;
  	uint32_t  up_xid;

	/* request */
	caddr_t   up_req;
	int       up_reqlen;

	/* reply */
	caddr_t   up_rep;
	size_t *  up_replen;	

	int up_error;		/* non-zero if an error occured */

	short     up_sleeponreply;

	TAILQ_ENTRY(rpcdev_upcall) up_entry;
};

#define rpcdev_upcall_get(MP) MALLOC((MP), struct rpcdev_upcall *, sizeof(struct rpcdev_upcall), M_RPCDEV, M_WAITOK | M_ZERO)

#define rpcdev_upcall_put(MP) FREE((MP), M_RPCDEV)


static int rpcdev_xid = 0;
cmtx_t     rpcdev_xid_mtx;

struct rpcdev_svc {
	int                         svc_type;	/* also used for CDEV_MINOR */
	char                        *svc_name;


	/* queue of pending upcalls */
	TAILQ_HEAD(, rpcdev_upcall) svc_newq;
	cmtx_t                      svc_newq_mtx;

	/* queue of upcalls waiting for replys */
	TAILQ_HEAD(, rpcdev_upcall) svc_waitq;
	cmtx_t                      svc_waitq_mtx;

	cthread_t                  *svc_opener;
	dev_t                       svc_device;
#ifdef __APPLE__
	void *                      svc_devfsid;
#endif /* __APPLE__ */
	cmtx_t                      svc_opener_mtx;

	struct selinfo              svc_si;
};

static struct rpcdev_svc rpcdev_services[RPCDEV_MAX_TYPE + 1] = {
	{RPCDEV_TYPE_IDMAP, "idmap", }, 
	{RPCDEV_TYPE_RPCD, "rpcd", }
};

/* dev hooks */
static d_open_t  rpcdev_open;
static d_close_t rpcdev_close;
static d_ioctl_t rpcdev_ioctl;
#if defined(__APPLE__)
static d_select_t  rpcdev_poll;
#else
static d_poll_t  rpcdev_poll;
#endif

static struct cdevsw rpcdev_cdevsw = {
  	.d_version =    D_VERSION_00,
	.d_name =	"RPCDEV",
	.d_open =	rpcdev_open,
	.d_close =	rpcdev_close,
	.d_ioctl =	rpcdev_ioctl,
#if defined(__APPLE__)
	.d_select =	rpcdev_poll,
#else
	.d_poll =	rpcdev_poll,

#endif
};

static int rpcdev_reply(caddr_t);
static int rpcdev_request(caddr_t);
static int rpcdev_checkowner(dev_t, cthread_t *);

/* Userland requests a new operation to service */
/* XXX Add funnel code for APPLE */
static int 
rpcdev_request(caddr_t addr)
{
	struct rpcdev_upcall * u;
	struct rpcdev_msg * m = (struct rpcdev_msg *) addr;
	int error = 0;
	int type;

	RPCDEV_DEBUG("rpcdev: request in");

	type = m->msg_type;

	if (m->msg_vers != RPCDEV_VERSION || type > RPCDEV_MAX_TYPE || 
	    m->msg_data == NULL || m->msg_len == 0) {
		error = EINVAL;
		goto bad;
	}

	cmtx_lock(&rpcdev_services[type].svc_newq_mtx);

	if (TAILQ_EMPTY(&rpcdev_services[type].svc_newq)) {
		cmtx_unlock(&rpcdev_services[type].svc_newq_mtx);
		error = EAGAIN;
		goto bad;
	}

	u = TAILQ_FIRST(&rpcdev_services[type].svc_newq);
	TAILQ_REMOVE(&rpcdev_services[type].svc_newq, u, up_entry);

	cmtx_unlock(&rpcdev_services[type].svc_newq_mtx);

	/* pass the message to userland */
	if (m->msg_len < u->up_reqlen) {
		error = EINVAL;
		goto bad2;
	}

	m->msg_xid = u->up_xid;
	m->msg_error = 0;

	if ((error = copyout(u->up_req, m->msg_data, u->up_reqlen))) {
	  	RPCDEV_DEBUG("%s message error %d", 
		    rpcdev_services[type].svc_name, error);
		goto bad2;
	}

	m->msg_len = u->up_reqlen;

	cmtx_lock(&rpcdev_services[type].svc_waitq_mtx);
	TAILQ_INSERT_TAIL(&rpcdev_services[type].svc_waitq, u, up_entry);
	cmtx_unlock(&rpcdev_services[type].svc_waitq_mtx);

	selwakeup(&rpcdev_services[type].svc_si);

	RPCDEV_DEBUG("request out (success)");
	return (0);
bad2:
	/* put upcall back on the queue */
	cmtx_lock(&rpcdev_services[type].svc_newq_mtx);
	TAILQ_INSERT_HEAD(&rpcdev_services[type].svc_newq, u, up_entry);
	cmtx_unlock(&rpcdev_services[type].svc_newq_mtx);
bad:
	RPCDEV_DEBUG("request out (fail: %d)", error);
	/* return the error to userland */
	m->msg_error = error;
	return (error);
}

static int
rpcdev_reply(caddr_t addr)
{
	struct rpcdev_upcall * u = NULL;
	struct rpcdev_msg * m = (struct rpcdev_msg *) addr;
	int error = 0;
	int type;

	RPCDEV_DEBUG("rpcdev: reply in");

	if (m->msg_vers != RPCDEV_VERSION) {
	  	RPCDEV_DEBUG("version mismatch %x/%x", 
		    m->msg_vers, RPCDEV_VERSION);
		error = EINVAL;
		goto bad;
	}

	type = m->msg_type;

	if (type > RPCDEV_MAX_TYPE) {
		RPCDEV_DEBUG("rpcdev: unsupported message type %x", type);
		error = EINVAL;
		goto bad;
	}

	if (m->msg_len == 0) {
	  	RPCDEV_DEBUG("bad message length");
		error = EINVAL;
		goto bad;
	}
	  	
	/* match the reply with a request */
	cmtx_lock(&rpcdev_services[type].svc_waitq_mtx);
	TAILQ_FOREACH(u, &rpcdev_services[type].svc_waitq, up_entry) {
		if (m->msg_xid == u->up_xid) {
			if (m->msg_type == u->up_type)
		  		goto found;
			RPCDEV_DEBUG("rpcdev: op type mismatch!");
			break;
		}
	}
	cmtx_unlock(&rpcdev_services[type].svc_waitq_mtx);

	RPCDEV_DEBUG("rpcdev msg op: %d xid: %x not found.",
	    m->msg_type, m->msg_xid);

	error = EIO;
	goto bad;

found:
	RPCDEV_DEBUG("found");
	TAILQ_REMOVE(&rpcdev_services[type].svc_waitq, u, up_entry);
	cmtx_unlock(&rpcdev_services[type].svc_waitq_mtx);

	if (m->msg_error) {
		RPCDEV_DEBUG("%s message (xid: %x) returned error %d", 
		    rpcdev_services[type].svc_name, m->msg_xid, m->msg_error);
		error = m->msg_error;
		goto bad;
	}

	if (m->msg_len > *u->up_replen) {
	  	RPCDEV_DEBUG("%s message (xid: %x) replen too small",
		    rpcdev_services[type].svc_name, m->msg_xid);
		error = EINVAL;
		goto bad;
	}

	if ((error = copyin(m->msg_data, u->up_rep, m->msg_len))) {
		RPCDEV_DEBUG("%s message copyin error %d", 
		    rpcdev_services[type].svc_name, error);
		goto bad;
	}
	*u->up_replen = m->msg_len;
	u->up_error = 0;

	wakeup(u);

	if (u->up_sleeponreply) {
		mtx_lock(&Giant); /* XXX */
		tsleep(&rpcdev_services[type], PWAIT|PCATCH, "rpcdev", 0);
		mtx_unlock(&Giant);
	}

	RPCDEV_DEBUG("rpcdev: reply out (success)");

	return (0);
bad:
	u->up_error = error;
	wakeup(u);

	RPCDEV_DEBUG("rpcdev: reply out (fail: %d)", error);

	return (error);
}

void
rpcdev_wakeup(int type)
{
	wakeup(&rpcdev_services[type]);
}

void
rpcdev_init(void)
{
	int i;
#if defined(__APPLE__)
  	int major;
#endif

	RPCDEV_DEBUG("init");

  	RANDOM(rpcdev_xid);
	cmtx_init(&rpcdev_xid_mtx, "rpcdev xid");

	for (i=0; i <= RPCDEV_MAX_TYPE; i++) {
		RPCDEV_DEBUG("initializing device \"/dev/%s\"", 
		    rpcdev_services[i].svc_name);

		TAILQ_INIT(&rpcdev_services[i].svc_newq);	
		cmtx_init(&rpcdev_services[i].svc_newq_mtx, "rpcdev queue");

		TAILQ_INIT(&rpcdev_services[i].svc_waitq);	
		cmtx_init(&rpcdev_services[i].svc_waitq_mtx, "rpcdev queue");

		cmtx_init(&rpcdev_services[i].svc_opener_mtx, "rpcdev opener");
		rpcdev_services[i].svc_opener = NULL;
#if defined(__APPLE__)
		rpcdev_services[i].svc_devfsid = 0;
#endif

#if defined(__FreeBSD__)
		RPCDEV_DEBUG("making device %s", rpcdev_services[i].svc_name);
		rpcdev_services[i].svc_device = make_dev(&rpcdev_cdevsw, 
		    rpcdev_type2devminor(i), (uid_t) 0, (gid_t) 0, 
		    S_IRUSR | S_IWUSR, rpcdev_services[i].svc_name);
#elif defined(__APPLE__)
	//major = cdevsw_add(-1, &rpcdev_cdevsw);
	//rpcdevice = makedev(major, CDEV_MINOR);
	//rpcdevfsid = devfs_make_node(rpcdevice, DEVFS_CHAR, 0, 0, 0660, RPCDEV_NAME);
#else
#error no devfs hook in rpcdev_init
#endif
	}

	return;
}

void
rpcdev_uninit(void)
{
	int i;

	rpcdev_purge();

	cmtx_destroy(&rpcdev_xid_mtx);

	for (i=0; i <= RPCDEV_MAX_TYPE; i++) {
		cthread_t *td;

		selwakeup(&rpcdev_services[i].svc_si);

  		cmtx_destroy(&rpcdev_services[i].svc_newq_mtx);
  		cmtx_destroy(&rpcdev_services[i].svc_waitq_mtx);
  		cmtx_destroy(&rpcdev_services[i].svc_opener_mtx);

		if ((td = rpcdev_services[i].svc_opener) != NULL) {
			RPCDEV_DEBUG("type %s reader proc %d killed", 
			    rpcdev_services[i].svc_name,
			    cthread_proc(td)->p_pid);
#if defined(__FreeBSD__)
			PROC_LOCK(cthread_proc(td));
#endif
			psignal(cthread_proc(td), SIGKILL);
#if defined(__FreeBSD__)
			PROC_UNLOCK(cthread_proc(td));
#endif
		}

#if defined(__FreeBSD__)
		RPCDEV_DEBUG("destroying device /dev/%s - %x", 
		    rpcdev_services[i].svc_name,
		    (unsigned int)rpcdev_services[i].svc_device);
		destroy_dev(rpcdev_services[i].svc_device);
#elif defined(__APPLE__)
		//devfs_remove(rpcdevfsid);
#else
#error no devfs hook in rpcdev_uninit
#endif
	}
	return;
}

/* device interface functions */
static int
rpcdev_open(dev_t dev, int flags, int fmt, cthread_t *td)
{
	uint32_t type = rpcdev_dev2type(dev);

	if (type > RPCDEV_MAX_TYPE) {
		RPCDEV_DEBUG("bad minor number");
		return (ENODEV);
	}

	cmtx_lock(&rpcdev_services[type].svc_opener_mtx);

	/* XXX this is very redundant */
	if (dev != rpcdev_services[type].svc_device) {
		cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);

		RPCDEV_DEBUG("device mismatch");
		return (ENXIO);
	}

	if (rpcdev_services[type].svc_opener != NULL) {
		cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);

		RPCDEV_DEBUG("open failed! %s already attached to proc %d",
		    rpcdev_services[type].svc_name,
		    cthread_proc(rpcdev_services[type].svc_opener)->p_pid);
	  	return (EBUSY);
	}
	rpcdev_services[type].svc_opener = td;
	cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);

	RPCDEV_DEBUG("service %s opened by proc %d", 
	    rpcdev_services[type].svc_name,
	    (cthread_proc(td))->p_pid);
	return (0);
}

static int
rpcdev_close(dev_t dev, int flags, int fmt, cthread_t *td)
{
	struct rpcdev_upcall * u;
	int error = 0;
	int type;
#ifdef __APPLE__
	boolean_t funnel_state;

	funnel_state = thread_funnel_set(kernel_flock, TRUE);
#endif /* __APPLE__ */

	if ((error = rpcdev_checkowner(dev, td))) {
		RPCDEV_DEBUG("close owner error");
		goto out;
	}

	type = rpcdev_dev2type(dev);

	cmtx_lock(&rpcdev_services[type].svc_waitq_mtx);
	while (!TAILQ_EMPTY(&rpcdev_services[type].svc_waitq)) {
		u = TAILQ_FIRST(&rpcdev_services[type].svc_waitq);
		TAILQ_REMOVE(&rpcdev_services[type].svc_waitq, u, up_entry);
		u->up_error = EINTR;
		wakeup(u);
	}
	cmtx_unlock(&rpcdev_services[type].svc_waitq_mtx);

	cmtx_lock(&rpcdev_services[type].svc_opener_mtx);
	rpcdev_services[type].svc_opener = NULL;
	cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);

	RPCDEV_DEBUG("rpc_dev: proc %d unbound from service %s", 
	    cthread_proc(td)->p_pid, rpcdev_services[type].svc_name);

out:
#ifdef __APPLE__
	(void) thread_funnel_set(kernel_flock, funnel_state);
#endif /* __APPLE__ */
	return (error);
}

static int 
rpcdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, cthread_t *td)
{
  	int error;
	uint32_t type;

	if ((error = rpcdev_checkowner(dev, td))) {
		RPCDEV_DEBUG("ioctl: checkowner fails");
		return (error);
	}

	if (data == NULL) 
	  	return (EFAULT);

	type = rpcdev_dev2type(dev);

	if (type != (((struct rpcdev_msg *)data)->msg_type)) {
	  	RPCDEV_DEBUG("type mismatch");
		return EINVAL;
	}

	switch (cmd) {
	case RPCDEVIOCGET:
		error = rpcdev_request(data);
	break;
	case RPCDEVIOCPUT:
		error = rpcdev_reply(data);
	break;
	default:
		RPCDEV_DEBUG("rpc_dev: %s unkown ioctl cmd %d", 
		    rpcdev_services[type].svc_name, (int)cmd);
		error = EOPNOTSUPP;
	break;
	}

	return (error);
}

#if defined(__APPLE__)
static int 
rpcdev_poll(dev_t dev, int events, void * wql, cthread_t *td)
#else
static int 
rpcdev_poll(dev_t dev, int events, cthread_t *td)
#endif
{
  	int revents = 0;
	int error;
	uint32_t type;

	if (!(events & (POLLIN|POLLOUT)))
		return (0);

	if ((error = rpcdev_checkowner(dev, td))) {
		RPCDEV_DEBUG("back check owner");
		/* XXX return Error? */
		return (0);
	}

	type = rpcdev_dev2type(dev);

	/* check readable data */
	cmtx_lock(&rpcdev_services[type].svc_newq_mtx);
	if (!TAILQ_EMPTY(&rpcdev_services[type].svc_newq))
		revents |= POLLIN;
	cmtx_unlock(&rpcdev_services[type].svc_newq_mtx);

	cmtx_lock(&rpcdev_services[type].svc_waitq_mtx);
	if (!TAILQ_EMPTY(&rpcdev_services[type].svc_waitq))
		revents |= POLLOUT;
	cmtx_unlock(&rpcdev_services[type].svc_waitq_mtx);

	if ((revents & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT))
		selrecord(td, &rpcdev_services[type].svc_si);

	return (revents);
}

cthread_t *
rpcdev_gethandlertd(uint32_t type)
{
	cthread_t *td;

	if (type > RPCDEV_MAX_TYPE)
		return (NULL);

	cmtx_lock(&rpcdev_services[type].svc_opener_mtx);
	td = rpcdev_services[type].svc_opener;
	if (td == NULL)
		cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);

	return (td);
}

int
rpcdev_puthandlertd(uint32_t type)
{
	if (type > RPCDEV_MAX_TYPE)
		return (-1);

	cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);

	return (0);
}

int 
rpcdev_call(uint32_t type, caddr_t req_data,
    size_t req_len, caddr_t rep_data, size_t * rep_lenp, int sleeponreply)
{
  	struct rpcdev_upcall 	*u;
	int 			 error = 0; 
	unsigned int 		 xtmp;
#if defined(__APPLE__)
	boolean_t		 funnel_state;

	funnel_state = thread_funnel_set(kernel_flock, TRUE);
#endif /* __APPLE__ */

	if (req_data == NULL || rep_data == NULL || req_len == 0 || 
	    *rep_lenp == 0) {
		error = EFAULT;
		goto bad;
	}

	if (type > RPCDEV_MAX_TYPE) {
		error = EINVAL;
		goto bad;
	}

	cmtx_lock(&rpcdev_services[type].svc_opener_mtx);
	if (rpcdev_services[type].svc_opener == NULL) {
	  	/* no daemon attached */
		cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);
		error = EINVAL;
		goto bad;
	}
	cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);

	RPCDEV_DEBUG("upcall %d/%d:%d", type, req_len, *rep_lenp);

	rpcdev_upcall_get(u);

	u->up_error = 0;
	u->up_rep = rep_data;
	u->up_replen = rep_lenp;
	u->up_type = type;

	u->up_req = req_data;
	u->up_reqlen = req_len;

	u->up_sleeponreply = sleeponreply;

	/* get new XID */
	cmtx_lock(&rpcdev_xid_mtx);
	do {
		RANDOM(xtmp);
	} while ((xtmp % 256) == 0);
	rpcdev_xid += xtmp;
	u->up_xid = rpcdev_xid;
	cmtx_unlock(&rpcdev_xid_mtx);

	cmtx_lock(&rpcdev_services[type].svc_newq_mtx);
	TAILQ_INSERT_TAIL(&rpcdev_services[type].svc_newq, u, up_entry);
	cmtx_unlock(&rpcdev_services[type].svc_newq_mtx);

	selwakeup(&rpcdev_services[type].svc_si);

	RPCDEV_DEBUG("rpc_dev: %s message xid: %x sleeping", 
	    rpcdev_services[type].svc_name, u->up_xid);

	error = tsleep(u, PLOCK, "rpcdev", 0);
	if (error != 0)
		goto bad;	/* XXX - what about u? */

	/* upcall now removed from the queue */

	RPCDEV_DEBUG("rpc_dev: %s message xid: %x continues...", 
	    rpcdev_services[type].svc_name, u->up_xid);

	if (u->up_error != 0) {
		error = u->up_error;
		RPCDEV_DEBUG("upcall error %d", u->up_error);
	}

	rpcdev_upcall_put(u);
bad:
#ifdef __APPLE__
	(void) thread_funnel_set(kernel_flock, funnel_state);
#endif /* __APPLE__ */
	return (error);
}

void
rpcdev_purge(void)
{
  	struct rpcdev_upcall	*u;
	int                      i;
#if defined(__APPLE__)
	boolean_t 		 funnel_state;
#endif

#ifdef __APPLE__
	funnel_state = thread_funnel_set(kernel_flock, TRUE);
#endif /* __APPLE__ */

	for (i=0; i <= RPCDEV_MAX_TYPE; i++) {
		cmtx_lock(&rpcdev_services[i].svc_newq_mtx);
		while (!TAILQ_EMPTY(&rpcdev_services[i].svc_newq)) {
			u = TAILQ_FIRST(&rpcdev_services[i].svc_newq);
			TAILQ_REMOVE(&rpcdev_services[i].svc_newq, u, 
			    up_entry);
			u->up_error = EPIPE;
			wakeup(u);
		}
		cmtx_unlock(&rpcdev_services[i].svc_newq_mtx);

		cmtx_lock(&rpcdev_services[i].svc_waitq_mtx);
		while (!TAILQ_EMPTY(&rpcdev_services[i].svc_waitq)) {
			u = TAILQ_FIRST(&rpcdev_services[i].svc_waitq);
			TAILQ_REMOVE(&rpcdev_services[i].svc_waitq, u, up_entry);
			u->up_error = EPIPE;

			wakeup(u);
		}
		cmtx_unlock(&rpcdev_services[i].svc_waitq_mtx);
	}
#ifdef __APPLE__
	(void) thread_funnel_set(kernel_flock, funnel_state);
#endif /* __APPLE__ */
}

static int
rpcdev_checkowner(dev_t dev, cthread_t *td)
{
	uint32_t type;

	if (td == NULL)
		panic("rpc_dev: null thread\n");

	type = rpcdev_dev2type(dev);

	if (type > RPCDEV_MAX_TYPE) {
		RPCDEV_DEBUG("bad minor number");
		return (ENODEV);
	}

	cmtx_lock(&rpcdev_services[type].svc_opener_mtx);
	if (dev != rpcdev_services[type].svc_device) {
		cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);
		RPCDEV_DEBUG("minor <-> dev mismatch");
		return (ENODEV);
	}

	if (cthread_proc(rpcdev_services[type].svc_opener)->p_pid !=
	    cthread_proc(td)->p_pid) {
		cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);
		RPCDEV_DEBUG("proc %d is not the owner of service %s",
		    cthread_proc(td)->p_pid, rpcdev_services[type].svc_name);
	  	return (EBUSY);
	}
	cmtx_unlock(&rpcdev_services[type].svc_opener_mtx);

	return (0);
}
