OpenSolaris_b135/uts/sun4v/io/drctl_impl.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <sys/door.h>
#include <sys/note.h>
#include <sys/drctl.h>
#include <sys/drctl_impl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/dr_util.h>

static door_handle_t drctl_dh;	/* Door for upcalls */


int
i_drctl_ioctl(int cmd, intptr_t arg)
{
	int rv;
	drctl_setup_t setup_rqst;

	switch (cmd) {
	case DRCTL_IOCTL_CONNECT_SERVER:
		if (ddi_copyin((caddr_t)arg,
		    &setup_rqst, sizeof (setup_rqst), 0) != 0) {
			cmn_err(CE_WARN, "i_drctl_ioctl: ddi_copyin failed "
			    "for DRCTL_IOCTL_CONNECT_SERVER");
			rv = EFAULT;
			break;
		}

		drctl_dh = door_ki_lookup(setup_rqst.did);
		rv = 0;
		break;

	default:
		rv = EIO;
	}

	return (rv);
}

int
i_drctl_send(void *msg, size_t size, void **obufp, size_t *osize)
{
	int up_err;
	int rv = 0;
	door_arg_t door_args;
	door_handle_t dh = drctl_dh;
	static const char me[] = "i_drctl_send";

retry:
	if (dh)
		door_ki_hold(dh);
	else
		return (EIO);

	door_args.data_ptr = (char *)msg;
	door_args.data_size = size;
	door_args.desc_ptr = NULL;
	door_args.desc_num = 0;

	/*
	 * We don't know the size of the message the daemon
	 * will pass back to us.  By setting rbuf to NULL,
	 * we force the door code to allocate a buf of the
	 * appropriate size.  We must set rsize > 0, however,
	 * else the door code acts as if no response was
	 * expected and doesn't pass the data to us.
	 */
	door_args.rbuf = NULL;
	door_args.rsize = 1;
	DR_DBG_CTL("%s: msg %p size %ld obufp %p osize %p\n",
	    me, msg, size, (void *)obufp, (void *)osize);

	up_err = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
	if (up_err == 0) {
		if (door_args.rbuf == NULL)
			goto done;

		DR_DBG_CTL("%s: rbuf %p rsize %ld\n", me,
		    (void *)door_args.rbuf, door_args.rsize);

		if (obufp != NULL) {
			*obufp = door_args.rbuf;
			*osize = door_args.rsize;
			DR_DBG_KMEM("%s: (door) alloc addr %p size %ld\n",
			    __func__,
			    (void *)(door_args.rbuf), door_args.rsize);
		} else {
			/*
			 * No output buffer pointer was passed in,
			 * so the response buffer allocated by the
			 * door code must be deallocated.
			 */
			DR_DBG_KMEM("%s: free addr %p size %ld\n", __func__,
			    (void *)(door_args.rbuf), door_args.rsize);
			kmem_free(door_args.rbuf, door_args.rsize);
		}
	} else {
		switch (up_err) {
		case EINTR:
			DR_DBG_CTL("%s: door call returned EINTR\n", me);
			_NOTE(FALLTHROUGH)
		case EAGAIN:
			/*
			 * Server process may be forking, try again.
			 */
			door_ki_rele(dh);
			delay(hz);
			goto retry;
		case EBADF:
		case EINVAL:
			drctl_dh = NULL;
			DR_DBG_CTL(
			    "%s: door call failed with %d\n", me, up_err);
			rv = EIO;
			break;
		default:
			DR_DBG_CTL("%s: unexpected return "
			    "code %d from door_ki_upcall\n", me, up_err);
			rv = EIO;
			break;
		}
	}

done:
	door_ki_rele(dh);
	return (rv);
}