xref: /titanic_50/usr/src/uts/sun4v/io/ds_snmp.c (revision 753a6d457b330b1b29b2d3eefcd0831116ce950d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * sun4v domain services SNMP driver
29  */
30 
31 #include <sys/types.h>
32 #include <sys/file.h>
33 #include <sys/errno.h>
34 #include <sys/open.h>
35 #include <sys/cred.h>
36 #include <sys/uio.h>
37 #include <sys/stat.h>
38 #include <sys/ksynch.h>
39 #include <sys/modctl.h>
40 #include <sys/conf.h>
41 #include <sys/devops.h>
42 #include <sys/debug.h>
43 #include <sys/cmn_err.h>
44 #include <sys/ddi.h>
45 #include <sys/sunddi.h>
46 #include <sys/ds.h>
47 #include <sys/ds_snmp.h>
48 
49 #define	DS_SNMP_NAME		"ds_snmp"
50 #define	DS_SNMP_MAX_OPENS	256
51 #define	DS_BITS_IN_UINT64	64
52 #define	DS_MINOR_POOL_SZ	(DS_SNMP_MAX_OPENS / DS_BITS_IN_UINT64)
53 #define	DS_SNMP_MINOR_SHIFT	56
54 #define	DS_SNMP_DBG		if (ds_snmp_debug) printf
55 
56 typedef	struct {
57 	uint64_t	seq_num;
58 	uint64_t	type;
59 } ds_snmp_msg_t;
60 
61 typedef	enum {
62 	DS_SNMP_REQUEST	= 0,
63 	DS_SNMP_REPLY	= 1,
64 	DS_SNMP_ERROR = 2
65 } ds_snmp_msg_type_t;
66 
67 typedef enum {
68 	DS_SNMP_READY = 0x0,
69 	DS_SNMP_REQUESTED = 0x1,
70 	DS_SNMP_DATA_AVL = 0x2,
71 	DS_SNMP_DATA_ERR = 0x3
72 } ds_snmp_flags_t;
73 
74 /*
75  * The single mutex 'lock' protects all the SNMP/DS variables in the state
76  * structure.
77  *
78  * The condition variable 'state_cv' helps serialize write() calls for a
79  * single descriptor. When write() is called, it sets a flag to indicate
80  * that an SNMP request has been made to the agent. No more write()'s on
81  * the same open descriptor will be allowed until this flag is cleared via
82  * a matching read(), where the requested packet is consumed on arrival.
83  * Read() then wakes up any waiters blocked in write() for sending the next
84  * SNMP request to the agent.
85  */
86 typedef struct ds_snmp_state {
87 	dev_info_t	*dip;
88 	int		instance;
89 	dev_t		dev;
90 
91 	/* SNMP/DS */
92 	kmutex_t	lock;
93 	kcondvar_t	state_cv;
94 	ds_snmp_flags_t	state;
95 	void		*data;
96 	size_t		data_len;
97 	uint64_t	req_id;
98 	uint64_t	last_req_id;
99 	uint64_t	gencount;
100 	boolean_t	sc_reset;
101 } ds_snmp_state_t;
102 
103 
104 static uint_t		ds_snmp_debug = 0;
105 static void		*ds_snmp_statep = NULL;
106 static int		ds_snmp_instance = -1;
107 static dev_info_t	*ds_snmp_devi = NULL;
108 
109 /*
110  * The ds_snmp_lock mutex protects the following data global to the
111  * driver.
112  *
113  * The ds_snmp_service_cv condition variable is used to resolve the
114  * potential race between the registration of snmp service via a
115  * ds_cap_init() in attach(), the acknowledgement of this registration
116  * at a later time in ds_snmp_reg_handler(), and a possible open() at
117  * a time inbetween. The ds_snmp_has_service and ds_snmp_handle are
118  * used to indicate whether the registration acknowledgement has happened
119  * or not.
120  *
121  * The ds_snmp_minor_pool[] is a bitmask to allocate and keep track of
122  * minor numbers dynamically.
123  */
124 static kmutex_t		ds_snmp_lock;
125 static kcondvar_t	ds_snmp_service_cv;
126 static int		ds_snmp_has_service = B_FALSE;
127 static ds_svc_hdl_t	ds_snmp_handle = DS_INVALID_HDL;
128 static uint64_t		ds_snmp_minor_pool[DS_MINOR_POOL_SZ];	/* bitmask */
129 static int		ds_snmp_num_opens = 0;
130 
131 static int ds_snmp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
132 static int ds_snmp_attach(dev_info_t *, ddi_attach_cmd_t);
133 static int ds_snmp_detach(dev_info_t *, ddi_detach_cmd_t);
134 static int ds_snmp_open(dev_t *, int, int, cred_t *);
135 static int ds_snmp_close(dev_t, int, int, cred_t *);
136 static int ds_snmp_read(dev_t, struct uio *, cred_t *);
137 static int ds_snmp_write(dev_t, struct uio *, cred_t *);
138 static int ds_snmp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
139 
140 /*
141  * DS Callbacks
142  */
143 static void ds_snmp_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
144 static void ds_snmp_unreg_handler(ds_cb_arg_t arg);
145 static void ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
146 
147 /*
148  * SNMP DS capability registration
149  */
150 static ds_ver_t ds_snmp_ver_1_0 = { 1, 0 };
151 static ds_capability_t ds_snmp_cap = {
152 	"snmp",
153 	&ds_snmp_ver_1_0,
154 	1
155 };
156 
157 /*
158  * SNMP DS Client callback vector
159  */
160 static ds_clnt_ops_t ds_snmp_ops = {
161 	ds_snmp_reg_handler,	/* ds_reg_cb */
162 	ds_snmp_unreg_handler,	/* ds_unreg_cb */
163 	ds_snmp_data_handler,	/* ds_data_cb */
164 	NULL			/* cb_arg */
165 };
166 
167 /*
168  * DS SNMP driver Ops Vector
169  */
170 static struct cb_ops ds_snmp_cb_ops = {
171 	ds_snmp_open,		/* cb_open */
172 	ds_snmp_close,		/* cb_close */
173 	nodev,			/* cb_strategy */
174 	nodev,			/* cb_print */
175 	nodev,			/* cb_dump */
176 	ds_snmp_read,		/* cb_read */
177 	ds_snmp_write,		/* cb_write */
178 	ds_snmp_ioctl,		/* cb_ioctl */
179 	nodev,			/* cb_devmap */
180 	nodev,			/* cb_mmap */
181 	nodev,			/* cb_segmap */
182 	nochpoll,		/* cb_chpoll */
183 	ddi_prop_op,		/* cb_prop_op */
184 	(struct streamtab *)NULL, /* cb_str */
185 	D_MP | D_64BIT,		/* cb_flag */
186 	CB_REV,			/* cb_rev */
187 	nodev,			/* cb_aread */
188 	nodev			/* cb_awrite */
189 };
190 
191 static struct dev_ops ds_snmp_dev_ops = {
192 	DEVO_REV,		/* devo_rev */
193 	0,			/* devo_refcnt */
194 	ds_snmp_getinfo,	/* devo_getinfo */
195 	nulldev,		/* devo_identify */
196 	nulldev,		/* devo_probe */
197 	ds_snmp_attach,		/* devo_attach */
198 	ds_snmp_detach,		/* devo_detach */
199 	nodev,			/* devo_reset */
200 	&ds_snmp_cb_ops,	/* devo_cb_ops */
201 	(struct bus_ops *)NULL,	/* devo_bus_ops */
202 	nulldev,		/* devo_power */
203 	ddi_quiesce_not_needed,		/* devo_quiesce */
204 };
205 
206 static struct modldrv modldrv = {
207 	&mod_driverops,
208 	"Domain Services SNMP Driver",
209 	&ds_snmp_dev_ops
210 };
211 
212 static struct modlinkage modlinkage = {
213 	MODREV_1,
214 	(void *)&modldrv,
215 	NULL
216 };
217 
218 int
219 _init(void)
220 {
221 	int retval;
222 
223 	mutex_init(&ds_snmp_lock, NULL, MUTEX_DRIVER, NULL);
224 	cv_init(&ds_snmp_service_cv, NULL, CV_DRIVER, NULL);
225 
226 	retval = ddi_soft_state_init(&ds_snmp_statep,
227 	    sizeof (ds_snmp_state_t), DS_SNMP_MAX_OPENS);
228 	if (retval != 0) {
229 		cv_destroy(&ds_snmp_service_cv);
230 		mutex_destroy(&ds_snmp_lock);
231 		return (retval);
232 	}
233 
234 	retval = mod_install(&modlinkage);
235 	if (retval != 0) {
236 		ddi_soft_state_fini(&ds_snmp_statep);
237 		cv_destroy(&ds_snmp_service_cv);
238 		mutex_destroy(&ds_snmp_lock);
239 	}
240 
241 	return (retval);
242 }
243 
244 int
245 _info(struct modinfo *modinfop)
246 {
247 	return (mod_info(&modlinkage, modinfop));
248 }
249 
250 int
251 _fini(void)
252 {
253 	int retval;
254 
255 	if ((retval = mod_remove(&modlinkage)) != 0)
256 		return (retval);
257 
258 	ddi_soft_state_fini(&ds_snmp_statep);
259 
260 	cv_destroy(&ds_snmp_service_cv);
261 	mutex_destroy(&ds_snmp_lock);
262 
263 	return (retval);
264 }
265 
266 /*ARGSUSED*/
267 static int
268 ds_snmp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
269 {
270 	ds_snmp_state_t *sp;
271 	int retval = DDI_FAILURE;
272 
273 	ASSERT(resultp != NULL);
274 
275 	switch (cmd) {
276 	case DDI_INFO_DEVT2DEVINFO:
277 		sp = ddi_get_soft_state(ds_snmp_statep, getminor((dev_t)arg));
278 		if (sp != NULL) {
279 			*resultp = sp->dip;
280 			retval = DDI_SUCCESS;
281 		} else
282 			*resultp = NULL;
283 		break;
284 
285 	case DDI_INFO_DEVT2INSTANCE:
286 		*resultp = (void *)(uintptr_t)getminor((dev_t)arg);
287 		retval = DDI_SUCCESS;
288 		break;
289 	}
290 
291 	return (retval);
292 }
293 
294 static int
295 ds_snmp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
296 {
297 	int	rv;
298 
299 	switch (cmd) {
300 	case DDI_ATTACH:
301 		if (ds_snmp_instance != -1)
302 			return (DDI_FAILURE);
303 		break;
304 
305 	case DDI_RESUME:
306 		return (DDI_SUCCESS);
307 
308 	default:
309 		return (DDI_FAILURE);
310 	}
311 
312 	ds_snmp_instance = ddi_get_instance(dip);
313 	if (ddi_create_minor_node(dip, DS_SNMP_NAME, S_IFCHR, ds_snmp_instance,
314 	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
315 		cmn_err(CE_WARN, "%s@%d: Unable to create minor node",
316 		    DS_SNMP_NAME, ds_snmp_instance);
317 		return (DDI_FAILURE);
318 	}
319 
320 	bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
321 
322 	ds_snmp_ops.cb_arg = dip;
323 	if ((rv = ds_cap_init(&ds_snmp_cap, &ds_snmp_ops)) != 0) {
324 		cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
325 		ddi_remove_minor_node(dip, NULL);
326 		ds_snmp_instance = -1;
327 		return (DDI_FAILURE);
328 	}
329 
330 	ds_snmp_devi = dip;
331 	ddi_report_dev(dip);
332 
333 	return (DDI_SUCCESS);
334 }
335 
336 /*ARGSUSED*/
337 static int
338 ds_snmp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
339 {
340 	switch (cmd) {
341 	case DDI_DETACH:
342 		if (ds_snmp_instance == -1)
343 			return (DDI_FAILURE);
344 		break;
345 
346 	case DDI_SUSPEND:
347 		return (DDI_SUCCESS);
348 
349 	default:
350 		return (DDI_FAILURE);
351 	}
352 
353 	(void) ds_cap_fini(&ds_snmp_cap);
354 
355 	ddi_remove_minor_node(ds_snmp_devi, NULL);
356 	bzero(ds_snmp_minor_pool, DS_MINOR_POOL_SZ * sizeof (uint64_t));
357 
358 	ds_snmp_instance = -1;
359 	ds_snmp_devi = NULL;
360 
361 	return (DDI_SUCCESS);
362 }
363 
364 static minor_t
365 ds_snmp_get_minor(void)
366 {
367 	uint64_t	val;
368 	int		i, ndx;
369 	minor_t		minor;
370 
371 	mutex_enter(&ds_snmp_lock);
372 	for (ndx = 0; ndx < DS_MINOR_POOL_SZ; ndx++) {
373 		val = ds_snmp_minor_pool[ndx];
374 		for (i = 0; i < DS_BITS_IN_UINT64; i++) {
375 			if ((val & 0x1) == 0) {
376 				ds_snmp_minor_pool[ndx] |= ((uint64_t)1 << i);
377 				ds_snmp_num_opens++;
378 				mutex_exit(&ds_snmp_lock);
379 
380 				minor = ndx * DS_BITS_IN_UINT64 + i + 1;
381 
382 				return (minor);
383 			}
384 			val >>= 1;
385 		}
386 	}
387 	mutex_exit(&ds_snmp_lock);
388 
389 	return (0);
390 }
391 
392 static void
393 ds_snmp_rel_minor(minor_t minor)
394 {
395 	int	i, ndx;
396 
397 	ndx = (minor - 1) / DS_BITS_IN_UINT64;
398 	i = (minor - 1) % DS_BITS_IN_UINT64;
399 
400 	ASSERT(ndx < DS_MINOR_POOL_SZ);
401 
402 	mutex_enter(&ds_snmp_lock);
403 
404 	ds_snmp_num_opens--;
405 	ds_snmp_minor_pool[ndx] &= ~((uint64_t)1 << i);
406 
407 	mutex_exit(&ds_snmp_lock);
408 }
409 
410 static boolean_t
411 ds_snmp_is_open(minor_t minor)
412 {
413 	uint64_t	val;
414 	int		i, ndx;
415 
416 	ndx = (minor - 1) / DS_BITS_IN_UINT64;
417 	i = (minor - 1) % DS_BITS_IN_UINT64;
418 
419 	val = ((uint64_t)1 << i);
420 	if (ds_snmp_minor_pool[ndx] & val)
421 		return (B_TRUE);
422 	else
423 		return (B_FALSE);
424 }
425 
426 static int
427 ds_snmp_create_state(dev_t *devp)
428 {
429 	major_t	major;
430 	minor_t	minor;
431 	ds_snmp_state_t	*sp;
432 
433 	if ((minor = ds_snmp_get_minor()) == 0)
434 		return (EMFILE);
435 
436 	if (ddi_soft_state_zalloc(ds_snmp_statep, minor) != DDI_SUCCESS) {
437 		cmn_err(CE_WARN, "%s@%d: Unable to allocate state",
438 		    DS_SNMP_NAME, minor);
439 		ds_snmp_rel_minor(minor);
440 		return (ENOMEM);
441 	}
442 
443 	sp = ddi_get_soft_state(ds_snmp_statep, minor);
444 	if (devp != NULL)
445 		major = getemajor(*devp);
446 	else
447 		major = ddi_driver_major(ds_snmp_devi);
448 
449 	sp->dev = makedevice(major, minor);
450 	if (devp != NULL)
451 		*devp = sp->dev;
452 
453 	sp->instance = minor;
454 	sp->data = NULL;
455 	sp->data_len = 0;
456 	sp->req_id = 0;
457 	sp->last_req_id = 0;
458 	sp->state = DS_SNMP_READY;
459 	sp->sc_reset = B_FALSE;
460 
461 	mutex_init(&sp->lock, NULL, MUTEX_DRIVER, NULL);
462 	cv_init(&sp->state_cv, NULL, CV_DRIVER, NULL);
463 
464 	return (0);
465 }
466 
467 static int
468 ds_snmp_destroy_state(dev_t dev)
469 {
470 	ds_snmp_state_t	*sp;
471 	minor_t	minor;
472 
473 	minor = getminor(dev);
474 
475 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
476 		return (ENXIO);
477 
478 	ASSERT(sp->instance == minor);
479 
480 	/*
481 	 * If the app has not exited cleanly, the data may not have been
482 	 * read/memory freed, hence take care of that here
483 	 */
484 	if (sp->data) {
485 		kmem_free(sp->data, sp->data_len);
486 	}
487 	cv_destroy(&sp->state_cv);
488 	mutex_destroy(&sp->lock);
489 
490 	ddi_soft_state_free(ds_snmp_statep, minor);
491 	ds_snmp_rel_minor(minor);
492 
493 	return (0);
494 }
495 
496 /*ARGSUSED*/
497 static int
498 ds_snmp_open(dev_t *devp, int flag, int otyp, cred_t *credp)
499 {
500 
501 	if (otyp != OTYP_CHR)
502 		return (EINVAL);
503 
504 	if (ds_snmp_instance == -1)
505 		return (ENXIO);
506 
507 	/*
508 	 * Avoid possible race condition - ds service may not be there yet
509 	 */
510 	mutex_enter(&ds_snmp_lock);
511 	while (ds_snmp_has_service == B_FALSE) {
512 		if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
513 			mutex_exit(&ds_snmp_lock);
514 			return (EINTR);
515 		}
516 	}
517 	mutex_exit(&ds_snmp_lock);
518 
519 	return (ds_snmp_create_state(devp));
520 }
521 
522 
523 /*ARGSUSED*/
524 static int
525 ds_snmp_close(dev_t dev, int flag, int otyp, cred_t *credp)
526 {
527 	if (otyp != OTYP_CHR)
528 		return (EINVAL);
529 
530 	if (ds_snmp_instance == -1)
531 		return (ENXIO);
532 
533 	if (ds_snmp_handle == DS_INVALID_HDL)
534 		return (EIO);
535 
536 	return (ds_snmp_destroy_state(dev));
537 }
538 
539 /*ARGSUSED*/
540 static int
541 ds_snmp_read(dev_t dev, struct uio *uiop, cred_t *credp)
542 {
543 	ds_snmp_state_t *sp;
544 	minor_t	minor;
545 	size_t len;
546 	int retval;
547 	caddr_t tmpbufp = (caddr_t)NULL;
548 
549 	/*
550 	 * Given that now we can have sc resets happening at any
551 	 * time, it is possible that it happened since the last time
552 	 * we issued a read, write or ioctl.  If so, we need to wait
553 	 * for the unreg-reg pair to complete before we can do
554 	 * anything.
555 	 */
556 	mutex_enter(&ds_snmp_lock);
557 	while (ds_snmp_has_service == B_FALSE) {
558 		DS_SNMP_DBG("ds_snmp_read: waiting for service\n");
559 		if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
560 			mutex_exit(&ds_snmp_lock);
561 			return (EINTR);
562 		}
563 	}
564 	mutex_exit(&ds_snmp_lock);
565 
566 	if ((len = uiop->uio_resid) == 0)
567 		return (0);
568 
569 	minor = getminor(dev);
570 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
571 		return (ENXIO);
572 
573 	mutex_enter(&sp->lock);
574 
575 	if (sp->sc_reset == B_TRUE) {
576 		mutex_exit(&sp->lock);
577 		return (ECANCELED);
578 	}
579 
580 	/*
581 	 * Block or bail if there is no SNMP data
582 	 */
583 	if (sp->state != DS_SNMP_DATA_AVL && sp->state != DS_SNMP_DATA_ERR) {
584 		DS_SNMP_DBG("ds_snmp_read: no SNMP data\n");
585 		if (uiop->uio_fmode & (FNDELAY | FNONBLOCK)) {
586 			mutex_exit(&sp->lock);
587 			return (EAGAIN);
588 		}
589 		while (sp->state != DS_SNMP_DATA_AVL &&
590 		    sp->state != DS_SNMP_DATA_ERR) {
591 			if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
592 				mutex_exit(&sp->lock);
593 				return (EINTR);
594 			}
595 		}
596 	}
597 
598 	/*
599 	 * If there has been an error, it could be because the agent
600 	 * returned failure and there is no data to read, or an ldc-reset
601 	 * has happened.  Figure out which and return appropriate
602 	 * error to the caller.
603 	 */
604 	if (sp->state == DS_SNMP_DATA_ERR) {
605 		if (sp->sc_reset == B_TRUE) {
606 			mutex_exit(&sp->lock);
607 			DS_SNMP_DBG("ds_snmp_read: sc got reset, "
608 			    "returning ECANCELED\n");
609 			return (ECANCELED);
610 		} else {
611 			sp->state = DS_SNMP_READY;
612 			cv_broadcast(&sp->state_cv);
613 			mutex_exit(&sp->lock);
614 			DS_SNMP_DBG("ds_snmp_read: data error, "
615 			    "returning EIO\n");
616 			return (EIO);
617 		}
618 	}
619 
620 	if (len > sp->data_len)
621 		len = sp->data_len;
622 
623 	tmpbufp = kmem_alloc(len, KM_SLEEP);
624 
625 	bcopy(sp->data, (void *)tmpbufp, len);
626 	kmem_free(sp->data, sp->data_len);
627 	sp->data = (caddr_t)NULL;
628 	sp->data_len = 0;
629 
630 	/*
631 	 * SNMP data has been consumed, wake up anyone waiting to send
632 	 */
633 	sp->state = DS_SNMP_READY;
634 	cv_broadcast(&sp->state_cv);
635 
636 	mutex_exit(&sp->lock);
637 
638 	retval = uiomove(tmpbufp, len, UIO_READ, uiop);
639 	kmem_free(tmpbufp, len);
640 
641 	return (retval);
642 }
643 
644 /*ARGSUSED*/
645 static int
646 ds_snmp_write(dev_t dev, struct uio *uiop, cred_t *credp)
647 {
648 	ds_snmp_state_t *sp;
649 	ds_snmp_msg_t hdr;
650 	minor_t minor;
651 	size_t len;
652 	caddr_t tmpbufp;
653 
654 	/*
655 	 * Check if there was an sc reset; if yes, wait until we have the
656 	 * service back again.
657 	 */
658 	mutex_enter(&ds_snmp_lock);
659 	while (ds_snmp_has_service == B_FALSE) {
660 		DS_SNMP_DBG("ds_snmp_write: waiting for service\n");
661 		if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
662 			mutex_exit(&ds_snmp_lock);
663 			return (EINTR);
664 		}
665 	}
666 	mutex_exit(&ds_snmp_lock);
667 
668 	minor = getminor(dev);
669 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
670 		return (ENXIO);
671 
672 	len = uiop->uio_resid + sizeof (ds_snmp_msg_t);
673 	tmpbufp = kmem_alloc(len, KM_SLEEP);
674 
675 	if (uiomove(tmpbufp + sizeof (ds_snmp_msg_t),
676 	    len - sizeof (ds_snmp_msg_t), UIO_WRITE, uiop) != 0) {
677 		kmem_free(tmpbufp, len);
678 		return (EIO);
679 	}
680 
681 	mutex_enter(&sp->lock);
682 
683 	if (sp->sc_reset == B_TRUE) {
684 		mutex_exit(&sp->lock);
685 		kmem_free(tmpbufp, len);
686 		DS_SNMP_DBG("ds_snmp_write: sc_reset is TRUE, "
687 		    "returning ECANCELD\n");
688 		return (ECANCELED);
689 	}
690 
691 	/*
692 	 * wait if earlier transaction is not yet completed
693 	 */
694 	while (sp->state != DS_SNMP_READY) {
695 		if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
696 			mutex_exit(&sp->lock);
697 			kmem_free(tmpbufp, len);
698 			return (EINTR);
699 		}
700 		/*
701 		 * Normally, only a reader would ever wake us up. But if we
702 		 * did get signalled with an ERROR, it could only mean there
703 		 * was an sc reset and there's no point waiting; we need to
704 		 * fail this write().
705 		 */
706 		if (sp->state == DS_SNMP_DATA_ERR && sp->sc_reset == B_TRUE) {
707 			DS_SNMP_DBG("ds_snmp_write: woke up with an sc_reset, "
708 			    "returning ECANCELED\n");
709 			mutex_exit(&sp->lock);
710 			kmem_free(tmpbufp, len);
711 			return (ECANCELED);
712 		}
713 	}
714 
715 	if (sp->req_id == (((uint64_t)1 << DS_SNMP_MINOR_SHIFT) - 1))
716 		sp->req_id = 0; /* Reset */
717 
718 	hdr.seq_num = ((uint64_t)minor << DS_SNMP_MINOR_SHIFT) | sp->req_id;
719 	sp->last_req_id = hdr.seq_num;
720 	(sp->req_id)++;
721 
722 	/*
723 	 * Set state to SNMP_REQUESTED, but don't wakeup anyone yet
724 	 */
725 	sp->state = DS_SNMP_REQUESTED;
726 
727 	mutex_exit(&sp->lock);
728 
729 	hdr.type = DS_SNMP_REQUEST;
730 	bcopy((void *)&hdr, (void *)tmpbufp, sizeof (hdr));
731 
732 	/*
733 	 * If the service went away since the time we entered this
734 	 * routine and now, tough luck. Just ignore the current
735 	 * write() and return.
736 	 */
737 	mutex_enter(&ds_snmp_lock);
738 	if (ds_snmp_has_service == B_FALSE) {
739 		DS_SNMP_DBG("ds_snmp_write: service went away, aborting "
740 		    "write, returning ECANCELED\n");
741 		mutex_exit(&ds_snmp_lock);
742 		kmem_free(tmpbufp, len);
743 		return (ECANCELED);
744 	}
745 	DS_SNMP_DBG("ds_snmp_write: ds_cap_send(0x%lx, %lu) called.\n",
746 	    ds_snmp_handle, len);
747 	(void) ds_cap_send(ds_snmp_handle, tmpbufp, len);
748 	mutex_exit(&ds_snmp_lock);
749 
750 	kmem_free(tmpbufp, len);
751 
752 	return (0);
753 }
754 
755 /*ARGSUSED*/
756 static int
757 ds_snmp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
758     int *rvalp)
759 {
760 	ds_snmp_state_t *sp;
761 	struct dssnmp_info info;
762 	minor_t	minor;
763 
764 	/*
765 	 * Check if there was an sc reset; if yes, wait until we have the
766 	 * service back again.
767 	 */
768 	mutex_enter(&ds_snmp_lock);
769 	while (ds_snmp_has_service == B_FALSE) {
770 		DS_SNMP_DBG("ds_snmp_ioctl: waiting for service\n");
771 		if (cv_wait_sig(&ds_snmp_service_cv, &ds_snmp_lock) == 0) {
772 			mutex_exit(&ds_snmp_lock);
773 			return (EINTR);
774 		}
775 	}
776 	mutex_exit(&ds_snmp_lock);
777 
778 	DS_SNMP_DBG("ds_snmp_ioctl: hdl=0x%lx\n", ds_snmp_handle);
779 
780 	minor = getminor(dev);
781 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
782 		return (ENXIO);
783 
784 	if (!(mode & FREAD))
785 		return (EACCES);
786 
787 	switch (cmd) {
788 	case DSSNMP_GETINFO:
789 		mutex_enter(&sp->lock);
790 
791 		if (sp->sc_reset == B_TRUE) {
792 			mutex_exit(&sp->lock);
793 			DS_SNMP_DBG("ds_snmp_ioctl: returning ECANCELED\n");
794 			return (ECANCELED);
795 		}
796 
797 		while (sp->state != DS_SNMP_DATA_AVL &&
798 		    sp->state != DS_SNMP_DATA_ERR) {
799 			DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
800 			    "waiting for data\n", sp->state, sp->sc_reset);
801 			if (cv_wait_sig(&sp->state_cv, &sp->lock) == 0) {
802 				sp->state = DS_SNMP_READY;
803 				mutex_exit(&sp->lock);
804 				return (EINTR);
805 			}
806 		}
807 		DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
808 		    "out of wait!\n", sp->state, sp->sc_reset);
809 
810 		/*
811 		 * If there has been an error, it could be because the
812 		 * agent returned failure and there is no data to read,
813 		 * or an ldc-reset has happened.  Figure out which and
814 		 * return appropriate error to the caller.
815 		 */
816 		if (sp->state == DS_SNMP_DATA_ERR) {
817 			if (sp->sc_reset == B_TRUE) {
818 				mutex_exit(&sp->lock);
819 				DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=TRUE "
820 				    "returning ECANCELED\n");
821 				return (ECANCELED);
822 			} else {
823 				sp->state = DS_SNMP_READY;
824 				cv_broadcast(&sp->state_cv);
825 				mutex_exit(&sp->lock);
826 				DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=FALSE "
827 				    "returning EIO\n");
828 				return (EIO);
829 			}
830 		}
831 
832 		info.size = sp->data_len;
833 		info.token = sp->gencount;
834 
835 		mutex_exit(&sp->lock);
836 
837 		if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0)
838 			return (EFAULT);
839 		break;
840 
841 	case DSSNMP_CLRLNKRESET:
842 		mutex_enter(&sp->lock);
843 
844 		DS_SNMP_DBG("ds_snmp_ioctl: DSSNMP_CLRLNKRESET\n");
845 		DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=%d\n", sp->sc_reset);
846 
847 		if (sp->sc_reset == B_TRUE) {
848 			if (sp->data) {
849 				DS_SNMP_DBG("ds_snmp_ioctl: data=%p, len=%lu\n",
850 				    sp->data, sp->data_len);
851 				kmem_free(sp->data, sp->data_len);
852 			}
853 			sp->data = NULL;
854 			sp->data_len = 0;
855 			sp->state = DS_SNMP_READY;
856 			sp->req_id = 0;
857 			sp->last_req_id = 0;
858 			sp->sc_reset = B_FALSE;
859 		}
860 		mutex_exit(&sp->lock);
861 		break;
862 
863 	default:
864 		return (ENOTTY);
865 	}
866 
867 	return (0);
868 }
869 
870 /*
871  * DS Callbacks
872  */
873 /*ARGSUSED*/
874 static void
875 ds_snmp_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
876 {
877 	DS_SNMP_DBG("ds_snmp_reg_handler: registering handle 0x%lx for version "
878 	    "0x%x:0x%x\n", (uint64_t)hdl, ver->major, ver->minor);
879 
880 	mutex_enter(&ds_snmp_lock);
881 
882 	ASSERT(ds_snmp_handle == DS_INVALID_HDL);
883 
884 	ds_snmp_handle = hdl;
885 	ds_snmp_has_service = B_TRUE;
886 
887 	cv_broadcast(&ds_snmp_service_cv);
888 
889 	mutex_exit(&ds_snmp_lock);
890 
891 }
892 
893 /*ARGSUSED*/
894 static void
895 ds_snmp_unreg_handler(ds_cb_arg_t arg)
896 {
897 	minor_t minor;
898 	ds_snmp_state_t *sp;
899 
900 	DS_SNMP_DBG("ds_snmp_unreg_handler: un-registering ds_snmp service\n");
901 
902 	mutex_enter(&ds_snmp_lock);
903 
904 	if (ds_snmp_num_opens) {
905 		DS_SNMP_DBG("ds_snmp_unreg_handler: %d opens, sc reset!\n",
906 		    ds_snmp_num_opens);
907 		for (minor = 1; minor <= DS_SNMP_MAX_OPENS; minor++) {
908 			if (ds_snmp_is_open(minor)) {
909 				DS_SNMP_DBG("ds_snmp_unreg_handler: minor %d "
910 				    "open\n", minor);
911 				sp = ddi_get_soft_state(ds_snmp_statep, minor);
912 				if (sp == NULL)
913 					continue;
914 
915 				/*
916 				 * Set the sc_reset flag and break any waiters
917 				 * out of their existing reads/writes/ioctls.
918 				 */
919 				DS_SNMP_DBG("ds_snmp_unreg_hdlr: about to "
920 				    "signal waiters\n");
921 				mutex_enter(&sp->lock);
922 				sp->sc_reset = B_TRUE;
923 				sp->state = DS_SNMP_DATA_ERR;
924 				cv_broadcast(&sp->state_cv);
925 				mutex_exit(&sp->lock);
926 			}
927 		}
928 	}
929 
930 	ds_snmp_handle = DS_INVALID_HDL;
931 	ds_snmp_has_service = B_FALSE;
932 
933 	DS_SNMP_DBG("ds_snmp_unreg_handler: handle invalidated\n");
934 
935 	mutex_exit(&ds_snmp_lock);
936 }
937 
938 /*ARGSUSED*/
939 static void
940 ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
941 {
942 	ds_snmp_state_t *sp;
943 	ds_snmp_msg_t   hdr;
944 	size_t  	snmp_size;
945 	minor_t 	minor;
946 
947 	/*
948 	 * Make sure the header is at least valid
949 	 */
950 	if (buflen < sizeof (hdr)) {
951 		cmn_err(CE_WARN,
952 		"ds_snmp_data_handler: buflen <%lu> too small", buflen);
953 		return;
954 	}
955 
956 	ASSERT(buf != NULL);
957 	bcopy(buf, (void *)&hdr, sizeof (hdr));
958 
959 	DS_SNMP_DBG("ds_snmp_data_handler: msg buf len 0x%lx : type 0x%lx, "
960 	    "seqn 0x%lx\n", buflen, hdr.type, hdr.seq_num);
961 
962 	minor = (int)(hdr.seq_num >> DS_SNMP_MINOR_SHIFT);
963 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
964 		return;
965 
966 	mutex_enter(&sp->lock);
967 
968 	/*
969 	 * If there is no pending SNMP request, then we've received
970 	 * bogus data or an SNMP trap or the reader was interrupted.
971 	 * Since we don't yet support SNMP traps, ignore it.
972 	 */
973 	if (sp->state != DS_SNMP_REQUESTED) {
974 		DS_SNMP_DBG("Received SNMP data without request");
975 		mutex_exit(&sp->lock);
976 		return;
977 	}
978 
979 	/*
980 	 * Response to a request therefore old SNMP must've been consumed
981 	 */
982 	ASSERT(sp->data_len == 0);
983 	ASSERT(sp->data == NULL);
984 
985 	/*
986 	 * Response seq_num should match our request seq_num
987 	 */
988 	if (hdr.seq_num != sp->last_req_id) {
989 		cmn_err(CE_WARN, "Received DS snmp data out of sequence with "
990 		    "request");
991 		mutex_exit(&sp->lock);
992 		return;
993 	}
994 
995 	if (hdr.type == DS_SNMP_ERROR) {
996 		sp->state = DS_SNMP_DATA_ERR;
997 		DS_SNMP_DBG("ds_snmp_data_handler: hdr.type = DS_SNMP_ERROR\n");
998 	} else {
999 		snmp_size = buflen - sizeof (ds_snmp_msg_t);
1000 		sp->data = kmem_alloc(snmp_size, KM_SLEEP);
1001 		sp->data_len = snmp_size;
1002 		sp->state = DS_SNMP_DATA_AVL;
1003 
1004 		bcopy((caddr_t)buf + sizeof (ds_snmp_msg_t),
1005 		    sp->data, sp->data_len);
1006 	}
1007 
1008 	sp->gencount++;
1009 
1010 	/*
1011 	 * Wake up any readers waiting for data
1012 	 */
1013 	cv_broadcast(&sp->state_cv);
1014 	mutex_exit(&sp->lock);
1015 }
1016