vlds.c revision ba9236fbcb49a99c7d71c0c25d1e45cb2a75ac78
/*
* 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
* 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.
*/
/*
* LDOMs Domain Services Device Driver
*/
#include <sys/mach_descrip.h>
#include <sys/sysevent.h>
static dev_info_t *vlds_devi;
typedef struct vlds_state {
int instance;
} vlds_state_t;
static void *vlds_statep;
typedef struct vlds_recv_hdr {
void *data; /* the data itself */
typedef struct vlds_svc_info {
int state; /* driver svc info state VLDS_RECV* */
int recv_nreaders; /* no of currently waiting readers */
#define VLDS_RECV_OK 1
#define VLDS_RECV_UNREG_PENDING 2
#define VLDS_RECV_OVERFLOW 3
static int vlds_ports_inited = 0;
#define VLDS_NAME "vlds"
int *rvalp);
void **resultp);
/* mdeg register functions */
static int ds_mdeg_register(void);
static int ds_mdeg_unregister(void);
/* driver utilities */
/*
* DS driver Ops Vector
*/
static struct cb_ops vlds_cb_ops = {
vlds_open, /* cb_open */
vlds_close, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
vlds_ioctl, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
CB_REV, /* cb_rev */
nodev, /* cb_aread */
nodev /* cb_awrite */
};
static struct dev_ops vlds_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
vlds_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
vlds_attach, /* devo_attach */
vlds_detach, /* devo_detach */
nodev, /* devo_reset */
&vlds_cb_ops, /* devo_cb_ops */
nulldev /* devo_power */
};
"Domain Services Driver 1.0",
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
/*
* Callback ops for user-land services.
*/
static ds_clnt_ops_t ds_user_ops = {
vlds_user_reg_cb, /* register */
vlds_user_unreg_cb, /* unregister */
vlds_user_data_cb, /* data */
NULL /* ds_ucap_init will fill in */
};
#define VLDS_MINOR_MAX SHRT_MAX
/* Definitions for binding handle array */
static kmutex_t vlds_minor_mutex;
/*
* Following vlds_minor_* routines map a binding handle to a minor number.
* Has to be called w/ locks held.
*/
static ulong_t *
vlds_minor_alloc(void)
{
/* Increase bitmap by one BT_NBIPUL */
}
if (bhst != &vlds_bitmap_initial)
return (vlds_minor_bitmap);
}
static void
{
if (bitmap != &vlds_bitmap_initial)
}
static index_t
vlds_minor_get(void)
{
/* Search for an available index */
vlds_minor_bits)) == -1) {
/* All busy - allocate additional binding handle bitmap space */
/* Reached our maximum of id's == SHRT_MAX */
return (0);
} else {
}
}
return (idx);
}
static void
{
}
static void
vlds_minor_init(void)
{
}
int
_init(void)
{
int s;
!= 0)
return (s);
if ((s = mod_install(&modlinkage)) != 0) {
return (s);
}
(void) ds_mdeg_register();
return (s);
}
int
_fini(void)
{
int s;
if ((s = mod_remove(&modlinkage)) != 0)
return (s);
(void) ds_mdeg_unregister();
return (s);
}
int
{
}
/*ARGSUSED*/
static int
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
*resultp = 0;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
if (cmd != DDI_ATTACH) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
if (cmd != DDI_DETACH) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
int minor;
return (EINVAL);
return (ENXIO);
minor = vlds_minor_get();
if (minor == 0)
/* All minors are busy */
return (EBUSY);
return (ENOMEM);
}
return (0);
}
/*ARGSUSED*/
static int
{
/*
* Unregister all handles associated with this process.
*/
return (EINVAL);
return (ENXIO);
}
}
return (0);
}
int
{
char evchan_name[MAX_CHNAME_LEN];
int rv;
if (flags & DSSF_ANYCB_VALID) {
__func__);
return (0);
}
EVCH_CREAT|EVCH_HOLD_PEND)) != 0) {
return (rv);
}
}
return (0);
}
#define ARGTOINT(x) ((int)(x))
static int
{
char *str;
if (len == 0) {
return (0);
}
if (len > MAXNAMELEN) {
len);
return (EINVAL);
}
return (EFAULT);
}
return (EINVAL);
}
return (0);
}
static int
{
int rv;
str = "";
}
/*
* If string is longer than user buffer, return a
* truncated, null-terminated string.
*/
if (len > 0) {
}
}
if (tstr) {
}
if (rv) {
return (EFAULT);
}
return (0);
}
static int
{
char *servp;
uint_t n;
int i;
int rv;
(void *)capp);
return (EFAULT);
}
if (nver > VLDS_MAX_VERS) {
nver);
return (EINVAL);
}
return (rv);
__func__);
return (EINVAL);
}
n = nver * sizeof (vlds_ver_t);
return (EFAULT);
}
for (i = 0; i < nver; i++) {
}
return (0);
}
static void
{
}
/*ARGSUSED*/
static int
int *rvalp)
{
char *servicep;
int rv;
return (ENXIO);
switch (cmd) {
case VLDS_SVC_REG:
{
mode) != 0) {
__func__);
return (EFAULT);
}
mode)) != 0) {
return (rv);
}
return (rv);
}
if (rv) {
return (rv);
}
__func__);
return (EFAULT);
}
break;
}
case VLDS_UNREG_HDL:
{
mode) != 0) {
__func__);
return (EFAULT);
}
" hdl: %lx inst: %d failed (%d)", __func__,
return (rv);
}
return (rv);
}
break;
}
case VLDS_HDL_LOOKUP:
{
mode) != 0) {
__func__);
return (EFAULT);
}
if (maxhdls == 0) {
return (EINVAL);
}
mode)) != 0) {
return (EFAULT);
" service is NULL", __func__);
return (EINVAL);
}
} else {
}
if (rv) {
if (hdlsp) {
}
return (rv);
}
if (hdlsp) {
}
" failed", __func__);
return (EFAULT);
}
if (hdlsp) {
}
" failed", __func__);
return (EFAULT);
}
break;
}
case VLDS_DMN_LOOKUP:
{
mode) != 0) {
__func__);
return (EFAULT);
}
return (rv);
}
return (rv);
}
break;
}
case VLDS_SEND_MSG:
{
char *bufp;
mode) != 0) {
__func__);
return (EFAULT);
}
" hdl: %lx inst: %d failed (%d)", __func__,
return (rv);
}
buflen);
mode) != 0) {
"copyin failed", __func__,
return (EFAULT);
}
return (rv);
}
buflen);
break;
}
case VLDS_RECV_MSG:
{
mode) != 0) {
__func__);
return (EFAULT);
}
" hdl: %lx inst: %d failed (%d)", __func__,
return (rv);
}
return (rv);
}
msglen_arg = msglen;
sizeof (msglen_arg), mode) != 0) {
"failed", __func__);
return (EFAULT);
}
return (EFBIG);
}
break;
}
case VLDS_HDL_ISREADY:
{
mode) != 0) {
"failed", __func__);
return (EFAULT);
}
return (rv);
}
sizeof (is_ready_arg), mode) != 0) {
"vlds_isready failed", __func__);
return (EFAULT);
}
break;
}
case VLDS_DOM_NAM2HDL:
{
char *domain_name;
mode) != 0) {
"failed", __func__);
return (EFAULT);
}
&domain_name, mode)) != 0) {
return (EFAULT);
" domain_name is NULL", __func__);
return (EINVAL);
}
return (rv);
}
" failed", __func__);
return (EFAULT);
}
break;
}
case VLDS_DOM_HDL2NAM:
{
char *domain_name;
mode) != 0) {
"failed", __func__);
return (EFAULT);
}
return (rv);
}
return (rv);
}
break;
}
default:
return (EINVAL);
}
return (0);
}
static uint_t
{
if (flags & VLDS_REG_CLIENT)
sflags |= DSSF_ISCLIENT;
if (flags & VLDS_REGCB_VALID)
if (flags & VLDS_UNREGCB_VALID)
if (flags & VLDS_DATACB_VALID)
return (sflags);
}
/*
* MD registration code.
* Placed in vlds rather than ds module due to cirular dependency of
* platsvc module which contains the mdeg code.
*/
/*
* There's only one domain services node, so we don't
* need to specify any match conditions. However, we
* have to supply a non-NULL property spec.
*/
static mdeg_prop_spec_t ds_prop_template[] = {
};
static mdeg_node_spec_t ds_node_template =
/*
* Matching criteria passed to the MDEG to register interest
* in changes to domain services port nodes identified by their
* 'id' property.
*/
static md_prop_match_t ds_port_prop_match[] = {
{ MDET_PROP_VAL, "id" },
{ MDET_LIST_END, NULL }
};
/* mdeg callback */
static int
{
int idx;
int rv;
return (MDEG_FAILURE);
}
/* process added ports */
/* attempt to add a port */
if (vlds_ports_inited) {
}
}
}
/* process removed ports */
/* read in the port's id property */
continue;
}
/* attempt to remove a port */
}
}
vlds_ports_inited = 1;
return (MDEG_SUCCESS);
}
/* register callback to mdeg */
static int
ds_mdeg_register(void)
{
int rv;
/* perform the registration */
NULL, &ds_mdeg_hdl);
if (rv != MDEG_SUCCESS) {
"failed, err = %d", rv);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* unregister callback from mdeg */
static int
ds_mdeg_unregister(void)
{
return (mdeg_unregister(ds_mdeg_hdl));
}
static int
{
/*
* Find the channel-endpoint node(s) (which should be under this
* port node) which contain the channel id(s).
*/
return (-1);
}
/* allocate space for node list */
if (nchan <= 0) {
__func__);
return (-1);
}
nchan);
/* use property from first node found */
__func__);
return (-1);
}
return (0);
}
/* add a DS services port */
static int
{
int rv;
char *dom_name;
/* read in the port's id property */
return (MDEG_FAILURE);
}
if (portno >= DS_MAX_PORTS) {
"larger than maximum supported number of ports", __func__,
portno);
return (MDEG_FAILURE);
}
/* get all channels for this device (currently only one) */
return (MDEG_FAILURE);
}
}
}
if (rv != 0) {
if (vlds_ports_inited) {
}
return (MDEG_FAILURE);
}
return (MDEG_SUCCESS);
}
static void
{
char *servicep;
int minor;
if ((flags & DSSF_DATACB_VALID) == 0) {
/*
* must allocate and init the svc read queue.
*/
hdl);
}
if ((flags & DSSF_REGCB_VALID) != 0) {
(flags & DSSF_ISCLIENT) != 0) ||
} else {
"succeeded", __func__);
}
}
}
static void
{
int minor;
void *dpsp;
if ((flags & DSSF_DATACB_VALID) == 0) {
if (dpsp) {
__func__);
}
}
if ((flags & DSSF_UNREGCB_VALID) != 0) {
hdl);
EVCH_SLEEP)) {
}
}
}
static void
{
int minor;
void *dpsp;
if ((flags & DSSF_DATACB_VALID) == 0) {
} else {
}
}
}
/*
* Initialize receive queue if request is from user land but
* data callback is null (implying user will be using ds_recv_msg).
*/
static void
{
}
static void
{
}
static int
{
int rv;
break;
__func__);
return (ENOBUFS);
}
/*
* Passing in a buflen of 0 allows user to poll for msgs.
*/
if (buflen == 0) {
*msglenp = 0;
return (EFBIG);
}
if (rv == 0) {
return (EINTR);
}
}
return (EINVAL);
}
/*
* Don't transfer truncated data, return EFBIG error if user-supplied
* buffer is too small.
*/
return (EFBIG);
}
} else {
}
if (rv == 0) {
}
if (rv != 0) {
return (EFAULT);
}
return (0);
}
static void
{
}
/*
* Make sure other readers have exited.
*/
while (dpsp->recv_nreaders > 0) {
}
}
static int
{
/*
* If we've already encountered an overflow, or there
* are pending messages and either queue size and
* message limits will be exceeded with this message,
* we mark the recvq as overflowed and return an ENOBUFS
* error. This allows the enqueuing of one big message
* or several little messages.
*/
__func__);
return (ENOBUFS);
}
} else {
}
}
return (0);
}
static int
int mode)
{
void *dpsp;
int rv;
return (rv);
}
return (EINVAL);
}
return (ENXIO);
}
return (rv);
}