xref: /titanic_41/usr/src/uts/sun4v/io/ds_snmp.c (revision 2449e17f82f6097fd2c665b64723e31ceecbeca6)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * sun4v domain services SNMP driver
30  */
31 
32 #include <sys/types.h>
33 #include <sys/file.h>
34 #include <sys/errno.h>
35 #include <sys/open.h>
36 #include <sys/cred.h>
37 #include <sys/uio.h>
38 #include <sys/stat.h>
39 #include <sys/ksynch.h>
40 #include <sys/modctl.h>
41 #include <sys/conf.h>
42 #include <sys/devops.h>
43 #include <sys/debug.h>
44 #include <sys/cmn_err.h>
45 #include <sys/ddi.h>
46 #include <sys/sunddi.h>
47 #include <sys/ds.h>
48 #include <sys/ds_snmp.h>
49 
50 #define	DS_SNMP_NAME		"ds_snmp"
51 #define	DS_SNMP_MAX_OPENS	256
52 #define	DS_BITS_IN_UINT64	64
53 #define	DS_MINOR_POOL_SZ	(DS_SNMP_MAX_OPENS / DS_BITS_IN_UINT64)
54 #define	DS_SNMP_MINOR_SHIFT	56
55 #define	DS_SNMP_DBG		if (ds_snmp_debug) printf
56 
57 typedef	struct {
58 	uint64_t	seq_num;
59 	uint64_t	type;
60 } ds_snmp_msg_t;
61 
62 typedef	enum {
63 	DS_SNMP_REQUEST	= 0,
64 	DS_SNMP_REPLY	= 1,
65 	DS_SNMP_ERROR = 2
66 } ds_snmp_msg_type_t;
67 
68 typedef enum {
69 	DS_SNMP_READY = 0x0,
70 	DS_SNMP_REQUESTED = 0x1,
71 	DS_SNMP_DATA_AVL = 0x2,
72 	DS_SNMP_DATA_ERR = 0x3
73 } ds_snmp_flags_t;
74 
75 /*
76  * The single mutex 'lock' protects all the SNMP/DS variables in the state
77  * structure.
78  *
79  * The condition variable 'state_cv' helps serialize write() calls for a
80  * single descriptor. When write() is called, it sets a flag to indicate
81  * that an SNMP request has been made to the agent. No more write()'s on
82  * the same open descriptor will be allowed until this flag is cleared via
83  * a matching read(), where the requested packet is consumed on arrival.
84  * Read() then wakes up any waiters blocked in write() for sending the next
85  * SNMP request to the agent.
86  */
87 typedef struct ds_snmp_state {
88 	dev_info_t	*dip;
89 	int		instance;
90 	dev_t		dev;
91 
92 	/* SNMP/DS */
93 	kmutex_t	lock;
94 	kcondvar_t	state_cv;
95 	ds_snmp_flags_t	state;
96 	void		*data;
97 	size_t		data_len;
98 	uint64_t	req_id;
99 	uint64_t	last_req_id;
100 	uint64_t	gencount;
101 	boolean_t	sc_reset;
102 } ds_snmp_state_t;
103 
104 
105 static uint_t		ds_snmp_debug = 0;
106 static void		*ds_snmp_statep = NULL;
107 static int		ds_snmp_instance = -1;
108 static dev_info_t	*ds_snmp_devi = NULL;
109 
110 /*
111  * The ds_snmp_lock mutex protects the following data global to the
112  * driver.
113  *
114  * The ds_snmp_service_cv condition variable is used to resolve the
115  * potential race between the registration of snmp service via a
116  * ds_cap_init() in attach(), the acknowledgement of this registration
117  * at a later time in ds_snmp_reg_handler(), and a possible open() at
118  * a time inbetween. The ds_snmp_has_service and ds_snmp_handle are
119  * used to indicate whether the registration acknowledgement has happened
120  * or not.
121  *
122  * The ds_snmp_minor_pool[] is a bitmask to allocate and keep track of
123  * minor numbers dynamically.
124  */
125 static kmutex_t		ds_snmp_lock;
126 static kcondvar_t	ds_snmp_service_cv;
127 static int		ds_snmp_has_service = B_FALSE;
128 static ds_svc_hdl_t	ds_snmp_handle = DS_INVALID_HDL;
129 static uint64_t		ds_snmp_minor_pool[DS_MINOR_POOL_SZ];	/* bitmask */
130 static int		ds_snmp_num_opens = 0;
131 
132 static int ds_snmp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
133 static int ds_snmp_attach(dev_info_t *, ddi_attach_cmd_t);
134 static int ds_snmp_detach(dev_info_t *, ddi_detach_cmd_t);
135 static int ds_snmp_open(dev_t *, int, int, cred_t *);
136 static int ds_snmp_close(dev_t, int, int, cred_t *);
137 static int ds_snmp_read(dev_t, struct uio *, cred_t *);
138 static int ds_snmp_write(dev_t, struct uio *, cred_t *);
139 static int ds_snmp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
140 
141 /*
142  * DS Callbacks
143  */
144 static void ds_snmp_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
145 static void ds_snmp_unreg_handler(ds_cb_arg_t arg);
146 static void ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
147 
148 /*
149  * SNMP DS capability registration
150  */
151 static ds_ver_t ds_snmp_ver_1_0 = { 1, 0 };
152 static ds_capability_t ds_snmp_cap = {
153 	"snmp",
154 	&ds_snmp_ver_1_0,
155 	1
156 };
157 
158 /*
159  * SNMP DS Client callback vector
160  */
161 static ds_clnt_ops_t ds_snmp_ops = {
162 	ds_snmp_reg_handler,	/* ds_reg_cb */
163 	ds_snmp_unreg_handler,	/* ds_unreg_cb */
164 	ds_snmp_data_handler,	/* ds_data_cb */
165 	NULL			/* cb_arg */
166 };
167 
168 /*
169  * DS SNMP driver Ops Vector
170  */
171 static struct cb_ops ds_snmp_cb_ops = {
172 	ds_snmp_open,		/* cb_open */
173 	ds_snmp_close,		/* cb_close */
174 	nodev,			/* cb_strategy */
175 	nodev,			/* cb_print */
176 	nodev,			/* cb_dump */
177 	ds_snmp_read,		/* cb_read */
178 	ds_snmp_write,		/* cb_write */
179 	ds_snmp_ioctl,		/* cb_ioctl */
180 	nodev,			/* cb_devmap */
181 	nodev,			/* cb_mmap */
182 	nodev,			/* cb_segmap */
183 	nochpoll,		/* cb_chpoll */
184 	ddi_prop_op,		/* cb_prop_op */
185 	(struct streamtab *)NULL, /* cb_str */
186 	D_MP | D_64BIT,		/* cb_flag */
187 	CB_REV,			/* cb_rev */
188 	nodev,			/* cb_aread */
189 	nodev			/* cb_awrite */
190 };
191 
192 static struct dev_ops ds_snmp_dev_ops = {
193 	DEVO_REV,		/* devo_rev */
194 	0,			/* devo_refcnt */
195 	ds_snmp_getinfo,	/* devo_getinfo */
196 	nulldev,		/* devo_identify */
197 	nulldev,		/* devo_probe */
198 	ds_snmp_attach,		/* devo_attach */
199 	ds_snmp_detach,		/* devo_detach */
200 	nodev,			/* devo_reset */
201 	&ds_snmp_cb_ops,	/* devo_cb_ops */
202 	(struct bus_ops *)NULL,	/* devo_bus_ops */
203 	nulldev			/* devo_power */
204 };
205 
206 static struct modldrv modldrv = {
207 	&mod_driverops,
208 	"Domain Services SNMP Driver 1.0",
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 				mutex_exit(&sp->lock);
803 				return (EINTR);
804 			}
805 		}
806 		DS_SNMP_DBG("ds_snmp_ioctl: state=%d, sc_reset=%d, "
807 		    "out of wait!\n", sp->state, sp->sc_reset);
808 
809 		/*
810 		 * If there has been an error, it could be because the
811 		 * agent returned failure and there is no data to read,
812 		 * or an ldc-reset has happened.  Figure out which and
813 		 * return appropriate error to the caller.
814 		 */
815 		if (sp->state == DS_SNMP_DATA_ERR) {
816 			if (sp->sc_reset == B_TRUE) {
817 				mutex_exit(&sp->lock);
818 				DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=TRUE "
819 				    "returning ECANCELED\n");
820 				return (ECANCELED);
821 			} else {
822 				sp->state = DS_SNMP_READY;
823 				cv_broadcast(&sp->state_cv);
824 				mutex_exit(&sp->lock);
825 				DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=FALSE "
826 				    "returning EIO\n");
827 				return (EIO);
828 			}
829 		}
830 
831 		info.size = sp->data_len;
832 		info.token = sp->gencount;
833 
834 		mutex_exit(&sp->lock);
835 
836 		if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0)
837 			return (EFAULT);
838 		break;
839 
840 	case DSSNMP_CLRLNKRESET:
841 		mutex_enter(&sp->lock);
842 
843 		DS_SNMP_DBG("ds_snmp_ioctl: DSSNMP_CLRLNKRESET\n");
844 		DS_SNMP_DBG("ds_snmp_ioctl: sc_reset=%d\n", sp->sc_reset);
845 
846 		if (sp->sc_reset == B_TRUE) {
847 			if (sp->data) {
848 				DS_SNMP_DBG("ds_snmp_ioctl: data=%p, len=%lu\n",
849 				    sp->data, sp->data_len);
850 				kmem_free(sp->data, sp->data_len);
851 			}
852 			sp->data = NULL;
853 			sp->data_len = 0;
854 			sp->state = DS_SNMP_READY;
855 			sp->req_id = 0;
856 			sp->last_req_id = 0;
857 			sp->sc_reset = B_FALSE;
858 		}
859 		mutex_exit(&sp->lock);
860 		break;
861 
862 	default:
863 		return (ENOTTY);
864 	}
865 
866 	return (0);
867 }
868 
869 /*
870  * DS Callbacks
871  */
872 /*ARGSUSED*/
873 static void
874 ds_snmp_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
875 {
876 	DS_SNMP_DBG("ds_snmp_reg_handler: registering handle 0x%lx for version "
877 	    "0x%x:0x%x\n", (uint64_t)hdl, ver->major, ver->minor);
878 
879 	mutex_enter(&ds_snmp_lock);
880 
881 	ASSERT(ds_snmp_handle == DS_INVALID_HDL);
882 
883 	ds_snmp_handle = hdl;
884 	ds_snmp_has_service = B_TRUE;
885 
886 	cv_broadcast(&ds_snmp_service_cv);
887 
888 	mutex_exit(&ds_snmp_lock);
889 
890 }
891 
892 /*ARGSUSED*/
893 static void
894 ds_snmp_unreg_handler(ds_cb_arg_t arg)
895 {
896 	minor_t minor;
897 	ds_snmp_state_t *sp;
898 
899 	DS_SNMP_DBG("ds_snmp_unreg_handler: un-registering ds_snmp service\n");
900 
901 	mutex_enter(&ds_snmp_lock);
902 
903 	if (ds_snmp_num_opens) {
904 		DS_SNMP_DBG("ds_snmp_unreg_handler: %d opens, sc reset!\n",
905 		    ds_snmp_num_opens);
906 		for (minor = 1; minor <= DS_SNMP_MAX_OPENS; minor++) {
907 			if (ds_snmp_is_open(minor)) {
908 				DS_SNMP_DBG("ds_snmp_unreg_handler: minor %d "
909 				    "open\n", minor);
910 				sp = ddi_get_soft_state(ds_snmp_statep, minor);
911 				if (sp == NULL)
912 					continue;
913 
914 				/*
915 				 * Set the sc_reset flag and break any waiters
916 				 * out of their existing reads/writes/ioctls.
917 				 */
918 				DS_SNMP_DBG("ds_snmp_unreg_hdlr: about to "
919 				    "signal waiters\n");
920 				mutex_enter(&sp->lock);
921 				sp->sc_reset = B_TRUE;
922 				sp->state = DS_SNMP_DATA_ERR;
923 				cv_broadcast(&sp->state_cv);
924 				mutex_exit(&sp->lock);
925 			}
926 		}
927 	}
928 
929 	ds_snmp_handle = DS_INVALID_HDL;
930 	ds_snmp_has_service = B_FALSE;
931 
932 	DS_SNMP_DBG("ds_snmp_unreg_handler: handle invalidated\n");
933 
934 	mutex_exit(&ds_snmp_lock);
935 }
936 
937 /*ARGSUSED*/
938 static void
939 ds_snmp_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
940 {
941 	ds_snmp_state_t *sp;
942 	ds_snmp_msg_t   hdr;
943 	size_t  	snmp_size;
944 	minor_t 	minor;
945 
946 	/*
947 	 * Make sure the header is at least valid
948 	 */
949 	if (buflen < sizeof (hdr)) {
950 		cmn_err(CE_WARN,
951 		"ds_snmp_data_handler: buflen <%lu> too small", buflen);
952 		return;
953 	}
954 
955 	ASSERT(buf != NULL);
956 	bcopy(buf, (void *)&hdr, sizeof (hdr));
957 
958 	DS_SNMP_DBG("ds_snmp_data_handler: msg buf len 0x%lx : type 0x%lx, "
959 	    "seqn 0x%lx\n", buflen, hdr.type, hdr.seq_num);
960 
961 	minor = (int)(hdr.seq_num >> DS_SNMP_MINOR_SHIFT);
962 	if ((sp = ddi_get_soft_state(ds_snmp_statep, minor)) == NULL)
963 		return;
964 
965 	mutex_enter(&sp->lock);
966 
967 	/*
968 	 * If there is no pending SNMP request, then we've received
969 	 * bogus data or an SNMP trap. Since we don't yet support SNMP
970 	 * traps, ignore it.
971 	 */
972 	if (sp->state != DS_SNMP_REQUESTED) {
973 		cmn_err(CE_WARN, "Received SNMP data without request");
974 		mutex_exit(&sp->lock);
975 		return;
976 	}
977 
978 	/*
979 	 * Response to a request therefore old SNMP must've been consumed
980 	 */
981 	ASSERT(sp->data_len == 0);
982 	ASSERT(sp->data == NULL);
983 
984 	/*
985 	 * Response seq_num should match our request seq_num
986 	 */
987 	if (hdr.seq_num != sp->last_req_id) {
988 		cmn_err(CE_WARN, "Received DS snmp data out of sequence with "
989 		    "request");
990 		mutex_exit(&sp->lock);
991 		return;
992 	}
993 
994 	if (hdr.type == DS_SNMP_ERROR) {
995 		sp->state = DS_SNMP_DATA_ERR;
996 		DS_SNMP_DBG("ds_snmp_data_handler: hdr.type = DS_SNMP_ERROR\n");
997 	} else {
998 		snmp_size = buflen - sizeof (ds_snmp_msg_t);
999 		sp->data = kmem_alloc(snmp_size, KM_SLEEP);
1000 		sp->data_len = snmp_size;
1001 		sp->state = DS_SNMP_DATA_AVL;
1002 
1003 		bcopy((caddr_t)buf + sizeof (ds_snmp_msg_t),
1004 		    sp->data, sp->data_len);
1005 	}
1006 
1007 	sp->gencount++;
1008 
1009 	/*
1010 	 * Wake up any readers waiting for data
1011 	 */
1012 	cv_broadcast(&sp->state_cv);
1013 	mutex_exit(&sp->lock);
1014 }
1015