10d63ce2bSvenki /*
20d63ce2bSvenki * CDDL HEADER START
30d63ce2bSvenki *
40d63ce2bSvenki * The contents of this file are subject to the terms of the
50d63ce2bSvenki * Common Development and Distribution License (the "License").
60d63ce2bSvenki * You may not use this file except in compliance with the License.
70d63ce2bSvenki *
80d63ce2bSvenki * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90d63ce2bSvenki * or http://www.opensolaris.org/os/licensing.
100d63ce2bSvenki * See the License for the specific language governing permissions
110d63ce2bSvenki * and limitations under the License.
120d63ce2bSvenki *
130d63ce2bSvenki * When distributing Covered Code, include this CDDL HEADER in each
140d63ce2bSvenki * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150d63ce2bSvenki * If applicable, add the following below this CDDL HEADER, with the
160d63ce2bSvenki * fields enclosed by brackets "[]" replaced with your own identifying
170d63ce2bSvenki * information: Portions Copyright [yyyy] [name of copyright owner]
180d63ce2bSvenki *
190d63ce2bSvenki * CDDL HEADER END
200d63ce2bSvenki */
210d63ce2bSvenki /*
22*c659a048SMichael Bergknoff * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
230d63ce2bSvenki */
240d63ce2bSvenki
250d63ce2bSvenki
260d63ce2bSvenki /*
270d63ce2bSvenki * sun4v domain services SNMP driver
280d63ce2bSvenki */
290d63ce2bSvenki
300d63ce2bSvenki #include <sys/types.h>
310d63ce2bSvenki #include <sys/file.h>
320d63ce2bSvenki #include <sys/errno.h>
330d63ce2bSvenki #include <sys/open.h>
340d63ce2bSvenki #include <sys/cred.h>
350d63ce2bSvenki #include <sys/uio.h>
360d63ce2bSvenki #include <sys/stat.h>
370d63ce2bSvenki #include <sys/ksynch.h>
380d63ce2bSvenki #include <sys/modctl.h>
390d63ce2bSvenki #include <sys/conf.h>
400d63ce2bSvenki #include <sys/devops.h>
410d63ce2bSvenki #include <sys/debug.h>
420d63ce2bSvenki #include <sys/cmn_err.h>
430d63ce2bSvenki #include <sys/ddi.h>
440d63ce2bSvenki #include <sys/sunddi.h>
450d63ce2bSvenki #include <sys/ds.h>
460d63ce2bSvenki #include <sys/ds_snmp.h>
470d63ce2bSvenki
480d63ce2bSvenki #define DS_SNMP_NAME "ds_snmp"
490d63ce2bSvenki #define DS_SNMP_MAX_OPENS 256
500d63ce2bSvenki #define DS_BITS_IN_UINT64 64
510d63ce2bSvenki #define DS_MINOR_POOL_SZ (DS_SNMP_MAX_OPENS / DS_BITS_IN_UINT64)
520d63ce2bSvenki #define DS_SNMP_MINOR_SHIFT 56
530d63ce2bSvenki #define DS_SNMP_DBG if (ds_snmp_debug) printf
540d63ce2bSvenki
550d63ce2bSvenki typedef struct {
560d63ce2bSvenki uint64_t seq_num;
570d63ce2bSvenki uint64_t type;
580d63ce2bSvenki } ds_snmp_msg_t;
590d63ce2bSvenki
600d63ce2bSvenki typedef enum {
610d63ce2bSvenki DS_SNMP_REQUEST = 0,
620d63ce2bSvenki DS_SNMP_REPLY = 1,
630d63ce2bSvenki DS_SNMP_ERROR = 2
640d63ce2bSvenki } ds_snmp_msg_type_t;
650d63ce2bSvenki
660d63ce2bSvenki typedef enum {
670d63ce2bSvenki DS_SNMP_READY = 0x0,
680d63ce2bSvenki DS_SNMP_REQUESTED = 0x1,
690d63ce2bSvenki DS_SNMP_DATA_AVL = 0x2,
700d63ce2bSvenki DS_SNMP_DATA_ERR = 0x3
710d63ce2bSvenki } ds_snmp_flags_t;
720d63ce2bSvenki
730d63ce2bSvenki /*
740d63ce2bSvenki * The single mutex 'lock' protects all the SNMP/DS variables in the state
750d63ce2bSvenki * structure.
760d63ce2bSvenki *
770d63ce2bSvenki * The condition variable 'state_cv' helps serialize write() calls for a
780d63ce2bSvenki * single descriptor. When write() is called, it sets a flag to indicate
790d63ce2bSvenki * that an SNMP request has been made to the agent. No more write()'s on
800d63ce2bSvenki * the same open descriptor will be allowed until this flag is cleared via
810d63ce2bSvenki * a matching read(), where the requested packet is consumed on arrival.
820d63ce2bSvenki * Read() then wakes up any waiters blocked in write() for sending the next
830d63ce2bSvenki * SNMP request to the agent.
840d63ce2bSvenki */
850d63ce2bSvenki typedef struct ds_snmp_state {
860d63ce2bSvenki dev_info_t *dip;
870d63ce2bSvenki int instance;
880d63ce2bSvenki dev_t dev;
890d63ce2bSvenki
900d63ce2bSvenki /* SNMP/DS */
910d63ce2bSvenki kmutex_t lock;
920d63ce2bSvenki kcondvar_t state_cv;
930d63ce2bSvenki ds_snmp_flags_t state;
940d63ce2bSvenki void *data;
950d63ce2bSvenki size_t data_len;
960d63ce2bSvenki uint64_t req_id;
970d63ce2bSvenki uint64_t last_req_id;
980d63ce2bSvenki uint64_t gencount;
990d63ce2bSvenki boolean_t sc_reset;
1000d63ce2bSvenki } ds_snmp_state_t;
1010d63ce2bSvenki
1020d63ce2bSvenki
1030d63ce2bSvenki static uint_t ds_snmp_debug = 0;
1040d63ce2bSvenki static void *ds_snmp_statep = NULL;
1050d63ce2bSvenki static int ds_snmp_instance = -1;
1060d63ce2bSvenki static dev_info_t *ds_snmp_devi = NULL;
1070d63ce2bSvenki
1080d63ce2bSvenki /*
1090d63ce2bSvenki * The ds_snmp_lock mutex protects the following data global to the
1100d63ce2bSvenki * driver.
1110d63ce2bSvenki *
1120d63ce2bSvenki * The ds_snmp_service_cv condition variable is used to resolve the
1130d63ce2bSvenki * potential race between the registration of snmp service via a
1140d63ce2bSvenki * ds_cap_init() in attach(), the acknowledgement of this registration
1150d63ce2bSvenki * at a later time in ds_snmp_reg_handler(), and a possible open() at
1160d63ce2bSvenki * a time inbetween. The ds_snmp_has_service and ds_snmp_handle are
1170d63ce2bSvenki * used to indicate whether the registration acknowledgement has happened
1180d63ce2bSvenki * or not.
1190d63ce2bSvenki *
1200d63ce2bSvenki * The ds_snmp_minor_pool[] is a bitmask to allocate and keep track of
1210d63ce2bSvenki * minor numbers dynamically.
1220d63ce2bSvenki */
1230d63ce2bSvenki static kmutex_t ds_snmp_lock;
1240d63ce2bSvenki static kcondvar_t ds_snmp_service_cv;
1250d63ce2bSvenki static int ds_snmp_has_service = B_FALSE;
1260d63ce2bSvenki static ds_svc_hdl_t ds_snmp_handle = DS_INVALID_HDL;
1270d63ce2bSvenki static uint64_t ds_snmp_minor_pool[DS_MINOR_POOL_SZ]; /* bitmask */
1280d63ce2bSvenki static int ds_snmp_num_opens = 0;
1290d63ce2bSvenki
1300d63ce2bSvenki static int ds_snmp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1310d63ce2bSvenki static int ds_snmp_attach(dev_info_t *, ddi_attach_cmd_t);
1320d63ce2bSvenki static int ds_snmp_detach(dev_info_t *, ddi_detach_cmd_t);
1330d63ce2bSvenki static int ds_snmp_open(dev_t *, int, int, cred_t *);
1340d63ce2bSvenki static int ds_snmp_close(dev_t, int, int, cred_t *);
1350d63ce2bSvenki static int ds_snmp_read(dev_t, struct uio *, cred_t *);
1360d63ce2bSvenki static int ds_snmp_write(dev_t, struct uio *, cred_t *);
1370d63ce2bSvenki static int ds_snmp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1380d63ce2bSvenki
1390d63ce2bSvenki /*
1400d63ce2bSvenki * DS Callbacks
1410d63ce2bSvenki */
1420d63ce2bSvenki static void ds_snmp_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
1430d63ce2bSvenki static void ds_snmp_unreg_handler(ds_cb_arg_t arg);
1440d63ce2bSvenki static void ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
1450d63ce2bSvenki
1460d63ce2bSvenki /*
1470d63ce2bSvenki * SNMP DS capability registration
1480d63ce2bSvenki */
1490d63ce2bSvenki static ds_ver_t ds_snmp_ver_1_0 = { 1, 0 };
1500d63ce2bSvenki static ds_capability_t ds_snmp_cap = {
1510d63ce2bSvenki "snmp",
1520d63ce2bSvenki &ds_snmp_ver_1_0,
1530d63ce2bSvenki 1
1540d63ce2bSvenki };
1550d63ce2bSvenki
1560d63ce2bSvenki /*
1570d63ce2bSvenki * SNMP DS Client callback vector
1580d63ce2bSvenki */
1590d63ce2bSvenki static ds_clnt_ops_t ds_snmp_ops = {
1600d63ce2bSvenki ds_snmp_reg_handler, /* ds_reg_cb */
1610d63ce2bSvenki ds_snmp_unreg_handler, /* ds_unreg_cb */
1620d63ce2bSvenki ds_snmp_data_handler, /* ds_data_cb */
1630d63ce2bSvenki NULL /* cb_arg */
1640d63ce2bSvenki };
1650d63ce2bSvenki
1660d63ce2bSvenki /*
1670d63ce2bSvenki * DS SNMP driver Ops Vector
1680d63ce2bSvenki */
1690d63ce2bSvenki static struct cb_ops ds_snmp_cb_ops = {
1700d63ce2bSvenki ds_snmp_open, /* cb_open */
1710d63ce2bSvenki ds_snmp_close, /* cb_close */
1720d63ce2bSvenki nodev, /* cb_strategy */
1730d63ce2bSvenki nodev, /* cb_print */
1740d63ce2bSvenki nodev, /* cb_dump */
1750d63ce2bSvenki ds_snmp_read, /* cb_read */
1760d63ce2bSvenki ds_snmp_write, /* cb_write */
1770d63ce2bSvenki ds_snmp_ioctl, /* cb_ioctl */
1780d63ce2bSvenki nodev, /* cb_devmap */
1790d63ce2bSvenki nodev, /* cb_mmap */
1800d63ce2bSvenki nodev, /* cb_segmap */
1810d63ce2bSvenki nochpoll, /* cb_chpoll */
1820d63ce2bSvenki ddi_prop_op, /* cb_prop_op */
1830d63ce2bSvenki (struct streamtab *)NULL, /* cb_str */
1840d63ce2bSvenki D_MP | D_64BIT, /* cb_flag */
1850d63ce2bSvenki CB_REV, /* cb_rev */
1860d63ce2bSvenki nodev, /* cb_aread */
1870d63ce2bSvenki nodev /* cb_awrite */
1880d63ce2bSvenki };
1890d63ce2bSvenki
1900d63ce2bSvenki static struct dev_ops ds_snmp_dev_ops = {
1910d63ce2bSvenki DEVO_REV, /* devo_rev */
1920d63ce2bSvenki 0, /* devo_refcnt */
1930d63ce2bSvenki ds_snmp_getinfo, /* devo_getinfo */
1940d63ce2bSvenki nulldev, /* devo_identify */
1950d63ce2bSvenki nulldev, /* devo_probe */
1960d63ce2bSvenki ds_snmp_attach, /* devo_attach */
1970d63ce2bSvenki ds_snmp_detach, /* devo_detach */
1980d63ce2bSvenki nodev, /* devo_reset */
1990d63ce2bSvenki &ds_snmp_cb_ops, /* devo_cb_ops */
2000d63ce2bSvenki (struct bus_ops *)NULL, /* devo_bus_ops */
20119397407SSherry Moore nulldev, /* devo_power */
20219397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */
2030d63ce2bSvenki };
2040d63ce2bSvenki
2050d63ce2bSvenki static struct modldrv modldrv = {
2060d63ce2bSvenki &mod_driverops,
20719397407SSherry Moore "Domain Services SNMP Driver",
2080d63ce2bSvenki &ds_snmp_dev_ops
2090d63ce2bSvenki };
2100d63ce2bSvenki
2110d63ce2bSvenki static struct modlinkage modlinkage = {
2120d63ce2bSvenki MODREV_1,
2130d63ce2bSvenki (void *)&modldrv,
2140d63ce2bSvenki NULL
2150d63ce2bSvenki };
2160d63ce2bSvenki
2170d63ce2bSvenki int
_init(void)2180d63ce2bSvenki _init(void)
2190d63ce2bSvenki {
2200d63ce2bSvenki int retval;
2210d63ce2bSvenki
2220d63ce2bSvenki mutex_init(&ds_snmp_lock, NULL, MUTEX_DRIVER, NULL);
2230d63ce2bSvenki cv_init(&ds_snmp_service_cv, NULL, CV_DRIVER, NULL);
2240d63ce2bSvenki
2250d63ce2bSvenki retval = ddi_soft_state_init(&ds_snmp_statep,
2260d63ce2bSvenki sizeof (ds_snmp_state_t), DS_SNMP_MAX_OPENS);
2270d63ce2bSvenki if (retval != 0) {
2280d63ce2bSvenki cv_destroy(&ds_snmp_service_cv);
2290d63ce2bSvenki mutex_destroy(&ds_snmp_lock);
2300d63ce2bSvenki return (retval);
2310d63ce2bSvenki }
2320d63ce2bSvenki
2330d63ce2bSvenki retval = mod_install(&modlinkage);
2340d63ce2bSvenki if (retval != 0) {
2350d63ce2bSvenki ddi_soft_state_fini(&ds_snmp_statep);
2360d63ce2bSvenki cv_destroy(&ds_snmp_service_cv);
2370d63ce2bSvenki mutex_destroy(&ds_snmp_lock);
2380d63ce2bSvenki }
2390d63ce2bSvenki
2400d63ce2bSvenki return (retval);
2410d63ce2bSvenki }
2420d63ce2bSvenki
2430d63ce2bSvenki int
_info(struct modinfo * modinfop)2440d63ce2bSvenki _info(struct modinfo *modinfop)
2450d63ce2bSvenki {
2460d63ce2bSvenki return (mod_info(&modlinkage, modinfop));
2470d63ce2bSvenki }
2480d63ce2bSvenki
2490d63ce2bSvenki int
_fini(void)2500d63ce2bSvenki _fini(void)
2510d63ce2bSvenki {
2520d63ce2bSvenki int retval;
2530d63ce2bSvenki
2540d63ce2bSvenki if ((retval = mod_remove(&modlinkage)) != 0)
2550d63ce2bSvenki return (retval);
2560d63ce2bSvenki
2570d63ce2bSvenki ddi_soft_state_fini(&ds_snmp_statep);
2580d63ce2bSvenki
2590d63ce2bSvenki cv_destroy(&ds_snmp_service_cv);
2600d63ce2bSvenki mutex_destroy(&ds_snmp_lock);
2610d63ce2bSvenki
2620d63ce2bSvenki return (retval);
2630d63ce2bSvenki }
2640d63ce2bSvenki
2650d63ce2bSvenki /*ARGSUSED*/
2660d63ce2bSvenki static int
ds_snmp_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)2670d63ce2bSvenki ds_snmp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2680d63ce2bSvenki {
2690d63ce2bSvenki ds_snmp_state_t *sp;
2700d63ce2bSvenki int retval = DDI_FAILURE;
2710d63ce2bSvenki
2720d63ce2bSvenki ASSERT(resultp != NULL);
2730d63ce2bSvenki
2740d63ce2bSvenki switch (cmd) {
2750d63ce2bSvenki case DDI_INFO_DEVT2DEVINFO:
2760d63ce2bSvenki sp = ddi_get_soft_state(ds_snmp_statep, getminor((dev_t)arg));
2770d63ce2bSvenki if (sp != NULL) {
2780d63ce2bSvenki *resultp = sp->dip;
2790d63ce2bSvenki retval = DDI_SUCCESS;
2800d63ce2bSvenki } else
2810d63ce2bSvenki *resultp = NULL;
2820d63ce2bSvenki break;
2830d63ce2bSvenki
2840d63ce2bSvenki case DDI_INFO_DEVT2INSTANCE:
2850d63ce2bSvenki *resultp = (void *)(uintptr_t)getminor((dev_t)arg);
2860d63ce2bSvenki retval = DDI_SUCCESS;
2870d63ce2bSvenki break;
2880d63ce2bSvenki }
2890d63ce2bSvenki
2900d63ce2bSvenki return (retval);
2910d63ce2bSvenki }
2920d63ce2bSvenki
2930d63ce2bSvenki static int
ds_snmp_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2940d63ce2bSvenki ds_snmp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2950d63ce2bSvenki {
2960d63ce2bSvenki int rv;
2970d63ce2bSvenki
2980d63ce2bSvenki switch (cmd) {
2990d63ce2bSvenki case DDI_ATTACH:
3000d63ce2bSvenki if (ds_snmp_instance != -1)
3010d63ce2bSvenki return (DDI_FAILURE);
3020d63ce2bSvenki break;
3030d63ce2bSvenki
3040d63ce2bSvenki case DDI_RESUME:
3050d63ce2bSvenki return (DDI_SUCCESS);
3060d63ce2bSvenki
3070d63ce2bSvenki default:
3080d63ce2bSvenki return (DDI_FAILURE);
3090d63ce2bSvenki }
3100d63ce2bSvenki
3110d63ce2bSvenki ds_snmp_instance = ddi_get_instance(dip);
3120d63ce2bSvenki if (ddi_create_minor_node(dip, DS_SNMP_NAME, S_IFCHR, ds_snmp_instance,
3130d63ce2bSvenki DDI_PSEUDO, 0) != DDI_SUCCESS) {
3140d63ce2bSvenki cmn_err(CE_WARN, "%s@%d: Unable to create minor node",
3150d63ce2bSvenki DS_SNMP_NAME, ds_snmp_instance);
3160d63ce2bSvenki return (DDI_FAILURE);
3170d63ce2bSvenki }
3180d63ce2bSvenki
3190d63ce2bSvenki bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
3200d63ce2bSvenki
3210d63ce2bSvenki ds_snmp_ops.cb_arg = dip;
3220d63ce2bSvenki if ((rv = ds_cap_init(&ds_snmp_cap, &ds_snmp_ops)) != 0) {
3230d63ce2bSvenki cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
3240d63ce2bSvenki ddi_remove_minor_node(dip, NULL);
3250d63ce2bSvenki ds_snmp_instance = -1;
3260d63ce2bSvenki return (DDI_FAILURE);
3270d63ce2bSvenki }
3280d63ce2bSvenki
3290d63ce2bSvenki ds_snmp_devi = dip;
3300d63ce2bSvenki ddi_report_dev(dip);
3310d63ce2bSvenki
3320d63ce2bSvenki return (DDI_SUCCESS);
3330d63ce2bSvenki }
3340d63ce2bSvenki
3350d63ce2bSvenki /*ARGSUSED*/
3360d63ce2bSvenki static int
ds_snmp_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3370d63ce2bSvenki ds_snmp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3380d63ce2bSvenki {
3390d63ce2bSvenki switch (cmd) {
3400d63ce2bSvenki case DDI_DETACH:
3410d63ce2bSvenki if (ds_snmp_instance == -1)
3420d63ce2bSvenki return (DDI_FAILURE);
3430d63ce2bSvenki break;
3440d63ce2bSvenki
3450d63ce2bSvenki case DDI_SUSPEND:
3460d63ce2bSvenki return (DDI_SUCCESS);
3470d63ce2bSvenki
3480d63ce2bSvenki default:
3490d63ce2bSvenki return (DDI_FAILURE);
3500d63ce2bSvenki }
3510d63ce2bSvenki
3520d63ce2bSvenki (void) ds_cap_fini(&ds_snmp_cap);
3530d63ce2bSvenki
3540d63ce2bSvenki ddi_remove_minor_node(ds_snmp_devi, NULL);
3550d63ce2bSvenki bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
3560d63ce2bSvenki
3570d63ce2bSvenki ds_snmp_instance = -1;
3580d63ce2bSvenki ds_snmp_devi = NULL;
3590d63ce2bSvenki
3600d63ce2bSvenki return (DDI_SUCCESS);
3610d63ce2bSvenki }
3620d63ce2bSvenki
3630d63ce2bSvenki static minor_t
ds_snmp_get_minor(void)3640d63ce2bSvenki ds_snmp_get_minor(void)
3650d63ce2bSvenki {
3660d63ce2bSvenki uint64_t val;
3670d63ce2bSvenki int i, ndx;
3680d63ce2bSvenki minor_t minor;
3690d63ce2bSvenki
3700d63ce2bSvenki mutex_enter(&ds_snmp_lock);
3710d63ce2bSvenki for (ndx = 0; ndx < DS_MINOR_POOL_SZ; ndx++) {
3720d63ce2bSvenki val = ds_snmp_minor_pool[ndx];
3730d63ce2bSvenki for (i = 0; i < DS_BITS_IN_UINT64; i++) {
3740d63ce2bSvenki if ((val & 0x1) == 0) {
3750d63ce2bSvenki ds_snmp_minor_pool[ndx] |= ((uint64_t)1 << i);
3760d63ce2bSvenki ds_snmp_num_opens++;
3770d63ce2bSvenki mutex_exit(&ds_snmp_lock);
3780d63ce2bSvenki
3790d63ce2bSvenki minor = ndx * DS_BITS_IN_UINT64 + i + 1;
3800d63ce2bSvenki
3810d63ce2bSvenki return (minor);
3820d63ce2bSvenki }
3830d63ce2bSvenki val >>= 1;
3840d63ce2bSvenki }
3850d63ce2bSvenki }
3860d63ce2bSvenki mutex_exit(&ds_snmp_lock);
3870d63ce2bSvenki
3880d63ce2bSvenki return (0);
3890d63ce2bSvenki }
3900d63ce2bSvenki
3910d63ce2bSvenki static void
ds_snmp_rel_minor(minor_t minor)3920d63ce2bSvenki ds_snmp_rel_minor(minor_t minor)
3930d63ce2bSvenki {
3940d63ce2bSvenki int i, ndx;
3950d63ce2bSvenki
3960d63ce2bSvenki ndx = (minor - 1) / DS_BITS_IN_UINT64;
3970d63ce2bSvenki i = (minor - 1) % DS_BITS_IN_UINT64;
3980d63ce2bSvenki
3990d63ce2bSvenki ASSERT(ndx < DS_MINOR_POOL_SZ);
4000d63ce2bSvenki
4010d63ce2bSvenki mutex_enter(&ds_snmp_lock);
4020d63ce2bSvenki
4030d63ce2bSvenki ds_snmp_num_opens--;
4040d63ce2bSvenki ds_snmp_minor_pool[ndx] &= ~((uint64_t)1 << i);
4050d63ce2bSvenki
4060d63ce2bSvenki mutex_exit(&ds_snmp_lock);
4070d63ce2bSvenki }
4080d63ce2bSvenki
4090d63ce2bSvenki static boolean_t
ds_snmp_is_open(minor_t minor)4100d63ce2bSvenki ds_snmp_is_open(minor_t minor)
4110d63ce2bSvenki {
4120d63ce2bSvenki uint64_t val;
4130d63ce2bSvenki int i, ndx;
4140d63ce2bSvenki
4150d63ce2bSvenki ndx = (minor - 1) / DS_BITS_IN_UINT64;
4160d63ce2bSvenki i = (minor - 1) % DS_BITS_IN_UINT64;
4170d63ce2bSvenki
4180d63ce2bSvenki val = ((uint64_t)1 << i);
4190d63ce2bSvenki if (ds_snmp_minor_pool[ndx] & val)
4200d63ce2bSvenki return (B_TRUE);
4210d63ce2bSvenki else
4220d63ce2bSvenki return (B_FALSE);
4230d63ce2bSvenki }
4240d63ce2bSvenki
4250d63ce2bSvenki static int
ds_snmp_create_state(dev_t * devp)4260d63ce2bSvenki ds_snmp_create_state(dev_t *devp)
4270d63ce2bSvenki {
4280d63ce2bSvenki major_t major;
4290d63ce2bSvenki minor_t minor;
4300d63ce2bSvenki ds_snmp_state_t *sp;
4310d63ce2bSvenki
4320d63ce2bSvenki if ((minor = ds_snmp_get_minor()) == 0)
4330d63ce2bSvenki return (EMFILE);
4340d63ce2bSvenki
4350d63ce2bSvenki if (ddi_soft_state_zalloc(ds_snmp_statep, minor) != DDI_SUCCESS) {
4360d63ce2bSvenki cmn_err(CE_WARN, "%s@%d: Unable to allocate state",
4370d63ce2bSvenki DS_SNMP_NAME, minor);
4380d63ce2bSvenki ds_snmp_rel_minor(minor);
4390d63ce2bSvenki return (ENOMEM);
4400d63ce2bSvenki }
4410d63ce2bSvenki
4420d63ce2bSvenki sp = ddi_get_soft_state(ds_snmp_statep, minor);
4430d63ce2bSvenki if (devp != NULL)
4440d63ce2bSvenki major = getemajor(*devp);
4450d63ce2bSvenki else
4460d63ce2bSvenki major = ddi_driver_major(ds_snmp_devi);
4470d63ce2bSvenki
4480d63ce2bSvenki sp->dev = makedevice(major, minor);
4490d63ce2bSvenki if (devp != NULL)
4500d63ce2bSvenki *devp = sp->dev;
4510d63ce2bSvenki
4520d63ce2bSvenki sp->instance = minor;
4530d63ce2bSvenki sp->data = NULL;
4540d63ce2bSvenki sp->data_len = 0;
4550d63ce2bSvenki sp->req_id = 0;
4560d63ce2bSvenki sp->last_req_id = 0;
4570d63ce2bSvenki sp->state = DS_SNMP_READY;
4580d63ce2bSvenki sp->sc_reset = B_FALSE;
4590d63ce2bSvenki
4600d63ce2bSvenki mutex_init(&sp->lock, NULL, MUTEX_DRIVER, NULL);
4610d63ce2bSvenki cv_init(&sp->state_cv, NULL, CV_DRIVER, NULL);
4620d63ce2bSvenki
4630d63ce2bSvenki return (0);
4640d63ce2bSvenki }
4650d63ce2bSvenki
4660d63ce2bSvenki static int
ds_snmp_destroy_state(dev_t dev)4670d63ce2bSvenki ds_snmp_destroy_state(dev_t dev)
4680d63ce2bSvenki {
4690d63ce2bSvenki ds_snmp_state_t *sp;
4700d63ce2bSvenki minor_t minor;
4710d63ce2bSvenki
4720d63ce2bSvenki minor = getminor(dev);
4730d63ce2bSvenki
4740d63ce2bSvenki if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
4750d63ce2bSvenki return (ENXIO);
4760d63ce2bSvenki
4770d63ce2bSvenki ASSERT(sp->instance == minor);
4780d63ce2bSvenki
4790d63ce2bSvenki /*
4800d63ce2bSvenki * If the app has not exited cleanly, the data may not have been
4810d63ce2bSvenki * read/memory freed, hence take care of that here
4820d63ce2bSvenki */
4830d63ce2bSvenki if (sp->data) {
4840d63ce2bSvenki kmem_free(sp->data, sp->data_len);
4850d63ce2bSvenki }
4860d63ce2bSvenki cv_destroy(&sp->state_cv);
4870d63ce2bSvenki mutex_destroy(&sp->lock);
4880d63ce2bSvenki
4890d63ce2bSvenki ddi_soft_state_free(ds_snmp_statep, minor);
4900d63ce2bSvenki ds_snmp_rel_minor(minor);
4910d63ce2bSvenki
4920d63ce2bSvenki return (0);
4930d63ce2bSvenki }
4940d63ce2bSvenki
4950d63ce2bSvenki /*ARGSUSED*/
4960d63ce2bSvenki static int
ds_snmp_open(dev_t * devp,int flag,int otyp,cred_t * credp)4970d63ce2bSvenki ds_snmp_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4980d63ce2bSvenki {
4990d63ce2bSvenki
5000d63ce2bSvenki if (otyp != OTYP_CHR)
5010d63ce2bSvenki return (EINVAL);
5020d63ce2bSvenki
5030d63ce2bSvenki if (ds_snmp_instance == -1)
5040d63ce2bSvenki return (ENXIO);
5050d63ce2bSvenki
5060d63ce2bSvenki /*
5070d63ce2bSvenki * Avoid possible race condition - ds service may not be there yet
5080d63ce2bSvenki */
5090d63ce2bSvenki mutex_enter(&ds_snmp_lock);
5100d63ce2bSvenki while (ds_snmp_has_service == B_FALSE) {
5110d63ce2bSvenki if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
5120d63ce2bSvenki mutex_exit(&ds_snmp_lock);
5130d63ce2bSvenki return (EINTR);
5140d63ce2bSvenki }
5150d63ce2bSvenki }
5160d63ce2bSvenki mutex_exit(&ds_snmp_lock);
5170d63ce2bSvenki
5180d63ce2bSvenki return (ds_snmp_create_state(devp));
5190d63ce2bSvenki }
5200d63ce2bSvenki
5210d63ce2bSvenki
5220d63ce2bSvenki /*ARGSUSED*/
5230d63ce2bSvenki static int
ds_snmp_close(dev_t dev,int flag,int otyp,cred_t * credp)5240d63ce2bSvenki ds_snmp_close(dev_t dev, int flag, int otyp, cred_t *credp)
5250d63ce2bSvenki {
5260d63ce2bSvenki if (otyp != OTYP_CHR)
5270d63ce2bSvenki return (EINVAL);
5280d63ce2bSvenki
5290d63ce2bSvenki if (ds_snmp_instance == -1)
5300d63ce2bSvenki return (ENXIO);
5310d63ce2bSvenki
5320d63ce2bSvenki if (ds_snmp_handle == DS_INVALID_HDL)
5330d63ce2bSvenki return (EIO);
5340d63ce2bSvenki
5350d63ce2bSvenki return (ds_snmp_destroy_state(dev));
5360d63ce2bSvenki }
5370d63ce2bSvenki
5380d63ce2bSvenki /*ARGSUSED*/
5390d63ce2bSvenki static int
ds_snmp_read(dev_t dev,struct uio * uiop,cred_t * credp)5400d63ce2bSvenki ds_snmp_read(dev_t dev, struct uio *uiop, cred_t *credp)
5410d63ce2bSvenki {
5420d63ce2bSvenki ds_snmp_state_t *sp;
5430d63ce2bSvenki minor_t minor;
5440d63ce2bSvenki size_t len;
5450d63ce2bSvenki int retval;
5460d63ce2bSvenki caddr_t tmpbufp = (caddr_t)NULL;
5470d63ce2bSvenki
5480d63ce2bSvenki /*
5490d63ce2bSvenki * Given that now we can have sc resets happening at any
5500d63ce2bSvenki * time, it is possible that it happened since the last time
5510d63ce2bSvenki * we issued a read, write or ioctl. If so, we need to wait
5520d63ce2bSvenki * for the unreg-reg pair to complete before we can do
5530d63ce2bSvenki * anything.
5540d63ce2bSvenki */
5550d63ce2bSvenki mutex_enter(&ds_snmp_lock);
5560d63ce2bSvenki while (ds_snmp_has_service == B_FALSE) {
5570d63ce2bSvenki DS_SNMP_DBG("ds_snmp_read: waiting for service\n");
5580d63ce2bSvenki if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
5590d63ce2bSvenki mutex_exit(&ds_snmp_lock);
5600d63ce2bSvenki return (EINTR);
5610d63ce2bSvenki }
5620d63ce2bSvenki }
5630d63ce2bSvenki mutex_exit(&ds_snmp_lock);
5640d63ce2bSvenki
5650d63ce2bSvenki if ((len = uiop->uio_resid) == 0)
5660d63ce2bSvenki return (0);
5670d63ce2bSvenki
5680d63ce2bSvenki minor = getminor(dev);
5690d63ce2bSvenki if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
5700d63ce2bSvenki return (ENXIO);
5710d63ce2bSvenki
5720d63ce2bSvenki mutex_enter(&sp->lock);
5730d63ce2bSvenki
5740d63ce2bSvenki if (sp->sc_reset == B_TRUE) {
5750d63ce2bSvenki mutex_exit(&sp->lock);
5760d63ce2bSvenki return (ECANCELED);
5770d63ce2bSvenki }
5780d63ce2bSvenki
5790d63ce2bSvenki /*
5800d63ce2bSvenki * Block or bail if there is no SNMP data
5810d63ce2bSvenki */
5820d63ce2bSvenki if (sp->state != DS_SNMP_DATA_AVL && sp->state != DS_SNMP_DATA_ERR) {
5830d63ce2bSvenki DS_SNMP_DBG("ds_snmp_read: no SNMP data\n");
5840d63ce2bSvenki if (uiop->uio_fmode & (FNDELAY | FNONBLOCK)) {
5850d63ce2bSvenki mutex_exit(&sp->lock);
5860d63ce2bSvenki return (EAGAIN);
5870d63ce2bSvenki }
5880d63ce2bSvenki while (sp->state != DS_SNMP_DATA_AVL &&
5890d63ce2bSvenki sp->state != DS_SNMP_DATA_ERR) {
5900d63ce2bSvenki if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
5910d63ce2bSvenki mutex_exit(&sp->lock);
5920d63ce2bSvenki return (EINTR);
5930d63ce2bSvenki }
5940d63ce2bSvenki }
5950d63ce2bSvenki }
5960d63ce2bSvenki
5970d63ce2bSvenki /*
5980d63ce2bSvenki * If there has been an error, it could be because the agent
5990d63ce2bSvenki * returned failure and there is no data to read, or an ldc-reset
6000d63ce2bSvenki * has happened. Figure out which and return appropriate
6010d63ce2bSvenki * error to the caller.
6020d63ce2bSvenki */
6030d63ce2bSvenki if (sp->state == DS_SNMP_DATA_ERR) {
6040d63ce2bSvenki if (sp->sc_reset == B_TRUE) {
6050d63ce2bSvenki mutex_exit(&sp->lock);
6060d63ce2bSvenki DS_SNMP_DBG("ds_snmp_read: sc got reset, "
6070d63ce2bSvenki "returning ECANCELED\n");
6080d63ce2bSvenki return (ECANCELED);
6090d63ce2bSvenki } else {
6100d63ce2bSvenki sp->state = DS_SNMP_READY;
6110d63ce2bSvenki cv_broadcast(&sp->state_cv);
6120d63ce2bSvenki mutex_exit(&sp->lock);
6130d63ce2bSvenki DS_SNMP_DBG("ds_snmp_read: data error, "
6140d63ce2bSvenki "returning EIO\n");
6150d63ce2bSvenki return (EIO);
6160d63ce2bSvenki }
6170d63ce2bSvenki }
6180d63ce2bSvenki
6190d63ce2bSvenki if (len > sp->data_len)
6200d63ce2bSvenki len = sp->data_len;
6210d63ce2bSvenki
6220d63ce2bSvenki tmpbufp = kmem_alloc(len, KM_SLEEP);
6230d63ce2bSvenki
6240d63ce2bSvenki bcopy(sp->data, (void *)tmpbufp, len);
6250d63ce2bSvenki kmem_free(sp->data, sp->data_len);
6260d63ce2bSvenki sp->data = (caddr_t)NULL;
6270d63ce2bSvenki sp->data_len = 0;
6280d63ce2bSvenki
6290d63ce2bSvenki /*
6300d63ce2bSvenki * SNMP data has been consumed, wake up anyone waiting to send
6310d63ce2bSvenki */
6320d63ce2bSvenki sp->state = DS_SNMP_READY;
6330d63ce2bSvenki cv_broadcast(&sp->state_cv);
6340d63ce2bSvenki
6350d63ce2bSvenki mutex_exit(&sp->lock);
6360d63ce2bSvenki
6370d63ce2bSvenki retval = uiomove(tmpbufp, len, UIO_READ, uiop);
6380d63ce2bSvenki kmem_free(tmpbufp, len);
6390d63ce2bSvenki
6400d63ce2bSvenki return (retval);
6410d63ce2bSvenki }
6420d63ce2bSvenki
6430d63ce2bSvenki /*ARGSUSED*/
6440d63ce2bSvenki static int
ds_snmp_write(dev_t dev,struct uio * uiop,cred_t * credp)6450d63ce2bSvenki ds_snmp_write(dev_t dev, struct uio *uiop, cred_t *credp)
6460d63ce2bSvenki {
6470d63ce2bSvenki ds_snmp_state_t *sp;
6480d63ce2bSvenki ds_snmp_msg_t hdr;
6490d63ce2bSvenki minor_t minor;
6500d63ce2bSvenki size_t len;
6510d63ce2bSvenki caddr_t tmpbufp;
652*c659a048SMichael Bergknoff size_t orig_size;
6530d63ce2bSvenki
6540d63ce2bSvenki /*
6550d63ce2bSvenki * Check if there was an sc reset; if yes, wait until we have the
6560d63ce2bSvenki * service back again.
6570d63ce2bSvenki */
6580d63ce2bSvenki mutex_enter(&ds_snmp_lock);
6590d63ce2bSvenki while (ds_snmp_has_service == B_FALSE) {
6600d63ce2bSvenki DS_SNMP_DBG("ds_snmp_write: waiting for service\n");
6610d63ce2bSvenki if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
6620d63ce2bSvenki mutex_exit(&ds_snmp_lock);
6630d63ce2bSvenki return (EINTR);
6640d63ce2bSvenki }
6650d63ce2bSvenki }
6660d63ce2bSvenki mutex_exit(&ds_snmp_lock);
6670d63ce2bSvenki
6680d63ce2bSvenki minor = getminor(dev);
6690d63ce2bSvenki if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
6700d63ce2bSvenki return (ENXIO);
6710d63ce2bSvenki
672*c659a048SMichael Bergknoff orig_size = uiop->uio_resid;
6730d63ce2bSvenki len = uiop->uio_resid + sizeof (ds_snmp_msg_t);
6740d63ce2bSvenki tmpbufp = kmem_alloc(len, KM_SLEEP);
6750d63ce2bSvenki
6760d63ce2bSvenki if (uiomove(tmpbufp + sizeof (ds_snmp_msg_t),
6770d63ce2bSvenki len - sizeof (ds_snmp_msg_t), UIO_WRITE, uiop) != 0) {
6780d63ce2bSvenki kmem_free(tmpbufp, len);
6790d63ce2bSvenki return (EIO);
6800d63ce2bSvenki }
6810d63ce2bSvenki
6820d63ce2bSvenki mutex_enter(&sp->lock);
6830d63ce2bSvenki
6840d63ce2bSvenki if (sp->sc_reset == B_TRUE) {
6850d63ce2bSvenki mutex_exit(&sp->lock);
6860d63ce2bSvenki kmem_free(tmpbufp, len);
6870d63ce2bSvenki DS_SNMP_DBG("ds_snmp_write: sc_reset is TRUE, "
6880d63ce2bSvenki "returning ECANCELD\n");
6890d63ce2bSvenki return (ECANCELED);
6900d63ce2bSvenki }
6910d63ce2bSvenki
6920d63ce2bSvenki /*
6930d63ce2bSvenki * wait if earlier transaction is not yet completed
6940d63ce2bSvenki */
6950d63ce2bSvenki while (sp->state != DS_SNMP_READY) {
6960d63ce2bSvenki if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
6970d63ce2bSvenki mutex_exit(&sp->lock);
6980d63ce2bSvenki kmem_free(tmpbufp, len);
699*c659a048SMichael Bergknoff uiop->uio_resid = orig_size;
7000d63ce2bSvenki return (EINTR);
7010d63ce2bSvenki }
7020d63ce2bSvenki /*
7030d63ce2bSvenki * Normally, only a reader would ever wake us up. But if we
7040d63ce2bSvenki * did get signalled with an ERROR, it could only mean there
7050d63ce2bSvenki * was an sc reset and there's no point waiting; we need to
7060d63ce2bSvenki * fail this write().
7070d63ce2bSvenki */
7080d63ce2bSvenki if (sp->state == DS_SNMP_DATA_ERR && sp->sc_reset == B_TRUE) {
7090d63ce2bSvenki DS_SNMP_DBG("ds_snmp_write: woke up with an sc_reset, "
7100d63ce2bSvenki "returning ECANCELED\n");
7110d63ce2bSvenki mutex_exit(&sp->lock);
7120d63ce2bSvenki kmem_free(tmpbufp, len);
7130d63ce2bSvenki return (ECANCELED);
7140d63ce2bSvenki }
7150d63ce2bSvenki }
7160d63ce2bSvenki
7170d63ce2bSvenki if (sp->req_id == (((uint64_t)1 << DS_SNMP_MINOR_SHIFT) - 1))
7180d63ce2bSvenki sp->req_id = 0; /* Reset */
7190d63ce2bSvenki
7200d63ce2bSvenki hdr.seq_num = ((uint64_t)minor << DS_SNMP_MINOR_SHIFT) | sp->req_id;
7210d63ce2bSvenki sp->last_req_id = hdr.seq_num;
7220d63ce2bSvenki (sp->req_id)++;
7230d63ce2bSvenki
7240d63ce2bSvenki /*
7250d63ce2bSvenki * Set state to SNMP_REQUESTED, but don't wakeup anyone yet
7260d63ce2bSvenki */
7270d63ce2bSvenki sp->state = DS_SNMP_REQUESTED;
7280d63ce2bSvenki
7290d63ce2bSvenki mutex_exit(&sp->lock);
7300d63ce2bSvenki
7310d63ce2bSvenki hdr.type = DS_SNMP_REQUEST;
7320d63ce2bSvenki bcopy((void *)&hdr, (void *)tmpbufp, sizeof (hdr));
7330d63ce2bSvenki
7340d63ce2bSvenki /*
7350d63ce2bSvenki * If the service went away since the time we entered this
7360d63ce2bSvenki * routine and now, tough luck. Just ignore the current
7370d63ce2bSvenki * write() and return.
7380d63ce2bSvenki */
7390d63ce2bSvenki mutex_enter(&ds_snmp_lock);
7400d63ce2bSvenki if (ds_snmp_has_service == B_FALSE) {
7410d63ce2bSvenki DS_SNMP_DBG("ds_snmp_write: service went away, aborting "
7420d63ce2bSvenki "write, returning ECANCELED\n");
7430d63ce2bSvenki mutex_exit(&ds_snmp_lock);
7440d63ce2bSvenki kmem_free(tmpbufp, len);
7450d63ce2bSvenki return (ECANCELED);
7460d63ce2bSvenki }
7470d63ce2bSvenki DS_SNMP_DBG("ds_snmp_write: ds_cap_send(0x%lx, %lu) called.\n",
7480d63ce2bSvenki ds_snmp_handle, len);
7490d63ce2bSvenki (void) ds_cap_send(ds_snmp_handle, tmpbufp, len);
7500d63ce2bSvenki mutex_exit(&ds_snmp_lock);
7510d63ce2bSvenki
7520d63ce2bSvenki kmem_free(tmpbufp, len);
7530d63ce2bSvenki
7540d63ce2bSvenki return (0);
7550d63ce2bSvenki }
7560d63ce2bSvenki
7570d63ce2bSvenki /*ARGSUSED*/
7580d63ce2bSvenki static int
ds_snmp_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)7590d63ce2bSvenki ds_snmp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
7600d63ce2bSvenki int *rvalp)
7610d63ce2bSvenki {
7620d63ce2bSvenki ds_snmp_state_t *sp;
7630d63ce2bSvenki struct dssnmp_info info;
7640d63ce2bSvenki minor_t minor;
7650d63ce2bSvenki
7660d63ce2bSvenki /*
7670d63ce2bSvenki * Check if there was an sc reset; if yes, wait until we have the
7680d63ce2bSvenki * service back again.
7690d63ce2bSvenki */
7700d63ce2bSvenki mutex_enter(&ds_snmp_lock);
7710d63ce2bSvenki while (ds_snmp_has_service == B_FALSE) {
7720d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: waiting for service\n");
7730d63ce2bSvenki if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
7740d63ce2bSvenki mutex_exit(&ds_snmp_lock);
7750d63ce2bSvenki return (EINTR);
7760d63ce2bSvenki }
7770d63ce2bSvenki }
7780d63ce2bSvenki mutex_exit(&ds_snmp_lock);
7790d63ce2bSvenki
7800d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: hdl=0x%lx\n", ds_snmp_handle);
7810d63ce2bSvenki
7820d63ce2bSvenki minor = getminor(dev);
7830d63ce2bSvenki if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
7840d63ce2bSvenki return (ENXIO);
7850d63ce2bSvenki
7860d63ce2bSvenki if (!(mode & FREAD))
7870d63ce2bSvenki return (EACCES);
7880d63ce2bSvenki
7890d63ce2bSvenki switch (cmd) {
7900d63ce2bSvenki case DSSNMP_GETINFO:
7910d63ce2bSvenki mutex_enter(&sp->lock);
7920d63ce2bSvenki
7930d63ce2bSvenki if (sp->sc_reset == B_TRUE) {
7940d63ce2bSvenki mutex_exit(&sp->lock);
7950d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: returning ECANCELED\n");
7960d63ce2bSvenki return (ECANCELED);
7970d63ce2bSvenki }
7980d63ce2bSvenki
7990d63ce2bSvenki while (sp->state != DS_SNMP_DATA_AVL &&
8000d63ce2bSvenki sp->state != DS_SNMP_DATA_ERR) {
8010d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
8020d63ce2bSvenki "waiting for data\n", sp->state, sp->sc_reset);
8030d63ce2bSvenki if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
804ece6eed9Sfw157321 sp->state = DS_SNMP_READY;
8050d63ce2bSvenki mutex_exit(&sp->lock);
8060d63ce2bSvenki return (EINTR);
8070d63ce2bSvenki }
8080d63ce2bSvenki }
8090d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
8100d63ce2bSvenki "out of wait!\n", sp->state, sp->sc_reset);
8110d63ce2bSvenki
8120d63ce2bSvenki /*
8130d63ce2bSvenki * If there has been an error, it could be because the
8140d63ce2bSvenki * agent returned failure and there is no data to read,
8150d63ce2bSvenki * or an ldc-reset has happened. Figure out which and
8160d63ce2bSvenki * return appropriate error to the caller.
8170d63ce2bSvenki */
8180d63ce2bSvenki if (sp->state == DS_SNMP_DATA_ERR) {
8190d63ce2bSvenki if (sp->sc_reset == B_TRUE) {
8200d63ce2bSvenki mutex_exit(&sp->lock);
8210d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=TRUE "
8220d63ce2bSvenki "returning ECANCELED\n");
8230d63ce2bSvenki return (ECANCELED);
8240d63ce2bSvenki } else {
8250d63ce2bSvenki sp->state = DS_SNMP_READY;
8260d63ce2bSvenki cv_broadcast(&sp->state_cv);
8270d63ce2bSvenki mutex_exit(&sp->lock);
8280d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=FALSE "
8290d63ce2bSvenki "returning EIO\n");
8300d63ce2bSvenki return (EIO);
8310d63ce2bSvenki }
8320d63ce2bSvenki }
8330d63ce2bSvenki
8340d63ce2bSvenki info.size = sp->data_len;
8350d63ce2bSvenki info.token = sp->gencount;
8360d63ce2bSvenki
8370d63ce2bSvenki mutex_exit(&sp->lock);
8380d63ce2bSvenki
8390d63ce2bSvenki if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0)
8400d63ce2bSvenki return (EFAULT);
8410d63ce2bSvenki break;
8420d63ce2bSvenki
8430d63ce2bSvenki case DSSNMP_CLRLNKRESET:
8440d63ce2bSvenki mutex_enter(&sp->lock);
8450d63ce2bSvenki
8460d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: DSSNMP_CLRLNKRESET\n");
8470d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=%d\n", sp->sc_reset);
8480d63ce2bSvenki
8490d63ce2bSvenki if (sp->sc_reset == B_TRUE) {
8500d63ce2bSvenki if (sp->data) {
8510d63ce2bSvenki DS_SNMP_DBG("ds_snmp_ioctl: data=%p, len=%lu\n",
8520d63ce2bSvenki sp->data, sp->data_len);
8530d63ce2bSvenki kmem_free(sp->data, sp->data_len);
8540d63ce2bSvenki }
8550d63ce2bSvenki sp->data = NULL;
8560d63ce2bSvenki sp->data_len = 0;
8570d63ce2bSvenki sp->state = DS_SNMP_READY;
8580d63ce2bSvenki sp->req_id = 0;
8590d63ce2bSvenki sp->last_req_id = 0;
8600d63ce2bSvenki sp->sc_reset = B_FALSE;
8610d63ce2bSvenki }
8620d63ce2bSvenki mutex_exit(&sp->lock);
8630d63ce2bSvenki break;
8640d63ce2bSvenki
8650d63ce2bSvenki default:
8660d63ce2bSvenki return (ENOTTY);
8670d63ce2bSvenki }
8680d63ce2bSvenki
8690d63ce2bSvenki return (0);
8700d63ce2bSvenki }
8710d63ce2bSvenki
8720d63ce2bSvenki /*
8730d63ce2bSvenki * DS Callbacks
8740d63ce2bSvenki */
8750d63ce2bSvenki /*ARGSUSED*/
8760d63ce2bSvenki static void
ds_snmp_reg_handler(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)8770d63ce2bSvenki ds_snmp_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
8780d63ce2bSvenki {
8790d63ce2bSvenki DS_SNMP_DBG("ds_snmp_reg_handler: registering handle 0x%lx for version "
8800d63ce2bSvenki "0x%x:0x%x\n", (uint64_t)hdl, ver->major, ver->minor);
8810d63ce2bSvenki
8820d63ce2bSvenki mutex_enter(&ds_snmp_lock);
8830d63ce2bSvenki
8840d63ce2bSvenki ASSERT(ds_snmp_handle == DS_INVALID_HDL);
8850d63ce2bSvenki
8860d63ce2bSvenki ds_snmp_handle = hdl;
8870d63ce2bSvenki ds_snmp_has_service = B_TRUE;
8880d63ce2bSvenki
8890d63ce2bSvenki cv_broadcast(&ds_snmp_service_cv);
8900d63ce2bSvenki
8910d63ce2bSvenki mutex_exit(&ds_snmp_lock);
8920d63ce2bSvenki
8930d63ce2bSvenki }
8940d63ce2bSvenki
8950d63ce2bSvenki /*ARGSUSED*/
8960d63ce2bSvenki static void
ds_snmp_unreg_handler(ds_cb_arg_t arg)8970d63ce2bSvenki ds_snmp_unreg_handler(ds_cb_arg_t arg)
8980d63ce2bSvenki {
8990d63ce2bSvenki minor_t minor;
9000d63ce2bSvenki ds_snmp_state_t *sp;
9010d63ce2bSvenki
9020d63ce2bSvenki DS_SNMP_DBG("ds_snmp_unreg_handler: un-registering ds_snmp service\n");
9030d63ce2bSvenki
9040d63ce2bSvenki mutex_enter(&ds_snmp_lock);
9050d63ce2bSvenki
9060d63ce2bSvenki if (ds_snmp_num_opens) {
9070d63ce2bSvenki DS_SNMP_DBG("ds_snmp_unreg_handler: %d opens, sc reset!\n",
9080d63ce2bSvenki ds_snmp_num_opens);
9090d63ce2bSvenki for (minor = 1; minor <= DS_SNMP_MAX_OPENS; minor++) {
9100d63ce2bSvenki if (ds_snmp_is_open(minor)) {
9110d63ce2bSvenki DS_SNMP_DBG("ds_snmp_unreg_handler: minor %d "
9120d63ce2bSvenki "open\n", minor);
9130d63ce2bSvenki sp = ddi_get_soft_state(ds_snmp_statep, minor);
9140d63ce2bSvenki if (sp == NULL)
9150d63ce2bSvenki continue;
9160d63ce2bSvenki
9170d63ce2bSvenki /*
9180d63ce2bSvenki * Set the sc_reset flag and break any waiters
9190d63ce2bSvenki * out of their existing reads/writes/ioctls.
9200d63ce2bSvenki */
9210d63ce2bSvenki DS_SNMP_DBG("ds_snmp_unreg_hdlr: about to "
9220d63ce2bSvenki "signal waiters\n");
9230d63ce2bSvenki mutex_enter(&sp->lock);
9240d63ce2bSvenki sp->sc_reset = B_TRUE;
9250d63ce2bSvenki sp->state = DS_SNMP_DATA_ERR;
9260d63ce2bSvenki cv_broadcast(&sp->state_cv);
9270d63ce2bSvenki mutex_exit(&sp->lock);
9280d63ce2bSvenki }
9290d63ce2bSvenki }
9300d63ce2bSvenki }
9310d63ce2bSvenki
9320d63ce2bSvenki ds_snmp_handle = DS_INVALID_HDL;
9330d63ce2bSvenki ds_snmp_has_service = B_FALSE;
9340d63ce2bSvenki
9350d63ce2bSvenki DS_SNMP_DBG("ds_snmp_unreg_handler: handle invalidated\n");
9360d63ce2bSvenki
9370d63ce2bSvenki mutex_exit(&ds_snmp_lock);
9380d63ce2bSvenki }
9390d63ce2bSvenki
9400d63ce2bSvenki /*ARGSUSED*/
9410d63ce2bSvenki static void
ds_snmp_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)9420d63ce2bSvenki ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
9430d63ce2bSvenki {
9440d63ce2bSvenki ds_snmp_state_t *sp;
9450d63ce2bSvenki ds_snmp_msg_t hdr;
9460d63ce2bSvenki size_t snmp_size;
9470d63ce2bSvenki minor_t minor;
9480d63ce2bSvenki
9490d63ce2bSvenki /*
9500d63ce2bSvenki * Make sure the header is at least valid
9510d63ce2bSvenki */
9520d63ce2bSvenki if (buflen < sizeof (hdr)) {
9530d63ce2bSvenki cmn_err(CE_WARN,
9540d63ce2bSvenki "ds_snmp_data_handler: buflen <%lu> too small", buflen);
9550d63ce2bSvenki return;
9560d63ce2bSvenki }
9570d63ce2bSvenki
9580d63ce2bSvenki ASSERT(buf != NULL);
9590d63ce2bSvenki bcopy(buf, (void *)&hdr, sizeof (hdr));
9600d63ce2bSvenki
9610d63ce2bSvenki DS_SNMP_DBG("ds_snmp_data_handler: msg buf len 0x%lx : type 0x%lx, "
9620d63ce2bSvenki "seqn 0x%lx\n", buflen, hdr.type, hdr.seq_num);
9630d63ce2bSvenki
9640d63ce2bSvenki minor = (int)(hdr.seq_num >> DS_SNMP_MINOR_SHIFT);
9650d63ce2bSvenki if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
9660d63ce2bSvenki return;
9670d63ce2bSvenki
9680d63ce2bSvenki mutex_enter(&sp->lock);
9690d63ce2bSvenki
9700d63ce2bSvenki /*
9710d63ce2bSvenki * If there is no pending SNMP request, then we've received
972ece6eed9Sfw157321 * bogus data or an SNMP trap or the reader was interrupted.
973ece6eed9Sfw157321 * Since we don't yet support SNMP traps, ignore it.
9740d63ce2bSvenki */
9750d63ce2bSvenki if (sp->state != DS_SNMP_REQUESTED) {
976ece6eed9Sfw157321 DS_SNMP_DBG("Received SNMP data without request");
9770d63ce2bSvenki mutex_exit(&sp->lock);
9780d63ce2bSvenki return;
9790d63ce2bSvenki }
9800d63ce2bSvenki
9810d63ce2bSvenki /*
9820d63ce2bSvenki * Response to a request therefore old SNMP must've been consumed
9830d63ce2bSvenki */
9840d63ce2bSvenki ASSERT(sp->data_len == 0);
9850d63ce2bSvenki ASSERT(sp->data == NULL);
9860d63ce2bSvenki
9870d63ce2bSvenki /*
9880d63ce2bSvenki * Response seq_num should match our request seq_num
9890d63ce2bSvenki */
9900d63ce2bSvenki if (hdr.seq_num != sp->last_req_id) {
9910d63ce2bSvenki cmn_err(CE_WARN, "Received DS snmp data out of sequence with "
9920d63ce2bSvenki "request");
9930d63ce2bSvenki mutex_exit(&sp->lock);
9940d63ce2bSvenki return;
9950d63ce2bSvenki }
9960d63ce2bSvenki
9970d63ce2bSvenki if (hdr.type == DS_SNMP_ERROR) {
9980d63ce2bSvenki sp->state = DS_SNMP_DATA_ERR;
9990d63ce2bSvenki DS_SNMP_DBG("ds_snmp_data_handler: hdr.type = DS_SNMP_ERROR\n");
10000d63ce2bSvenki } else {
10010d63ce2bSvenki snmp_size = buflen - sizeof (ds_snmp_msg_t);
10020d63ce2bSvenki sp->data = kmem_alloc(snmp_size, KM_SLEEP);
10030d63ce2bSvenki sp->data_len = snmp_size;
10040d63ce2bSvenki sp->state = DS_SNMP_DATA_AVL;
10050d63ce2bSvenki
10060d63ce2bSvenki bcopy((caddr_t)buf + sizeof (ds_snmp_msg_t),
10070d63ce2bSvenki sp->data, sp->data_len);
10080d63ce2bSvenki }
10090d63ce2bSvenki
10100d63ce2bSvenki sp->gencount++;
10110d63ce2bSvenki
10120d63ce2bSvenki /*
10130d63ce2bSvenki * Wake up any readers waiting for data
10140d63ce2bSvenki */
10150d63ce2bSvenki cv_broadcast(&sp->state_cv);
10160d63ce2bSvenki mutex_exit(&sp->lock);
10170d63ce2bSvenki }
1018