ds_snmp.c revision ece6eed9d93d19d2c26893d5ac4fd2f73b1633dc
/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* sun4v domain services SNMP driver
*/
#define DS_SNMP_NAME "ds_snmp"
#define DS_SNMP_MAX_OPENS 256
#define DS_BITS_IN_UINT64 64
#define DS_SNMP_MINOR_SHIFT 56
typedef struct {
typedef enum {
DS_SNMP_REQUEST = 0,
DS_SNMP_REPLY = 1,
DS_SNMP_ERROR = 2
typedef enum {
DS_SNMP_READY = 0x0,
DS_SNMP_REQUESTED = 0x1,
DS_SNMP_DATA_AVL = 0x2,
DS_SNMP_DATA_ERR = 0x3
/*
* structure.
*
* The condition variable 'state_cv' helps serialize write() calls for a
* single descriptor. When write() is called, it sets a flag to indicate
* that an SNMP request has been made to the agent. No more write()'s on
* the same open descriptor will be allowed until this flag is cleared via
* a matching read(), where the requested packet is consumed on arrival.
* Read() then wakes up any waiters blocked in write() for sending the next
* SNMP request to the agent.
*/
typedef struct ds_snmp_state {
int instance;
void *data;
static uint_t ds_snmp_debug = 0;
static void *ds_snmp_statep = NULL;
static int ds_snmp_instance = -1;
/*
* The ds_snmp_lock mutex protects the following data global to the
* driver.
*
* The ds_snmp_service_cv condition variable is used to resolve the
* potential race between the registration of snmp service via a
* ds_cap_init() in attach(), the acknowledgement of this registration
* at a later time in ds_snmp_reg_handler(), and a possible open() at
* a time inbetween. The ds_snmp_has_service and ds_snmp_handle are
* used to indicate whether the registration acknowledgement has happened
* or not.
*
* The ds_snmp_minor_pool[] is a bitmask to allocate and keep track of
* minor numbers dynamically.
*/
static kmutex_t ds_snmp_lock;
static kcondvar_t ds_snmp_service_cv;
static int ds_snmp_has_service = B_FALSE;
static int ds_snmp_num_opens = 0;
/*
* DS Callbacks
*/
/*
* SNMP DS capability registration
*/
static ds_capability_t ds_snmp_cap = {
"snmp",
1
};
/*
* SNMP DS Client callback vector
*/
static ds_clnt_ops_t ds_snmp_ops = {
ds_snmp_reg_handler, /* ds_reg_cb */
ds_snmp_unreg_handler, /* ds_unreg_cb */
ds_snmp_data_handler, /* ds_data_cb */
NULL /* cb_arg */
};
/*
* DS SNMP driver Ops Vector
*/
static struct cb_ops ds_snmp_cb_ops = {
ds_snmp_open, /* cb_open */
ds_snmp_close, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
ds_snmp_read, /* cb_read */
ds_snmp_write, /* cb_write */
ds_snmp_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 ds_snmp_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
ds_snmp_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
ds_snmp_attach, /* devo_attach */
ds_snmp_detach, /* devo_detach */
nodev, /* devo_reset */
&ds_snmp_cb_ops, /* devo_cb_ops */
nulldev /* devo_power */
};
"Domain Services SNMP Driver 1.0",
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
int
_init(void)
{
int retval;
sizeof (ds_snmp_state_t), DS_SNMP_MAX_OPENS);
if (retval != 0) {
return (retval);
}
if (retval != 0) {
}
return (retval);
}
int
{
}
int
_fini(void)
{
int retval;
return (retval);
return (retval);
}
/*ARGSUSED*/
static int
{
int retval = DDI_FAILURE;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
} else
break;
case DDI_INFO_DEVT2INSTANCE:
break;
}
return (retval);
}
static int
{
int rv;
switch (cmd) {
case DDI_ATTACH:
if (ds_snmp_instance != -1)
return (DDI_FAILURE);
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
DDI_PSEUDO, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
ds_snmp_instance = -1;
return (DDI_FAILURE);
}
ds_snmp_devi = dip;
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
switch (cmd) {
case DDI_DETACH:
if (ds_snmp_instance == -1)
return (DDI_FAILURE);
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
(void) ds_cap_fini(&ds_snmp_cap);
ds_snmp_instance = -1;
ds_snmp_devi = NULL;
return (DDI_SUCCESS);
}
static minor_t
ds_snmp_get_minor(void)
{
int i, ndx;
for (i = 0; i < DS_BITS_IN_UINT64; i++) {
if ((val & 0x1) == 0) {
return (minor);
}
val >>= 1;
}
}
return (0);
}
static void
{
int i, ndx;
}
static boolean_t
{
int i, ndx;
return (B_TRUE);
else
return (B_FALSE);
}
static int
{
if ((minor = ds_snmp_get_minor()) == 0)
return (EMFILE);
return (ENOMEM);
}
else
sp->last_req_id = 0;
return (0);
}
static int
{
return (ENXIO);
/*
* If the app has not exited cleanly, the data may not have been
*/
}
return (0);
}
/*ARGSUSED*/
static int
{
return (EINVAL);
if (ds_snmp_instance == -1)
return (ENXIO);
/*
* Avoid possible race condition - ds service may not be there yet
*/
while (ds_snmp_has_service == B_FALSE) {
return (EINTR);
}
}
return (ds_snmp_create_state(devp));
}
/*ARGSUSED*/
static int
{
return (EINVAL);
if (ds_snmp_instance == -1)
return (ENXIO);
if (ds_snmp_handle == DS_INVALID_HDL)
return (EIO);
return (ds_snmp_destroy_state(dev));
}
/*ARGSUSED*/
static int
{
int retval;
/*
* Given that now we can have sc resets happening at any
* time, it is possible that it happened since the last time
* we issued a read, write or ioctl. If so, we need to wait
* for the unreg-reg pair to complete before we can do
* anything.
*/
while (ds_snmp_has_service == B_FALSE) {
DS_SNMP_DBG("ds_snmp_read: waiting for service\n");
return (EINTR);
}
}
return (0);
return (ENXIO);
return (ECANCELED);
}
/*
* Block or bail if there is no SNMP data
*/
DS_SNMP_DBG("ds_snmp_read: no SNMP data\n");
return (EAGAIN);
}
return (EINTR);
}
}
}
/*
* If there has been an error, it could be because the agent
* returned failure and there is no data to read, or an ldc-reset
* has happened. Figure out which and return appropriate
* error to the caller.
*/
DS_SNMP_DBG("ds_snmp_read: sc got reset, "
"returning ECANCELED\n");
return (ECANCELED);
} else {
DS_SNMP_DBG("ds_snmp_read: data error, "
"returning EIO\n");
return (EIO);
}
}
/*
* SNMP data has been consumed, wake up anyone waiting to send
*/
return (retval);
}
/*ARGSUSED*/
static int
{
/*
* Check if there was an sc reset; if yes, wait until we have the
* service back again.
*/
while (ds_snmp_has_service == B_FALSE) {
DS_SNMP_DBG("ds_snmp_write: waiting for service\n");
return (EINTR);
}
}
return (ENXIO);
return (EIO);
}
DS_SNMP_DBG("ds_snmp_write: sc_reset is TRUE, "
"returning ECANCELD\n");
return (ECANCELED);
}
/*
* wait if earlier transaction is not yet completed
*/
return (EINTR);
}
/*
* Normally, only a reader would ever wake us up. But if we
* did get signalled with an ERROR, it could only mean there
* was an sc reset and there's no point waiting; we need to
* fail this write().
*/
DS_SNMP_DBG("ds_snmp_write: woke up with an sc_reset, "
"returning ECANCELED\n");
return (ECANCELED);
}
}
/*
* Set state to SNMP_REQUESTED, but don't wakeup anyone yet
*/
/*
* If the service went away since the time we entered this
* routine and now, tough luck. Just ignore the current
* write() and return.
*/
if (ds_snmp_has_service == B_FALSE) {
DS_SNMP_DBG("ds_snmp_write: service went away, aborting "
"write, returning ECANCELED\n");
return (ECANCELED);
}
DS_SNMP_DBG("ds_snmp_write: ds_cap_send(0x%lx, %lu) called.\n",
return (0);
}
/*ARGSUSED*/
static int
int *rvalp)
{
struct dssnmp_info info;
/*
* Check if there was an sc reset; if yes, wait until we have the
* service back again.
*/
while (ds_snmp_has_service == B_FALSE) {
DS_SNMP_DBG("ds_snmp_ioctl: waiting for service\n");
return (EINTR);
}
}
return (ENXIO);
return (EACCES);
switch (cmd) {
case DSSNMP_GETINFO:
DS_SNMP_DBG("ds_snmp_ioctl: returning ECANCELED\n");
return (ECANCELED);
}
DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
return (EINTR);
}
}
DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
/*
* If there has been an error, it could be because the
* agent returned failure and there is no data to read,
* or an ldc-reset has happened. Figure out which and
* return appropriate error to the caller.
*/
DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=TRUE "
"returning ECANCELED\n");
return (ECANCELED);
} else {
DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=FALSE "
"returning EIO\n");
return (EIO);
}
}
return (EFAULT);
break;
case DSSNMP_CLRLNKRESET:
DS_SNMP_DBG("ds_snmp_ioctl: DSSNMP_CLRLNKRESET\n");
DS_SNMP_DBG("ds_snmp_ioctl: data=%p, len=%lu\n",
}
sp->last_req_id = 0;
}
break;
default:
return (ENOTTY);
}
return (0);
}
/*
* DS Callbacks
*/
/*ARGSUSED*/
static void
{
DS_SNMP_DBG("ds_snmp_reg_handler: registering handle 0x%lx for version "
}
/*ARGSUSED*/
static void
{
DS_SNMP_DBG("ds_snmp_unreg_handler: un-registering ds_snmp service\n");
if (ds_snmp_num_opens) {
DS_SNMP_DBG("ds_snmp_unreg_handler: %d opens, sc reset!\n",
if (ds_snmp_is_open(minor)) {
DS_SNMP_DBG("ds_snmp_unreg_handler: minor %d "
"open\n", minor);
continue;
/*
* Set the sc_reset flag and break any waiters
*/
DS_SNMP_DBG("ds_snmp_unreg_hdlr: about to "
"signal waiters\n");
}
}
}
DS_SNMP_DBG("ds_snmp_unreg_handler: handle invalidated\n");
}
/*ARGSUSED*/
static void
{
/*
* Make sure the header is at least valid
*/
"ds_snmp_data_handler: buflen <%lu> too small", buflen);
return;
}
DS_SNMP_DBG("ds_snmp_data_handler: msg buf len 0x%lx : type 0x%lx, "
return;
/*
* If there is no pending SNMP request, then we've received
* bogus data or an SNMP trap or the reader was interrupted.
* Since we don't yet support SNMP traps, ignore it.
*/
DS_SNMP_DBG("Received SNMP data without request");
return;
}
/*
* Response to a request therefore old SNMP must've been consumed
*/
/*
* Response seq_num should match our request seq_num
*/
"request");
return;
}
DS_SNMP_DBG("ds_snmp_data_handler: hdr.type = DS_SNMP_ERROR\n");
} else {
}
/*
* Wake up any readers waiting for data
*/
}