xref: /illumos-gate/usr/src/uts/sun4v/io/vlds.c (revision 3e3ca1ce307a7d82ef007b435088f3f04a0f3e65)
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 *
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
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
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
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
261 vlds_minor_init(void)
262 {
263 	mutex_init(&vlds_minor_mutex, NULL, MUTEX_DEFAULT, NULL);
264 }
265 
266 int
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
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
299 _info(struct modinfo *modinfop)
300 {
301 	return (mod_info(&modlinkage, modinfop));
302 }
303 
304 
305 
306 /*ARGSUSED*/
307 static int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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