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 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 2440d63ce2bSvenki _info(struct modinfo *modinfop) 2450d63ce2bSvenki { 2460d63ce2bSvenki return (mod_info(&modlinkage, modinfop)); 2470d63ce2bSvenki } 2480d63ce2bSvenki 2490d63ce2bSvenki int 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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