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