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