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