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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * LDOMs Domain Services Device Driver
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/taskq.h>
46 #include <sys/disp.h>
47 #include <sys/note.h>
48 #include <sys/mach_descrip.h>
49 #include <sys/mdesc.h>
50 #include <sys/mdeg.h>
51 #include <sys/ldc.h>
52 #include <sys/ds.h>
53 #include <sys/ds_impl.h>
54 #include <sys/vlds.h>
55 #include <sys/bitmap.h>
56 #include <sys/sysevent.h>
57
58 static dev_info_t *vlds_devi;
59
60
61 typedef struct vlds_state {
62 dev_info_t *dip;
63 int instance;
64 evchan_t *evchan;
65 } vlds_state_t;
66
67 static void *vlds_statep;
68
69 typedef struct vlds_recv_hdr {
70 struct vlds_recv_hdr *next; /* next in recv list */
71 void *data; /* the data itself */
72 size_t datasz; /* size of the data */
73 } vlds_recv_hdr_t;
74
75 typedef struct vlds_svc_info {
76 int state; /* driver svc info state VLDS_RECV* */
77 vlds_recv_hdr_t *recv_headp; /* ptr to head of recv queue */
78 vlds_recv_hdr_t *recv_tailp; /* ptr to tail of recv queue */
79 size_t recv_size; /* no. of bytes in recv queue */
80 uint_t recv_cnt; /* no of messages in recv queue */
81 kmutex_t recv_lock; /* lock for recv queue */
82 kcondvar_t recv_cv; /* condition variable for recv queue */
83 int recv_nreaders; /* no of currently waiting readers */
84 } vlds_svc_info_t;
85
86 #define VLDS_RECV_OK 1
87 #define VLDS_RECV_UNREG_PENDING 2
88 #define VLDS_RECV_OVERFLOW 3
89
90 static int vlds_ports_inited = 0;
91
92 static uint_t vlds_flags_to_svc(uint64_t flags);
93
94
95 #define VLDS_NAME "vlds"
96 static int vlds_open(dev_t *devp, int flag, int otyp, cred_t *credp);
97 static int vlds_close(dev_t dev, int flag, int otyp, cred_t *credp);
98 static int vlds_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
99 int *rvalp);
100 static int vlds_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
101 void **resultp);
102 static int vlds_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
103 static int vlds_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
104
105 /* mdeg register functions */
106 static void vlds_mdeg_init(void);
107 static int vlds_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
108 static int vlds_mdeg_register(void);
109 static int vlds_mdeg_unregister(void);
110 static int vlds_add_mdeg_port(md_t *mdp, mde_cookie_t node);
111
112 /* driver utilities */
113 static void vlds_user_reg_cb(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
114 static void vlds_user_unreg_cb(ds_cb_arg_t arg);
115 static void vlds_user_data_cb(ds_cb_arg_t arg, void *buf, size_t buflen);
116 static void vlds_recvq_init(vlds_svc_info_t *dpsp);
117 static void vlds_recvq_destroy(vlds_svc_info_t *dpsp);
118 static int vlds_recvq_get_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen,
119 size_t *msglenp, int mode);
120 static void vlds_recvq_drain(vlds_svc_info_t *dpsp);
121 static int vlds_recvq_put_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen);
122 static int vlds_recv_msg(ds_svc_hdl_t hdl, void *buf, size_t buflen,
123 size_t *msglenp, int mode);
124
125 /*
126 * DS driver Ops Vector
127 */
128 static struct cb_ops vlds_cb_ops = {
129 vlds_open, /* cb_open */
130 vlds_close, /* cb_close */
131 nodev, /* cb_strategy */
132 nodev, /* cb_print */
133 nodev, /* cb_dump */
134 nodev, /* cb_read */
135 nodev, /* cb_write */
136 vlds_ioctl, /* cb_ioctl */
137 nodev, /* cb_devmap */
138 nodev, /* cb_mmap */
139 nodev, /* cb_segmap */
140 nochpoll, /* cb_chpoll */
141 ddi_prop_op, /* cb_prop_op */
142 (struct streamtab *)NULL, /* cb_str */
143 D_MP | D_64BIT, /* cb_flag */
144 CB_REV, /* cb_rev */
145 nodev, /* cb_aread */
146 nodev /* cb_awrite */
147 };
148
149 static struct dev_ops vlds_dev_ops = {
150 DEVO_REV, /* devo_rev */
151 0, /* devo_refcnt */
152 vlds_getinfo, /* devo_getinfo */
153 nulldev, /* devo_identify */
154 nulldev, /* devo_probe */
155 vlds_attach, /* devo_attach */
156 vlds_detach, /* devo_detach */
157 nodev, /* devo_reset */
158 &vlds_cb_ops, /* devo_cb_ops */
159 (struct bus_ops *)NULL, /* devo_bus_ops */
160 nulldev /* devo_power */
161 };
162
163 static struct modldrv modldrv = {
164 &mod_driverops,
165 "Domain Services Driver 1.0",
166 &vlds_dev_ops
167 };
168
169 static struct modlinkage modlinkage = {
170 MODREV_1,
171 (void *)&modldrv,
172 NULL
173 };
174
175 /*
176 * Callback ops for user-land services.
177 */
178 static ds_clnt_ops_t ds_user_ops = {
179 vlds_user_reg_cb, /* register */
180 vlds_user_unreg_cb, /* unregister */
181 vlds_user_data_cb, /* data */
182 NULL /* ds_ucap_init will fill in */
183 };
184
185 static size_t vlds_recvq_maxsize = DS_STREAM_MTU * 8;
186 static uint_t vlds_recvq_maxmsg = 16;
187
188 #define VLDS_MINOR_MAX SHRT_MAX
189
190 /* Definitions for binding handle array */
191 static ulong_t vlds_bitmap_initial = 1; /* index 0 indicates error */
192 static ulong_t *vlds_minor_bitmap = &vlds_bitmap_initial;
193 static size_t vlds_minor_bits = BT_NBIPUL;
194 static kmutex_t vlds_minor_mutex;
195
196 /*
197 * Following vlds_minor_* routines map a binding handle to a minor number.
198 * Has to be called w/ locks held.
199 */
200 static ulong_t *
vlds_minor_alloc(void)201 vlds_minor_alloc(void)
202 {
203 ulong_t *bhst = vlds_minor_bitmap;
204
205 /* Increase bitmap by one BT_NBIPUL */
206 if (vlds_minor_bits + BT_NBIPUL > VLDS_MINOR_MAX) {
207 return ((ulong_t *)NULL);
208 }
209 vlds_minor_bitmap = kmem_zalloc(
210 BT_SIZEOFMAP(vlds_minor_bits + BT_NBIPUL), KM_SLEEP);
211 bcopy(bhst, vlds_minor_bitmap, BT_SIZEOFMAP(vlds_minor_bits));
212 if (bhst != &vlds_bitmap_initial)
213 kmem_free(bhst, BT_SIZEOFMAP(vlds_minor_bits));
214 vlds_minor_bits += BT_NBIPUL;
215
216 return (vlds_minor_bitmap);
217 }
218
219 static void
vlds_minor_free(ulong_t * bitmap)220 vlds_minor_free(ulong_t *bitmap)
221 {
222 if (bitmap != &vlds_bitmap_initial)
223 kmem_free(bitmap, BT_SIZEOFMAP(vlds_minor_bits));
224 }
225
226 static index_t
vlds_minor_get(void)227 vlds_minor_get(void)
228 {
229 index_t idx;
230 ulong_t *bhst;
231
232 /* Search for an available index */
233 mutex_enter(&vlds_minor_mutex);
234 if ((idx = bt_availbit(vlds_minor_bitmap,
235 vlds_minor_bits)) == -1) {
236 /* All busy - allocate additional binding handle bitmap space */
237 if ((bhst = vlds_minor_alloc()) == NULL) {
238 /* Reached our maximum of id's == SHRT_MAX */
239 mutex_exit(&vlds_minor_mutex);
240 return (0);
241 } else {
242 vlds_minor_bitmap = bhst;
243 }
244 idx = bt_availbit(vlds_minor_bitmap, vlds_minor_bits);
245 }
246 BT_SET(vlds_minor_bitmap, idx);
247 mutex_exit(&vlds_minor_mutex);
248 return (idx);
249 }
250
251 static void
vlds_minor_rele(index_t idx)252 vlds_minor_rele(index_t idx)
253 {
254 mutex_enter(&vlds_minor_mutex);
255 ASSERT(BT_TEST(vlds_minor_bitmap, idx) == 1);
256 BT_CLEAR(vlds_minor_bitmap, idx);
257 mutex_exit(&vlds_minor_mutex);
258 }
259
260 static void
vlds_minor_init(void)261 vlds_minor_init(void)
262 {
263 mutex_init(&vlds_minor_mutex, NULL, MUTEX_DEFAULT, NULL);
264 }
265
266 int
_init(void)267 _init(void)
268 {
269 int s;
270
271 if ((s = ddi_soft_state_init(&vlds_statep, sizeof (vlds_state_t), 0))
272 != 0)
273 return (s);
274
275 if ((s = mod_install(&modlinkage)) != 0) {
276 ddi_soft_state_fini(&vlds_statep);
277 return (s);
278 }
279
280 vlds_mdeg_init();
281
282 return (s);
283 }
284
285 int
_fini(void)286 _fini(void)
287 {
288 int s;
289
290 if ((s = mod_remove(&modlinkage)) != 0)
291 return (s);
292
293 ddi_soft_state_fini(&vlds_statep);
294
295 return (s);
296 }
297
298 int
_info(struct modinfo * modinfop)299 _info(struct modinfo *modinfop)
300 {
301 return (mod_info(&modlinkage, modinfop));
302 }
303
304
305
306 /*ARGSUSED*/
307 static int
vlds_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)308 vlds_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
309 {
310 switch (cmd) {
311 case DDI_INFO_DEVT2DEVINFO:
312 *resultp = vlds_devi;
313 return (DDI_SUCCESS);
314 case DDI_INFO_DEVT2INSTANCE:
315 *resultp = 0;
316 return (DDI_SUCCESS);
317 }
318 return (DDI_FAILURE);
319 }
320
321
322 static int
vlds_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)323 vlds_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
324 {
325 if (cmd != DDI_ATTACH) {
326 return (DDI_FAILURE);
327 }
328
329 if (ddi_create_minor_node(devi, VLDS_NAME, S_IFCHR,
330 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
331 ddi_remove_minor_node(devi, NULL);
332 return (DDI_FAILURE);
333 }
334 vlds_devi = devi;
335
336 vlds_minor_init();
337
338 (void) vlds_mdeg_register();
339
340 return (DDI_SUCCESS);
341 }
342
343
344 /*ARGSUSED*/
345 static int
vlds_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)346 vlds_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
347 {
348 if (cmd != DDI_DETACH) {
349 return (DDI_FAILURE);
350 }
351
352 vlds_minor_free(vlds_minor_bitmap);
353 ddi_remove_minor_node(devi, NULL);
354 (void) vlds_mdeg_unregister();
355 return (DDI_SUCCESS);
356 }
357
358
359 /*ARGSUSED*/
360 static int
vlds_open(dev_t * devp,int flag,int otyp,cred_t * credp)361 vlds_open(dev_t *devp, int flag, int otyp, cred_t *credp)
362 {
363 int minor;
364
365 if (otyp != OTYP_CHR)
366 return (EINVAL);
367
368 if (getminor(*devp) != 0)
369 return (ENXIO);
370
371 minor = vlds_minor_get();
372 if (minor == 0)
373 /* All minors are busy */
374 return (EBUSY);
375
376 if (ddi_soft_state_zalloc(vlds_statep, minor) != DDI_SUCCESS) {
377 vlds_minor_rele(minor);
378 return (ENOMEM);
379 }
380
381 *devp = makedevice(getmajor(*devp), minor);
382
383 return (0);
384 }
385
386
387 /*ARGSUSED*/
388 static int
vlds_close(dev_t dev,int flag,int otyp,cred_t * credp)389 vlds_close(dev_t dev, int flag, int otyp, cred_t *credp)
390 {
391 int minor = (int)getminor(dev);
392 vlds_state_t *sp;
393
394 DS_DBG_VLDS(CE_NOTE, "vlds_close");
395
396 /*
397 * Unregister all handles associated with this process.
398 */
399 ds_unreg_all(minor);
400
401 if (otyp != OTYP_CHR)
402 return (EINVAL);
403
404 sp = ddi_get_soft_state(vlds_statep, minor);
405 if (sp == NULL) {
406 return (ENXIO);
407 }
408
409 if (sp->evchan) {
410 (void) sysevent_evc_unbind(sp->evchan);
411 sp->evchan = NULL;
412 }
413
414 ddi_soft_state_free(vlds_statep, minor);
415 vlds_minor_rele(minor);
416
417 return (0);
418 }
419
420 int
vlds_init_sysevent(vlds_state_t * sp,uint32_t flags)421 vlds_init_sysevent(vlds_state_t *sp, uint32_t flags)
422 {
423 char evchan_name[MAX_CHNAME_LEN];
424 int rv;
425
426 if (flags & DSSF_ANYCB_VALID) {
427 if (sp->evchan) {
428 DS_DBG_VLDS(CE_NOTE, "%s: sysevent already bound",
429 __func__);
430 return (0);
431 }
432 (void) sprintf(evchan_name, VLDS_SYSEV_CHAN_FMT, ddi_get_pid());
433 if ((rv = sysevent_evc_bind(evchan_name, &sp->evchan,
434 EVCH_CREAT|EVCH_HOLD_PEND)) != 0) {
435 cmn_err(CE_WARN, "%s: can't bind to '%s' (%d)",
436 __func__, evchan_name, rv);
437 return (rv);
438 }
439
440 DS_DBG_VLDS(CE_NOTE, "%s: sysevent bind to '%s' successful",
441 __func__, evchan_name);
442 }
443 return (0);
444 }
445
446 #define ARGTOPTR(x) ((void *)((uintptr_t)(x)))
447 #define ARGTOUINT(x) ((uint_t)(x))
448 #define ARGTOINT(x) ((int)(x))
449
450 static int
vlds_get_string(vlds_string_t * strp,char ** rstrp,int mode)451 vlds_get_string(vlds_string_t *strp, char **rstrp, int mode)
452 {
453 char *str;
454 uint_t len = strp->vlds_strlen;
455 uint_t slen;
456
457 if (len == 0) {
458 *rstrp = NULL;
459 return (0);
460 }
461 if (len > MAXNAMELEN) {
462 DS_DBG_VLDS(CE_NOTE, "%s: invalid string length: %d", __func__,
463 len);
464 return (EINVAL);
465 }
466 str = DS_MALLOC(len);
467 if (ddi_copyin(ARGTOPTR(strp->vlds_strp), str, len, mode) != 0) {
468 DS_DBG_VLDS(CE_NOTE, "%s: ddi copyin failed (%p)", __func__,
469 ARGTOPTR(strp->vlds_strp));
470 DS_FREE(str, len);
471 return (EFAULT);
472 }
473 slen = strlen(str) + 1;
474 if (slen != len) {
475 DS_DBG_VLDS(CE_NOTE, "%s: invalid string len: %d != len: %d",
476 __func__, slen, len);
477 DS_FREE(str, len);
478 return (EINVAL);
479 }
480 *rstrp = str;
481 return (0);
482 }
483
484 static int
vlds_put_string(char * str,vlds_string_t * strp,int mode)485 vlds_put_string(char *str, vlds_string_t *strp, int mode)
486 {
487 uint_t len;
488 char *tstr = NULL;
489 int rv;
490
491 if (str == NULL) {
492 str = "";
493 }
494 len = strlen(str) + 1;
495
496 /*
497 * If string is longer than user buffer, return a
498 * truncated, null-terminated string.
499 */
500 if (len > strp->vlds_strlen) {
501 len = strp->vlds_strlen;
502 if (len > 0) {
503 tstr = DS_MALLOC(len);
504 (void) memcpy(tstr, str, len - 1);
505 tstr[len - 1] = '\0';
506 str = tstr;
507 }
508 }
509 rv = ddi_copyout(str, ARGTOPTR(strp->vlds_strp), len, mode);
510 if (tstr) {
511 DS_FREE(tstr, len);
512 }
513 if (rv) {
514 DS_DBG_VLDS(CE_NOTE, "%s: copyout (%p) failed", __func__,
515 ARGTOPTR(strp->vlds_strp));
516 return (EFAULT);
517 }
518 return (0);
519 }
520
521 static int
vlds_get_ucap(vlds_cap_t * capp,ds_capability_t * ucap,int mode)522 vlds_get_ucap(vlds_cap_t *capp, ds_capability_t *ucap, int mode)
523 {
524 char *servp;
525 vlds_ver_t *dsvp;
526 vlds_cap_t vlds_cap;
527 uint_t n;
528 uint_t nver;
529 int i;
530 int rv;
531
532 if (ddi_copyin(capp, &vlds_cap, sizeof (vlds_cap), mode) != 0) {
533 DS_DBG_VLDS(CE_NOTE, "%s: cap copyin failed (%p)", __func__,
534 (void *)capp);
535 return (EFAULT);
536 }
537
538 nver = ARGTOUINT(vlds_cap.vlds_nver);
539
540 if (nver > VLDS_MAX_VERS) {
541 DS_DBG_VLDS(CE_NOTE, "%s: vlds_nver (%d) invalid", __func__,
542 nver);
543 return (EINVAL);
544 }
545
546 if ((rv = vlds_get_string(&vlds_cap.vlds_service, &servp, mode)) != 0) {
547 DS_DBG_VLDS(CE_NOTE, "%s: vlds_get_string vlds_service failed "
548 "(%d)", __func__, rv);
549 return (rv);
550 } else if (servp == NULL) {
551 DS_DBG_VLDS(CE_NOTE, "%s: vlds_get_string vlds_service is NULL",
552 __func__);
553 return (EINVAL);
554 }
555
556 n = nver * sizeof (vlds_ver_t);
557 dsvp = DS_MALLOC(n);
558
559 if (ddi_copyin(ARGTOPTR(vlds_cap.vlds_versp), dsvp, n, mode) != 0) {
560 DS_DBG_VLDS(CE_NOTE, "%s: copyin of vers (%p, %d) failed",
561 __func__, ARGTOPTR(vlds_cap.vlds_versp), n);
562 DS_FREE(servp, strlen(servp) + 1);
563 DS_FREE(dsvp, n);
564 return (EFAULT);
565 }
566
567 ucap->svc_id = servp;
568 ucap->vers = DS_MALLOC(nver * sizeof (ds_ver_t));
569 for (i = 0; i < nver; i++) {
570 ucap->vers[i].major = dsvp[i].vlds_major;
571 ucap->vers[i].minor = dsvp[i].vlds_minor;
572 }
573 ucap->nvers = nver;
574 DS_FREE(dsvp, n);
575 return (0);
576 }
577
578 static void
vlds_free_ucap(ds_capability_t * ucap)579 vlds_free_ucap(ds_capability_t *ucap)
580 {
581 kmem_free(ucap->svc_id, strlen(ucap->svc_id) + 1);
582 kmem_free(ucap->vers, ucap->nvers * sizeof (ds_ver_t));
583 }
584
585 /*ARGSUSED*/
586 static int
vlds_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)587 vlds_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
588 int *rvalp)
589 {
590 vlds_state_t *sp;
591 ds_svc_hdl_t hdl;
592 ds_domain_hdl_t dhdl;
593 char *servicep;
594 int rv;
595 int minor = (int)getminor(dev);
596
597 if ((sp = ddi_get_soft_state(vlds_statep, minor)) == NULL)
598 return (ENXIO);
599
600 switch (cmd) {
601
602 case VLDS_SVC_REG:
603 {
604 vlds_svc_reg_arg_t vlds_arg;
605 ds_capability_t ucap;
606 uint64_t hdl_arg;
607 uint_t flags;
608
609 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
610 mode) != 0) {
611 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG arg copyin failed",
612 __func__);
613 return (EFAULT);
614 }
615
616 if ((rv = vlds_get_ucap(ARGTOPTR(vlds_arg.vlds_capp), &ucap,
617 mode)) != 0) {
618 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG get_ucap failed (%d)",
619 __func__, rv);
620 return (rv);
621 }
622
623 flags = vlds_flags_to_svc(vlds_arg.vlds_reg_flags);
624 if ((rv = vlds_init_sysevent(sp, flags)) != 0) {
625 vlds_free_ucap(&ucap);
626 return (rv);
627 }
628
629 rv = ds_ucap_init(&ucap, &ds_user_ops,
630 vlds_flags_to_svc(vlds_arg.vlds_reg_flags) | DSSF_ISUSER,
631 minor, &hdl);
632
633 vlds_free_ucap(&ucap);
634
635 if (rv) {
636 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG ds_ucap_init failed "
637 "(%d)", __func__, rv);
638 return (rv);
639 }
640
641 hdl_arg = hdl;
642 if (ddi_copyout(&hdl_arg, ARGTOPTR(vlds_arg.vlds_hdlp),
643 sizeof (hdl_arg), mode) != 0) {
644 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG copyout failed",
645 __func__);
646 return (EFAULT);
647 }
648 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG succeeded: hdl: %lx",
649 __func__, hdl);
650 break;
651 }
652
653 case VLDS_UNREG_HDL:
654 {
655 vlds_unreg_hdl_arg_t vlds_arg;
656
657 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
658 mode) != 0) {
659 DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL arg copyin failed",
660 __func__);
661 return (EFAULT);
662 }
663
664 hdl = vlds_arg.vlds_hdl;
665
666 if ((rv = ds_is_my_hdl(hdl, minor)) != 0) {
667 DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL ds_is_my_hdl "
668 " hdl: %lx inst: %d failed (%d)", __func__,
669 hdl, rv, minor);
670 return (rv);
671 }
672
673 if ((rv = ds_unreg_hdl(hdl)) != 0) {
674 DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL ds_cap_unreg "
675 " hdl: %lx failed (%d)", __func__, hdl, rv);
676 return (rv);
677 }
678 DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL hdl: %lx succeeded",
679 __func__, hdl);
680 break;
681 }
682
683 case VLDS_HDL_LOOKUP:
684 {
685 vlds_hdl_lookup_arg_t vlds_arg;
686 ds_svc_hdl_t *hdlsp;
687 uint_t is_client, maxhdls, nhdls;
688 uint64_t nhdls_arg;
689
690 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
691 mode) != 0) {
692 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP arg copyin failed",
693 __func__);
694 return (EFAULT);
695 }
696
697 is_client = ARGTOUINT(vlds_arg.vlds_isclient);
698 maxhdls = ARGTOUINT(vlds_arg.vlds_maxhdls);
699 if (maxhdls == 0) {
700 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP invalid maxhdls "
701 "%d", __func__, maxhdls);
702 return (EINVAL);
703 }
704
705 if ((rv = vlds_get_string(&vlds_arg.vlds_service, &servicep,
706 mode)) != 0) {
707 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP vlds_get_string "
708 "(service) failed (%d)", __func__, rv);
709 return (EFAULT);
710 } else if (servicep == NULL) {
711 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP vlds_get_string "
712 " service is NULL", __func__);
713 return (EINVAL);
714 }
715
716 if (ARGTOPTR(vlds_arg.vlds_hdlsp) == 0) {
717 hdlsp = NULL;
718 } else {
719 hdlsp = DS_MALLOC(maxhdls * sizeof (*hdlsp));
720 }
721
722 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP (%s, %d) entered",
723 __func__, servicep, is_client);
724 rv = ds_hdl_lookup(servicep, is_client, hdlsp, maxhdls, &nhdls);
725
726 DS_FREE(servicep, strlen(servicep) + 1);
727 if (rv) {
728 if (hdlsp) {
729 DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp));
730 }
731 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP failed: (%d)",
732 __func__, rv);
733 return (rv);
734 }
735
736 if (hdlsp != NULL && nhdls > 0 &&
737 ddi_copyout(hdlsp, ARGTOPTR(vlds_arg.vlds_hdlsp),
738 nhdls * sizeof (ds_svc_hdl_t), mode) != 0) {
739 if (hdlsp) {
740 DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp));
741 }
742 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP copyout of hdls "
743 " failed", __func__);
744 return (EFAULT);
745 }
746 if (hdlsp) {
747 DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp));
748 }
749
750 nhdls_arg = nhdls;
751 if (ddi_copyout(&nhdls_arg, ARGTOPTR(vlds_arg.vlds_nhdlsp),
752 sizeof (nhdls_arg), mode) != 0) {
753 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP copyout of nhdls "
754 " failed", __func__);
755 return (EFAULT);
756 }
757 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP succeeded: nhdls: %d",
758 __func__, nhdls);
759 break;
760 }
761
762 case VLDS_DMN_LOOKUP:
763 {
764 vlds_dmn_lookup_arg_t vlds_arg;
765 uint64_t dhdl_arg;
766
767 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
768 mode) != 0) {
769 DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP arg copyin failed",
770 __func__);
771 return (EFAULT);
772 }
773
774 hdl = vlds_arg.vlds_hdl;
775
776 if ((rv = ds_domain_lookup(hdl, &dhdl)) != 0) {
777 DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP lookup hdl: 0x%lx "
778 "failed (%d)", __func__, hdl, rv);
779 return (rv);
780 }
781
782 dhdl_arg = dhdl;
783
784 if (ddi_copyout(&dhdl_arg, ARGTOPTR(vlds_arg.vlds_dhdlp),
785 sizeof (dhdl_arg), mode) != 0) {
786 DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP copyout "
787 "failed (%d)", __func__, rv);
788 return (rv);
789 }
790
791 DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP hdl: 0x%lx, dhdl: 0x%lx "
792 "succeeded", __func__, hdl, dhdl);
793 break;
794 }
795
796 case VLDS_SEND_MSG:
797 {
798 vlds_send_msg_arg_t vlds_arg;
799 size_t buflen;
800 char *bufp;
801
802 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
803 mode) != 0) {
804 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG arg copyin failed",
805 __func__);
806 return (EFAULT);
807 }
808
809 hdl = vlds_arg.vlds_hdl;
810 if ((rv = ds_is_my_hdl(hdl, minor)) != 0) {
811 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG ds_is_my_hdl "
812 " hdl: %lx inst: %d failed (%d)", __func__,
813 hdl, rv, minor);
814 return (rv);
815 }
816
817 buflen = ARGTOUINT(vlds_arg.vlds_buflen);
818 bufp = DS_MALLOC(buflen);
819 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG (hdl: %lx, bufp: %p, "
820 "buflen: %ld", __func__, hdl, ARGTOPTR(vlds_arg.vlds_bufp),
821 buflen);
822
823 if (ddi_copyin(ARGTOPTR(vlds_arg.vlds_bufp), bufp, buflen,
824 mode) != 0) {
825 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG buf (%p, %ld) "
826 "copyin failed", __func__,
827 ARGTOPTR(vlds_arg.vlds_bufp), buflen);
828 DS_FREE(bufp, buflen);
829 return (EFAULT);
830 }
831
832 if ((rv = ds_cap_send(hdl, bufp, buflen)) != 0) {
833 DS_FREE(bufp, buflen);
834 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG ds_cap_send failed "
835 "(%d)", __func__, rv);
836 return (rv);
837 }
838 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG hdl: %lx, bufp: %p, "
839 "buflen: %ld succeeded", __func__, hdl, (void *)bufp,
840 buflen);
841 DS_DUMP_MSG(DS_DBG_FLAG_VLDS, bufp, buflen);
842 DS_FREE(bufp, buflen);
843 break;
844 }
845
846 case VLDS_RECV_MSG:
847 {
848 vlds_recv_msg_arg_t vlds_arg;
849 size_t buflen, msglen;
850 uint64_t msglen_arg;
851
852 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
853 mode) != 0) {
854 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG arg copyin failed",
855 __func__);
856 return (EFAULT);
857 }
858
859 hdl = vlds_arg.vlds_hdl;
860 if ((rv = ds_is_my_hdl(hdl, minor)) != 0) {
861 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG ds_is_my_hdl "
862 " hdl: %lx inst: %d failed (%d)", __func__,
863 hdl, rv, minor);
864 return (rv);
865 }
866
867 buflen = ARGTOUINT(vlds_arg.vlds_buflen);
868
869 if ((rv = vlds_recv_msg(hdl, ARGTOPTR(vlds_arg.vlds_bufp),
870 buflen, &msglen, mode)) != 0 && rv != EFBIG) {
871 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG vlds_recv_msg "
872 " failed (%d)", __func__, rv);
873 return (rv);
874 }
875
876 msglen_arg = msglen;
877 if (ddi_copyout(&msglen_arg, ARGTOPTR(vlds_arg.vlds_msglenp),
878 sizeof (msglen_arg), mode) != 0) {
879 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG copyout of msglen "
880 "failed", __func__);
881 return (EFAULT);
882 }
883
884 if (rv == EFBIG) {
885 return (EFBIG);
886 }
887
888 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG hdl: %lx, "
889 "msglen: %ld succeeded", __func__, hdl, buflen);
890 break;
891 }
892
893 case VLDS_HDL_ISREADY:
894 {
895 vlds_hdl_isready_arg_t vlds_arg;
896 ds_svc_hdl_t hdl;
897 uint64_t is_ready_arg;
898 uint_t is_ready;
899
900 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
901 mode) != 0) {
902 DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY arg copyin "
903 "failed", __func__);
904 return (EFAULT);
905 }
906
907 hdl = vlds_arg.vlds_hdl;
908 if ((rv = ds_hdl_isready(hdl, &is_ready)) != 0) {
909 DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY ds_hdl_isready "
910 "error (%d)", __func__, rv);
911 return (rv);
912 }
913
914 is_ready_arg = is_ready;
915 if (ddi_copyout(&is_ready_arg, ARGTOPTR(vlds_arg.vlds_isreadyp),
916 sizeof (is_ready_arg), mode) != 0) {
917 DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY copyout of "
918 "vlds_isready failed", __func__);
919 return (EFAULT);
920 }
921 DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY succeeded hdl: %lx, "
922 "is_ready: %d", __func__, hdl, is_ready);
923 break;
924 }
925
926 case VLDS_DOM_NAM2HDL:
927 {
928 vlds_dom_nam2hdl_arg_t vlds_arg;
929 char *domain_name;
930 uint64_t dhdl_arg;
931 ds_domain_hdl_t dhdl;
932
933 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
934 mode) != 0) {
935 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL arg copyin "
936 "failed", __func__);
937 return (EFAULT);
938 }
939
940 if ((rv = vlds_get_string(&vlds_arg.vlds_domain_name,
941 &domain_name, mode)) != 0) {
942 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL vlds_get_string "
943 "domain_name failed (%d)", __func__, rv);
944 return (EFAULT);
945 } else if (servicep == NULL) {
946 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL vlds_get_string "
947 " domain_name is NULL", __func__);
948 return (EINVAL);
949 }
950
951 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL (%s) entered", __func__,
952 domain_name);
953
954 if ((rv = ds_dom_name_to_hdl(domain_name, &dhdl)) != 0) {
955 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL name: '%s' "
956 "failed: (%d)", __func__, domain_name, rv);
957 DS_FREE(domain_name, strlen(domain_name) + 1);
958 return (rv);
959 }
960
961 dhdl_arg = dhdl;
962 if (ddi_copyout(&dhdl_arg, ARGTOPTR(vlds_arg.vlds_dhdlp),
963 sizeof (dhdl_arg), mode) != 0) {
964 DS_FREE(domain_name, strlen(domain_name) + 1);
965 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL copyout of dhdl "
966 " failed", __func__);
967 return (EFAULT);
968 }
969
970 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL succeeded: name: '%s', "
971 "dhdl: 0x%lx", __func__, domain_name, dhdl);
972 DS_FREE(domain_name, strlen(domain_name) + 1);
973 break;
974 }
975
976 case VLDS_DOM_HDL2NAM:
977 {
978 vlds_dom_hdl2nam_arg_t vlds_arg;
979 ds_domain_hdl_t dhdl;
980 char *domain_name;
981
982 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
983 mode) != 0) {
984 DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM arg copyin "
985 "failed", __func__);
986 return (EFAULT);
987 }
988
989 dhdl = vlds_arg.vlds_dhdl;
990 if ((rv = ds_dom_hdl_to_name(dhdl, &domain_name)) != 0) {
991 DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM lookup dhdl: %lx "
992 "failed (%d)", __func__, dhdl, rv);
993 return (rv);
994 }
995
996 if ((rv = vlds_put_string(domain_name,
997 &vlds_arg.vlds_domain_name, mode)) != 0) {
998 DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM vlds_put_string "
999 "'%s' failed (%d)", __func__, domain_name, rv);
1000 return (rv);
1001 }
1002
1003 DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM dhdl: 0x%lx name: '%s'",
1004 __func__, dhdl, domain_name);
1005 break;
1006 }
1007
1008 default:
1009 return (EINVAL);
1010 }
1011 return (0);
1012 }
1013
1014 static uint_t
vlds_flags_to_svc(uint64_t flags)1015 vlds_flags_to_svc(uint64_t flags)
1016 {
1017 uint_t sflags = 0;
1018
1019 if (flags & VLDS_REG_CLIENT)
1020 sflags |= DSSF_ISCLIENT;
1021 if (flags & VLDS_REGCB_VALID)
1022 sflags |= DSSF_REGCB_VALID;
1023 if (flags & VLDS_UNREGCB_VALID)
1024 sflags |= DSSF_UNREGCB_VALID;
1025 if (flags & VLDS_DATACB_VALID)
1026 sflags |= DSSF_DATACB_VALID;
1027 return (sflags);
1028 }
1029
1030 /*
1031 * MD registration code.
1032 * Placed in vlds rather than ds module due to cirular dependency of
1033 * platsvc module which contains the mdeg code.
1034 */
1035 mdeg_handle_t vlds_mdeg_hdl;
1036
1037 /*
1038 * Look for "virtual-device-service" node among the
1039 * "virtual-device" nodes.
1040 */
1041 static mdeg_prop_spec_t vlds_prop_template[] = {
1042 { MDET_PROP_STR, "name", VLDS_MD_VIRT_ROOT_NAME },
1043 { MDET_LIST_END, NULL, NULL }
1044 };
1045
1046 static mdeg_node_spec_t vlds_node_template =
1047 { VLDS_MD_VIRT_DEV_NAME, vlds_prop_template };
1048
1049 /*
1050 * Matching criteria passed to the MDEG to register interest
1051 * in changes to domain services port nodes identified by their
1052 * 'id' property.
1053 */
1054 static md_prop_match_t vlds_port_prop_match[] = {
1055 { MDET_PROP_VAL, "id" },
1056 { MDET_LIST_END, NULL }
1057 };
1058
1059 static mdeg_node_match_t vlds_port_match = { VLDS_MD_VIRT_PORT_NAME,
1060 vlds_port_prop_match };
1061
1062 /* mdeg callback */
1063 static int
vlds_mdeg_cb(void * cb_argp,mdeg_result_t * resp)1064 vlds_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
1065 {
1066 _NOTE(ARGUNUSED(cb_argp))
1067 int idx;
1068 uint64_t portno;
1069 int rv;
1070 md_t *mdp;
1071 mde_cookie_t node;
1072
1073 if (resp == NULL) {
1074 DS_DBG_VLDS(CE_NOTE, "vlds_mdeg_cb: no result returned");
1075 return (MDEG_FAILURE);
1076 }
1077
1078 DS_DBG_VLDS(CE_NOTE, "%s: added=%d, removed=%d, matched=%d", __func__,
1079 resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem);
1080
1081 /* process added ports */
1082 for (idx = 0; idx < resp->added.nelem; idx++) {
1083 mdp = resp->added.mdp;
1084 node = resp->added.mdep[idx];
1085
1086 DS_DBG_VLDS(CE_NOTE, "%s: processing added node 0x%lx",
1087 __func__, node);
1088
1089 /* attempt to add a port */
1090 if ((rv = vlds_add_mdeg_port(mdp, node)) != MDEG_SUCCESS) {
1091 if (vlds_ports_inited) {
1092 cmn_err(CE_NOTE, "%s: unable to add port, "
1093 "err = %d", __func__, rv);
1094 }
1095 }
1096 }
1097
1098 /* process removed ports */
1099 for (idx = 0; idx < resp->removed.nelem; idx++) {
1100 mdp = resp->removed.mdp;
1101 node = resp->removed.mdep[idx];
1102
1103 DS_DBG_VLDS(CE_NOTE, "%s: processing removed node 0x%lx",
1104 __func__, node);
1105
1106 /* read in the port's id property */
1107 if (md_get_prop_val(mdp, node, "id", &portno)) {
1108 cmn_err(CE_NOTE, "%s: node 0x%lx of removed list "
1109 "has no 'id' property", __func__, node);
1110 continue;
1111 }
1112
1113 /* attempt to remove a port */
1114 if ((rv = ds_remove_port(portno, 0)) != 0) {
1115 cmn_err(CE_NOTE, "%s: unable to remove port %lu, "
1116 " err %d", __func__, portno, rv);
1117 }
1118 }
1119
1120 vlds_ports_inited = 1;
1121
1122 return (MDEG_SUCCESS);
1123 }
1124
1125 /* register callback to mdeg */
1126 static int
vlds_mdeg_register(void)1127 vlds_mdeg_register(void)
1128 {
1129 int rv;
1130
1131 DS_DBG_VLDS(CE_NOTE, "vlds_mdeg_register: entered");
1132
1133 /* perform the registration */
1134 rv = mdeg_register(&vlds_node_template, &vlds_port_match, vlds_mdeg_cb,
1135 NULL, &vlds_mdeg_hdl);
1136
1137 if (rv != MDEG_SUCCESS) {
1138 cmn_err(CE_NOTE, "vlds_mdeg_register: mdeg_register "
1139 "failed, err = %d", rv);
1140 return (DDI_FAILURE);
1141 }
1142
1143 return (DDI_SUCCESS);
1144 }
1145
1146 /* unregister callback from mdeg */
1147 static int
vlds_mdeg_unregister(void)1148 vlds_mdeg_unregister(void)
1149 {
1150 DS_DBG_VLDS(CE_NOTE, "vlds_mdeg_unregister: hdl=0x%lx", vlds_mdeg_hdl);
1151
1152 return (mdeg_unregister(vlds_mdeg_hdl));
1153 }
1154
1155 static int
vlds_get_port_channel(md_t * mdp,mde_cookie_t node,uint64_t * ldc_id)1156 vlds_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id)
1157 {
1158 int num_nodes, nchan;
1159 size_t listsz;
1160 mde_cookie_t *listp;
1161
1162 /*
1163 * Find the channel-endpoint node(s) (which should be under this
1164 * port node) which contain the channel id(s).
1165 */
1166 if ((num_nodes = md_node_count(mdp)) <= 0) {
1167 cmn_err(CE_NOTE, "%s: invalid number of channel-endpoint nodes "
1168 "found (%d)", __func__, num_nodes);
1169 return (-1);
1170 }
1171
1172 /* allocate space for node list */
1173 listsz = num_nodes * sizeof (mde_cookie_t);
1174 listp = kmem_alloc(listsz, KM_SLEEP);
1175
1176 nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"),
1177 md_find_name(mdp, "fwd"), listp);
1178
1179 if (nchan <= 0) {
1180 cmn_err(CE_NOTE, "%s: no channel-endpoint nodes found",
1181 __func__);
1182 kmem_free(listp, listsz);
1183 return (-1);
1184 }
1185
1186 DS_DBG_VLDS(CE_NOTE, "%s: %d channel-endpoint nodes found", __func__,
1187 nchan);
1188
1189 /* use property from first node found */
1190 if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) {
1191 cmn_err(CE_NOTE, "%s: channel-endpoint has no 'id' property",
1192 __func__);
1193 kmem_free(listp, listsz);
1194 return (-1);
1195 }
1196
1197 kmem_free(listp, listsz);
1198
1199 return (0);
1200 }
1201
1202 /* add a DS services port */
1203 static int
vlds_add_mdeg_port(md_t * mdp,mde_cookie_t node)1204 vlds_add_mdeg_port(md_t *mdp, mde_cookie_t node)
1205 {
1206 uint64_t portno;
1207 uint64_t ldc_id;
1208 int rv;
1209 uint64_t dhdl;
1210 char *dom_name;
1211
1212 /* read in the port's id property */
1213 if (md_get_prop_val(mdp, node, "id", &portno)) {
1214 cmn_err(CE_NOTE, "%s: node 0x%lx of added list has no "
1215 "'id' property", __func__, node);
1216 return (MDEG_FAILURE);
1217 }
1218
1219 if (portno >= DS_MAX_PORTS) {
1220 cmn_err(CE_NOTE, "%s: found port number (%lu) "
1221 "larger than maximum supported number of ports", __func__,
1222 portno);
1223 return (MDEG_FAILURE);
1224 }
1225
1226 /* get all channels for this device (currently only one) */
1227 if (vlds_get_port_channel(mdp, node, &ldc_id) == -1) {
1228 return (MDEG_FAILURE);
1229 }
1230
1231 if (md_get_prop_val(mdp, node, VLDS_MD_REM_DOMAIN_HDL, &dhdl) != 0) {
1232 cmn_err(CE_NOTE, "!ds%lx: %s no %s property", portno, __func__,
1233 VLDS_MD_REM_DOMAIN_HDL);
1234 dhdl = DS_DHDL_INVALID;
1235 }
1236
1237 if (md_get_prop_str(mdp, node, VLDS_MD_REM_DOMAIN_NAME, &dom_name)
1238 != 0) {
1239 cmn_err(CE_NOTE, "!ds%lx: %s no %s property", portno, __func__,
1240 VLDS_MD_REM_DOMAIN_NAME);
1241 dom_name = NULL;
1242 }
1243
1244 rv = ds_add_port(portno, ldc_id, dhdl, dom_name, vlds_ports_inited);
1245
1246 if (rv != 0) {
1247 if (vlds_ports_inited) {
1248 DS_DBG_VLDS(CE_NOTE, "ds%lx: %s LDC chan: %lx "
1249 "failed err = %d", portno, __func__, ldc_id, rv);
1250 }
1251 return (MDEG_FAILURE);
1252 }
1253
1254 DS_DBG_VLDS(CE_NOTE, "ds%lx: %s LDC chan: %lx inited", portno,
1255 __func__, ldc_id);
1256
1257 return (MDEG_SUCCESS);
1258 }
1259
1260 static void
vlds_mdeg_init(void)1261 vlds_mdeg_init(void)
1262 {
1263 md_t *mdp;
1264 int num_nodes;
1265 int listsz;
1266 mde_cookie_t rootnode;
1267 mde_cookie_t vldsnode;
1268 mde_cookie_t *vlds_nodes = NULL;
1269 int nvlds;
1270 int i;
1271 ds_domain_hdl_t dhdl;
1272 char *dom_name;
1273 char *svc_name;
1274
1275 if ((mdp = md_get_handle()) == NULL) {
1276 cmn_err(CE_NOTE, "Unable to initialize machine description");
1277 return;
1278 }
1279
1280 num_nodes = md_node_count(mdp);
1281 ASSERT(num_nodes > 0);
1282
1283 listsz = num_nodes * sizeof (mde_cookie_t);
1284
1285 /* allocate temporary storage for MD scans */
1286 vlds_nodes = kmem_zalloc(listsz, KM_SLEEP);
1287
1288 rootnode = md_root_node(mdp);
1289 ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
1290
1291 /*
1292 * Search for Virtual Domain Service node.
1293 */
1294 nvlds = md_scan_dag(mdp, rootnode, md_find_name(mdp,
1295 VLDS_MD_VIRT_DEV_NAME), md_find_name(mdp, "fwd"), vlds_nodes);
1296
1297 if (nvlds <= 0) {
1298 DS_DBG_MD(CE_NOTE, "No '%s' nodes in MD",
1299 VLDS_MD_VIRT_DEV_NAME);
1300 goto done;
1301 }
1302
1303 for (i = 0; i < nvlds; i++) {
1304 if (md_get_prop_str(mdp, vlds_nodes[i], "name", &svc_name)) {
1305 DS_DBG_MD(CE_NOTE, "%s: missing 'name' property for"
1306 " IO node %d\n", __func__, i);
1307 continue;
1308 }
1309
1310 if (strcmp(svc_name, VLDS_MD_VIRT_ROOT_NAME) == 0) {
1311 vldsnode = vlds_nodes[i];
1312 break;
1313 }
1314 }
1315
1316 if (i >= nvlds) {
1317 DS_DBG_MD(CE_NOTE, "No '%s' node in MD",
1318 VLDS_MD_VIRT_ROOT_NAME);
1319 goto done;
1320 }
1321
1322 if (md_get_prop_val(mdp, vldsnode, VLDS_MD_DOMAIN_HDL, &dhdl) != 0) {
1323 DS_DBG_MD(CE_NOTE, "No '%s' property for '%s' node in MD",
1324 VLDS_MD_DOMAIN_HDL, VLDS_MD_VIRT_ROOT_NAME);
1325 dhdl = DS_DHDL_INVALID;
1326 }
1327 if (md_get_prop_str(mdp, vldsnode, VLDS_MD_DOMAIN_NAME, &dom_name)
1328 != 0) {
1329 DS_DBG_MD(CE_NOTE, "No '%s' property for '%s' node in MD",
1330 VLDS_MD_DOMAIN_NAME, VLDS_MD_VIRT_ROOT_NAME);
1331 dom_name = NULL;
1332 }
1333 DS_DBG_MD(CE_NOTE, "My Domain Hdl: 0x%lx, Name: '%s'", dhdl,
1334 dom_name == NULL ? "NULL" : dom_name);
1335 ds_set_my_dom_hdl_name(dhdl, dom_name);
1336
1337 done:
1338 DS_FREE(vlds_nodes, listsz);
1339
1340 (void) md_fini_handle(mdp);
1341 }
1342
1343 static void
vlds_user_reg_cb(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)1344 vlds_user_reg_cb(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
1345 {
1346 nvlist_t *nvl = NULL;
1347 ds_domain_hdl_t dhdl;
1348 char *servicep;
1349 uint32_t flags;
1350 int minor;
1351 vlds_state_t *sp;
1352 vlds_svc_info_t *dpsp;
1353
1354 ds_cbarg_get_flags(arg, &flags);
1355 ASSERT((flags & DSSF_ISUSER) != 0);
1356
1357 if ((flags & DSSF_DATACB_VALID) == 0) {
1358 /*
1359 * must allocate and init the svc read queue.
1360 */
1361 DS_DBG_VLDS(CE_NOTE, "%s: hdl: 0x%lx initing recvq", __func__,
1362 hdl);
1363 dpsp = DS_MALLOC(sizeof (vlds_svc_info_t));
1364 vlds_recvq_init(dpsp);
1365 ds_cbarg_set_drv_per_svc_ptr(arg, dpsp);
1366 }
1367
1368 if ((flags & DSSF_REGCB_VALID) != 0) {
1369 ds_cbarg_get_drv_info(arg, &minor);
1370 sp = ddi_get_soft_state(vlds_statep, minor);
1371 ASSERT(sp != NULL);
1372 ASSERT(sp->evchan != NULL);
1373 ds_cbarg_get_domain(arg, &dhdl);
1374 ds_cbarg_get_service_id(arg, &servicep);
1375 DS_DBG_VLDS(CE_NOTE, "%s: regcb: hdl: 0x%lx, ver%d.%d, "
1376 " dhdl: 0x%lx", __func__, hdl, ver->major,
1377 ver->minor, dhdl);
1378 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
1379 nvlist_add_uint64(nvl, VLDS_HDL, hdl) ||
1380 nvlist_add_uint16(nvl, VLDS_VER_MAJOR, ver->major) ||
1381 nvlist_add_uint16(nvl, VLDS_VER_MINOR, ver->minor) ||
1382 nvlist_add_uint64(nvl, VLDS_DOMAIN_HDL, dhdl) ||
1383 nvlist_add_string(nvl, VLDS_SERVICE_ID, servicep) ||
1384 nvlist_add_boolean_value(nvl, VLDS_ISCLIENT,
1385 (flags & DSSF_ISCLIENT) != 0) ||
1386 sysevent_evc_publish(sp->evchan, EC_VLDS,
1387 ESC_VLDS_REGISTER, "sun.com", "kernel", nvl, EVCH_SLEEP)) {
1388 cmn_err(CE_WARN, "Failed to send REG Callback");
1389 } else {
1390 DS_DBG_VLDS(CE_NOTE, "%s: sysevent_evc_publish "
1391 "succeeded", __func__);
1392 }
1393 nvlist_free(nvl);
1394 }
1395 }
1396
1397 static void
vlds_user_unreg_cb(ds_cb_arg_t arg)1398 vlds_user_unreg_cb(ds_cb_arg_t arg)
1399 {
1400 nvlist_t *nvl = NULL;
1401 int minor;
1402 ds_svc_hdl_t hdl;
1403 vlds_state_t *sp;
1404 void *dpsp;
1405 uint32_t flags;
1406
1407 ds_cbarg_get_flags(arg, &flags);
1408 ASSERT((flags & DSSF_ISUSER) != 0);
1409
1410 if ((flags & DSSF_DATACB_VALID) == 0) {
1411 ds_cbarg_get_drv_per_svc_ptr(arg, &dpsp);
1412 if (dpsp) {
1413 DS_DBG_VLDS(CE_NOTE, "%s: unregcb draining recvq",
1414 __func__);
1415 vlds_recvq_drain(dpsp);
1416 vlds_recvq_destroy(dpsp);
1417 ds_cbarg_set_drv_per_svc_ptr(arg, NULL);
1418 }
1419 }
1420
1421 if ((flags & DSSF_UNREGCB_VALID) != 0) {
1422 ds_cbarg_get_hdl(arg, &hdl);
1423 DS_DBG_VLDS(CE_NOTE, "%s: unregcb hdl: 0x%lx", __func__,
1424 hdl);
1425 ds_cbarg_get_drv_info(arg, &minor);
1426 sp = ddi_get_soft_state(vlds_statep, minor);
1427 ASSERT(sp != NULL);
1428 ASSERT(sp->evchan != NULL);
1429 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
1430 nvlist_add_uint64(nvl, VLDS_HDL, hdl) ||
1431 sysevent_evc_publish(sp->evchan, EC_VLDS,
1432 ESC_VLDS_UNREGISTER, "sun.com", "kernel", nvl,
1433 EVCH_SLEEP)) {
1434 cmn_err(CE_WARN, "Failed to send UNREG Callback");
1435 }
1436 nvlist_free(nvl);
1437 }
1438 }
1439
1440 static void
vlds_user_data_cb(ds_cb_arg_t arg,void * buf,size_t buflen)1441 vlds_user_data_cb(ds_cb_arg_t arg, void *buf, size_t buflen)
1442 {
1443 nvlist_t *nvl = NULL;
1444 ds_svc_hdl_t hdl;
1445 int minor;
1446 void *dpsp;
1447 vlds_state_t *sp;
1448 uint32_t flags;
1449
1450 ds_cbarg_get_flags(arg, &flags);
1451 ASSERT((flags & DSSF_ISUSER) != 0);
1452
1453 if ((flags & DSSF_DATACB_VALID) == 0) {
1454 ds_cbarg_get_drv_per_svc_ptr(arg, &dpsp);
1455 ASSERT(dpsp != NULL);
1456 DS_DBG_VLDS(CE_NOTE, "%s: datacb: to recvq: buflen: %ld",
1457 __func__, buflen);
1458 (void) vlds_recvq_put_data(dpsp, buf, buflen);
1459 } else {
1460 ds_cbarg_get_hdl(arg, &hdl);
1461 DS_DBG_VLDS(CE_NOTE, "%s: datacb: usercb: hdl: 0x%lx, "
1462 " buflen: %ld", __func__, hdl, buflen);
1463 ds_cbarg_get_drv_info(arg, &minor);
1464 sp = ddi_get_soft_state(vlds_statep, minor);
1465 ASSERT(sp != NULL);
1466 ASSERT(sp->evchan != NULL);
1467 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
1468 nvlist_add_uint64(nvl, VLDS_HDL, hdl) ||
1469 nvlist_add_byte_array(nvl, VLDS_DATA, buf, buflen) ||
1470 sysevent_evc_publish(sp->evchan, EC_VLDS,
1471 ESC_VLDS_DATA, "sun.com", "kernel", nvl, EVCH_SLEEP)) {
1472 cmn_err(CE_WARN, "Failed to send DATA Callback");
1473 }
1474 }
1475 nvlist_free(nvl);
1476 }
1477
1478 /*
1479 * Initialize receive queue if request is from user land but
1480 * data callback is null (implying user will be using ds_recv_msg).
1481 */
1482 static void
vlds_recvq_init(vlds_svc_info_t * dpsp)1483 vlds_recvq_init(vlds_svc_info_t *dpsp)
1484 {
1485 dpsp->state = VLDS_RECV_OK;
1486 mutex_init(&dpsp->recv_lock, NULL, MUTEX_DRIVER, NULL);
1487 cv_init(&dpsp->recv_cv, NULL, CV_DRIVER, NULL);
1488 dpsp->recv_headp = NULL;
1489 dpsp->recv_tailp = NULL;
1490 dpsp->recv_size = 0;
1491 dpsp->recv_cnt = 0;
1492 }
1493
1494 static void
vlds_recvq_destroy(vlds_svc_info_t * dpsp)1495 vlds_recvq_destroy(vlds_svc_info_t *dpsp)
1496 {
1497 ASSERT(dpsp->state == VLDS_RECV_UNREG_PENDING);
1498 ASSERT(dpsp->recv_size == 0);
1499 ASSERT(dpsp->recv_cnt == 0);
1500 ASSERT(dpsp->recv_headp == NULL);
1501 ASSERT(dpsp->recv_tailp == NULL);
1502
1503 mutex_destroy(&dpsp->recv_lock);
1504 cv_destroy(&dpsp->recv_cv);
1505 DS_FREE(dpsp, sizeof (vlds_svc_info_t));
1506 }
1507
1508 static int
vlds_recvq_get_data(vlds_svc_info_t * dpsp,void * buf,size_t buflen,size_t * msglenp,int mode)1509 vlds_recvq_get_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen,
1510 size_t *msglenp, int mode)
1511 {
1512 vlds_recv_hdr_t *rhp;
1513 int rv;
1514 size_t msglen;
1515
1516 mutex_enter(&dpsp->recv_lock);
1517 while (dpsp->recv_size == 0) {
1518 ASSERT(dpsp->recv_cnt == 0);
1519 if (dpsp->state == VLDS_RECV_UNREG_PENDING)
1520 break;
1521
1522 if (dpsp->state == VLDS_RECV_OVERFLOW) {
1523 DS_DBG_RCVQ(CE_NOTE, "%s: user data queue overflow",
1524 __func__);
1525 dpsp->state = VLDS_RECV_OK;
1526 mutex_exit(&dpsp->recv_lock);
1527 return (ENOBUFS);
1528 }
1529 /*
1530 * Passing in a buflen of 0 allows user to poll for msgs.
1531 */
1532 if (buflen == 0) {
1533 mutex_exit(&dpsp->recv_lock);
1534 *msglenp = 0;
1535 return (EFBIG);
1536 }
1537 dpsp->recv_nreaders += 1;
1538 rv = cv_wait_sig(&dpsp->recv_cv, &dpsp->recv_lock);
1539 dpsp->recv_nreaders -= 1;
1540 if (rv == 0) {
1541 DS_DBG_RCVQ(CE_NOTE, "%s: signal EINTR", __func__);
1542 mutex_exit(&dpsp->recv_lock);
1543 return (EINTR);
1544 }
1545 }
1546 if (dpsp->state == VLDS_RECV_UNREG_PENDING) {
1547 DS_DBG_RCVQ(CE_NOTE, "%s: unreg pending", __func__);
1548 cv_broadcast(&dpsp->recv_cv);
1549 mutex_exit(&dpsp->recv_lock);
1550 return (EINVAL);
1551 }
1552 ASSERT(dpsp->recv_headp != NULL);
1553 rhp = dpsp->recv_headp;
1554
1555 /*
1556 * Don't transfer truncated data, return EFBIG error if user-supplied
1557 * buffer is too small.
1558 */
1559 if (rhp->datasz > buflen) {
1560 *msglenp = rhp->datasz;
1561 mutex_exit(&dpsp->recv_lock);
1562 return (EFBIG);
1563 }
1564 if (rhp == dpsp->recv_tailp) {
1565 dpsp->recv_headp = NULL;
1566 dpsp->recv_tailp = NULL;
1567 } else {
1568 dpsp->recv_headp = rhp->next;
1569 ASSERT(dpsp->recv_headp != NULL);
1570 }
1571 ASSERT(dpsp->recv_cnt > 0);
1572 dpsp->recv_size -= rhp->datasz;
1573 dpsp->recv_cnt -= 1;
1574 mutex_exit(&dpsp->recv_lock);
1575
1576 msglen = rhp->datasz;
1577 rv = ddi_copyout(rhp->data, buf, msglen, mode);
1578
1579 if (rv == 0) {
1580 DS_DBG_VLDS(CE_NOTE, "%s: user data dequeued msglen: %ld",
1581 __func__, rhp->datasz);
1582 DS_DUMP_MSG(DS_DBG_FLAG_VLDS, rhp->data, rhp->datasz);
1583 }
1584
1585 DS_FREE(rhp->data, rhp->datasz);
1586 DS_FREE(rhp, sizeof (vlds_recv_hdr_t));
1587
1588 if (rv != 0) {
1589 DS_DBG_VLDS(CE_NOTE, "%s: copyout failed", __func__);
1590 return (EFAULT);
1591 }
1592
1593 *msglenp = msglen;
1594 return (0);
1595 }
1596
1597 uint64_t vlds_recv_drain_delay_time = 1 * MILLISEC;
1598
1599 static void
vlds_recvq_drain(vlds_svc_info_t * dpsp)1600 vlds_recvq_drain(vlds_svc_info_t *dpsp)
1601 {
1602 vlds_recv_hdr_t *rhp, *nextp;
1603
1604 mutex_enter(&dpsp->recv_lock);
1605 dpsp->state = VLDS_RECV_UNREG_PENDING;
1606 for (rhp = dpsp->recv_tailp; rhp != NULL; rhp = nextp) {
1607 nextp = rhp->next;
1608 DS_FREE(rhp->data, rhp->datasz);
1609 DS_FREE(rhp, sizeof (vlds_recv_hdr_t));
1610 }
1611 dpsp->recv_headp = NULL;
1612 dpsp->recv_tailp = NULL;
1613 dpsp->recv_size = 0;
1614 dpsp->recv_cnt = 0;
1615
1616 /*
1617 * Make sure other readers have exited.
1618 */
1619 while (dpsp->recv_nreaders > 0) {
1620 cv_broadcast(&dpsp->recv_cv);
1621 mutex_exit(&dpsp->recv_lock);
1622 delay(vlds_recv_drain_delay_time);
1623 mutex_enter(&dpsp->recv_lock);
1624 }
1625
1626 mutex_exit(&dpsp->recv_lock);
1627 }
1628
1629 static int
vlds_recvq_put_data(vlds_svc_info_t * dpsp,void * buf,size_t buflen)1630 vlds_recvq_put_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen)
1631 {
1632 vlds_recv_hdr_t *rhp;
1633
1634 mutex_enter(&dpsp->recv_lock);
1635 if (dpsp->state != VLDS_RECV_UNREG_PENDING) {
1636 /*
1637 * If we've already encountered an overflow, or there
1638 * are pending messages and either queue size and
1639 * message limits will be exceeded with this message,
1640 * we mark the recvq as overflowed and return an ENOBUFS
1641 * error. This allows the enqueuing of one big message
1642 * or several little messages.
1643 */
1644 if ((dpsp->state == VLDS_RECV_OVERFLOW) ||
1645 ((dpsp->recv_cnt != 0) &&
1646 ((dpsp->recv_size + buflen) > vlds_recvq_maxsize) ||
1647 ((dpsp->recv_cnt + 1) > vlds_recvq_maxmsg))) {
1648 DS_DBG_RCVQ(CE_NOTE, "%s: user data queue overflow",
1649 __func__);
1650 dpsp->state = VLDS_RECV_OVERFLOW;
1651 cv_broadcast(&dpsp->recv_cv);
1652 mutex_exit(&dpsp->recv_lock);
1653 return (ENOBUFS);
1654 }
1655
1656 DS_DBG_RCVQ(CE_NOTE, "%s: user data enqueued msglen: %ld",
1657 __func__, buflen);
1658 DS_DUMP_MSG(DS_DBG_FLAG_RCVQ, buf, buflen);
1659 rhp = DS_MALLOC(sizeof (vlds_recv_hdr_t));
1660 rhp->data = DS_MALLOC(buflen);
1661 (void) memcpy(rhp->data, buf, buflen);
1662 rhp->datasz = buflen;
1663 rhp->next = NULL;
1664 if (dpsp->recv_headp == NULL) {
1665 dpsp->recv_headp = rhp;
1666 dpsp->recv_tailp = rhp;
1667 } else {
1668 dpsp->recv_tailp->next = rhp;
1669 dpsp->recv_tailp = rhp;
1670 }
1671 dpsp->recv_size += rhp->datasz;
1672 dpsp->recv_cnt += 1;
1673 cv_broadcast(&dpsp->recv_cv);
1674 }
1675 mutex_exit(&dpsp->recv_lock);
1676 return (0);
1677 }
1678
1679 static int
vlds_recv_msg(ds_svc_hdl_t hdl,void * buf,size_t buflen,size_t * msglenp,int mode)1680 vlds_recv_msg(ds_svc_hdl_t hdl, void *buf, size_t buflen, size_t *msglenp,
1681 int mode)
1682 {
1683 void *dpsp;
1684 ds_cb_arg_t cbarg;
1685 uint32_t flags;
1686 int rv;
1687
1688 if ((rv = ds_hdl_get_cbarg(hdl, &cbarg)) != 0) {
1689 DS_DBG_VLDS(CE_NOTE, "%s: handle %lx not found (%d)", __func__,
1690 hdl, rv);
1691 return (rv);
1692 }
1693 ds_cbarg_get_flags(cbarg, &flags);
1694 if ((flags & DSSF_ISUSER) == 0 || (flags & DSSF_DATACB_VALID) != 0) {
1695 DS_DBG_VLDS(CE_NOTE, "%s: invalid flags: %x", __func__, flags);
1696 return (EINVAL);
1697 }
1698 ds_cbarg_get_drv_per_svc_ptr(cbarg, &dpsp);
1699 if (dpsp == NULL) {
1700 DS_DBG_VLDS(CE_NOTE, "%s: recv on non-ready handle: %x",
1701 __func__, flags);
1702 return (ENXIO);
1703 }
1704 rv = vlds_recvq_get_data(dpsp, buf, buflen, msglenp, mode);
1705 return (rv);
1706 }
1707