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