xref: /titanic_44/usr/src/uts/sun4v/io/vlds.c (revision af4c679f647cf088543c762e33d41a3ac52cfa14)
130588217SMike Christensen /*
230588217SMike Christensen  * CDDL HEADER START
330588217SMike Christensen  *
430588217SMike Christensen  * The contents of this file are subject to the terms of the
530588217SMike Christensen  * Common Development and Distribution License (the "License").
630588217SMike Christensen  * You may not use this file except in compliance with the License.
730588217SMike Christensen  *
830588217SMike Christensen  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
930588217SMike Christensen  * or http://www.opensolaris.org/os/licensing.
1030588217SMike Christensen  * See the License for the specific language governing permissions
1130588217SMike Christensen  * and limitations under the License.
1230588217SMike Christensen  *
1330588217SMike Christensen  * When distributing Covered Code, include this CDDL HEADER in each
1430588217SMike Christensen  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1530588217SMike Christensen  * If applicable, add the following below this CDDL HEADER, with the
1630588217SMike Christensen  * fields enclosed by brackets "[]" replaced with your own identifying
1730588217SMike Christensen  * information: Portions Copyright [yyyy] [name of copyright owner]
1830588217SMike Christensen  *
1930588217SMike Christensen  * CDDL HEADER END
2030588217SMike Christensen  */
2130588217SMike Christensen 
2230588217SMike Christensen /*
2349b225e1SGavin Maltby  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2430588217SMike Christensen  * Use is subject to license terms.
2530588217SMike Christensen  */
2630588217SMike Christensen 
2730588217SMike Christensen /*
2830588217SMike Christensen  * LDOMs Domain Services Device Driver
2930588217SMike Christensen  */
3030588217SMike Christensen #include <sys/types.h>
3130588217SMike Christensen #include <sys/file.h>
3230588217SMike Christensen #include <sys/errno.h>
3330588217SMike Christensen #include <sys/open.h>
3430588217SMike Christensen #include <sys/cred.h>
3530588217SMike Christensen #include <sys/uio.h>
3630588217SMike Christensen #include <sys/stat.h>
3730588217SMike Christensen #include <sys/ksynch.h>
3830588217SMike Christensen #include <sys/modctl.h>
3930588217SMike Christensen #include <sys/conf.h>
4030588217SMike Christensen #include <sys/devops.h>
4130588217SMike Christensen #include <sys/debug.h>
4230588217SMike Christensen #include <sys/cmn_err.h>
4330588217SMike Christensen #include <sys/ddi.h>
4430588217SMike Christensen #include <sys/sunddi.h>
4530588217SMike Christensen #include <sys/taskq.h>
4630588217SMike Christensen #include <sys/disp.h>
4730588217SMike Christensen #include <sys/note.h>
4830588217SMike Christensen #include <sys/mach_descrip.h>
4930588217SMike Christensen #include <sys/mdesc.h>
5030588217SMike Christensen #include <sys/mdeg.h>
5130588217SMike Christensen #include <sys/ldc.h>
5230588217SMike Christensen #include <sys/ds.h>
5330588217SMike Christensen #include <sys/ds_impl.h>
5430588217SMike Christensen #include <sys/vlds.h>
5530588217SMike Christensen #include <sys/bitmap.h>
5630588217SMike Christensen #include <sys/sysevent.h>
5730588217SMike Christensen 
5830588217SMike Christensen static dev_info_t *vlds_devi;
5930588217SMike Christensen 
6030588217SMike Christensen 
6130588217SMike Christensen typedef struct vlds_state {
6230588217SMike Christensen 	dev_info_t	*dip;
6330588217SMike Christensen 	int		instance;
6430588217SMike Christensen 	evchan_t	*evchan;
6530588217SMike Christensen } vlds_state_t;
6630588217SMike Christensen 
6730588217SMike Christensen static void *vlds_statep;
6830588217SMike Christensen 
6930588217SMike Christensen typedef struct vlds_recv_hdr {
7030588217SMike Christensen 	struct vlds_recv_hdr	*next;		/* next in recv list */
7130588217SMike Christensen 	void			*data;		/* the data itself */
7230588217SMike Christensen 	size_t			datasz;		/* size of the data */
7330588217SMike Christensen } vlds_recv_hdr_t;
7430588217SMike Christensen 
7530588217SMike Christensen typedef struct vlds_svc_info {
7630588217SMike Christensen 	int		state;		/* driver svc info state VLDS_RECV* */
7730588217SMike Christensen 	vlds_recv_hdr_t	*recv_headp;	/* ptr to head of recv queue */
7830588217SMike Christensen 	vlds_recv_hdr_t	*recv_tailp;	/* ptr to tail of recv queue */
7930588217SMike Christensen 	size_t		recv_size;	/* no. of bytes in recv queue */
80ba9236fbSMike Christensen 	uint_t		recv_cnt;	/* no of messages in recv queue */
8130588217SMike Christensen 	kmutex_t	recv_lock;	/* lock for recv queue */
8230588217SMike Christensen 	kcondvar_t	recv_cv;	/* condition variable for recv queue */
8330588217SMike Christensen 	int		recv_nreaders;	/* no of currently waiting readers */
8430588217SMike Christensen } vlds_svc_info_t;
8530588217SMike Christensen 
8630588217SMike Christensen #define	VLDS_RECV_OK		1
8730588217SMike Christensen #define	VLDS_RECV_UNREG_PENDING	2
88ba9236fbSMike Christensen #define	VLDS_RECV_OVERFLOW	3
8930588217SMike Christensen 
9030588217SMike Christensen static int vlds_ports_inited = 0;
9130588217SMike Christensen 
9230588217SMike Christensen static uint_t vlds_flags_to_svc(uint64_t flags);
9330588217SMike Christensen 
9430588217SMike Christensen 
9530588217SMike Christensen #define	VLDS_NAME		"vlds"
9630588217SMike Christensen static int vlds_open(dev_t *devp, int flag, int otyp, cred_t *credp);
9730588217SMike Christensen static int vlds_close(dev_t dev, int flag, int otyp, cred_t *credp);
9830588217SMike Christensen static int vlds_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
9930588217SMike Christensen     int *rvalp);
10030588217SMike Christensen static int vlds_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
10130588217SMike Christensen     void **resultp);
10230588217SMike Christensen static int vlds_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
10330588217SMike Christensen static int vlds_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
10430588217SMike Christensen 
10530588217SMike Christensen /* mdeg register functions */
106a600f50dSMike Christensen static void vlds_mdeg_init(void);
107a600f50dSMike Christensen static int vlds_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
108a600f50dSMike Christensen static int vlds_mdeg_register(void);
109a600f50dSMike Christensen static int vlds_mdeg_unregister(void);
110a600f50dSMike Christensen static int vlds_add_mdeg_port(md_t *mdp, mde_cookie_t node);
11130588217SMike Christensen 
11230588217SMike Christensen /* driver utilities */
11330588217SMike Christensen static void vlds_user_reg_cb(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
11430588217SMike Christensen static void vlds_user_unreg_cb(ds_cb_arg_t arg);
11530588217SMike Christensen static void vlds_user_data_cb(ds_cb_arg_t arg, void *buf, size_t buflen);
11630588217SMike Christensen static void vlds_recvq_init(vlds_svc_info_t *dpsp);
11730588217SMike Christensen static void vlds_recvq_destroy(vlds_svc_info_t *dpsp);
11830588217SMike Christensen static int vlds_recvq_get_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen,
11930588217SMike Christensen     size_t *msglenp, int mode);
12030588217SMike Christensen static void vlds_recvq_drain(vlds_svc_info_t *dpsp);
12130588217SMike Christensen static int vlds_recvq_put_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen);
12230588217SMike Christensen static int vlds_recv_msg(ds_svc_hdl_t hdl, void *buf, size_t buflen,
12330588217SMike Christensen     size_t *msglenp, int mode);
12430588217SMike Christensen 
12530588217SMike Christensen /*
12630588217SMike Christensen  * DS driver Ops Vector
12730588217SMike Christensen  */
12830588217SMike Christensen static struct cb_ops vlds_cb_ops = {
12930588217SMike Christensen 	vlds_open,		/* cb_open */
13030588217SMike Christensen 	vlds_close,		/* cb_close */
13130588217SMike Christensen 	nodev,			/* cb_strategy */
13230588217SMike Christensen 	nodev,			/* cb_print */
13330588217SMike Christensen 	nodev,			/* cb_dump */
13430588217SMike Christensen 	nodev,			/* cb_read */
13530588217SMike Christensen 	nodev,			/* cb_write */
13630588217SMike Christensen 	vlds_ioctl,		/* cb_ioctl */
13730588217SMike Christensen 	nodev,			/* cb_devmap */
13830588217SMike Christensen 	nodev,			/* cb_mmap */
13930588217SMike Christensen 	nodev,			/* cb_segmap */
14030588217SMike Christensen 	nochpoll,		/* cb_chpoll */
14130588217SMike Christensen 	ddi_prop_op,		/* cb_prop_op */
14230588217SMike Christensen 	(struct streamtab *)NULL, /* cb_str */
14330588217SMike Christensen 	D_MP | D_64BIT,		/* cb_flag */
14430588217SMike Christensen 	CB_REV,			/* cb_rev */
14530588217SMike Christensen 	nodev,			/* cb_aread */
14630588217SMike Christensen 	nodev			/* cb_awrite */
14730588217SMike Christensen };
14830588217SMike Christensen 
14930588217SMike Christensen static struct dev_ops vlds_dev_ops = {
15030588217SMike Christensen 	DEVO_REV,		/* devo_rev */
15130588217SMike Christensen 	0,			/* devo_refcnt */
15230588217SMike Christensen 	vlds_getinfo,		/* devo_getinfo */
15330588217SMike Christensen 	nulldev,		/* devo_identify */
15430588217SMike Christensen 	nulldev,		/* devo_probe */
15530588217SMike Christensen 	vlds_attach,		/* devo_attach */
15630588217SMike Christensen 	vlds_detach,		/* devo_detach */
15730588217SMike Christensen 	nodev,			/* devo_reset */
15830588217SMike Christensen 	&vlds_cb_ops,		/* devo_cb_ops */
15930588217SMike Christensen 	(struct bus_ops *)NULL,	/* devo_bus_ops */
16030588217SMike Christensen 	nulldev			/* devo_power */
16130588217SMike Christensen };
16230588217SMike Christensen 
16330588217SMike Christensen static struct modldrv modldrv = {
16430588217SMike Christensen 	&mod_driverops,
16530588217SMike Christensen 	"Domain Services Driver 1.0",
16630588217SMike Christensen 	&vlds_dev_ops
16730588217SMike Christensen };
16830588217SMike Christensen 
16930588217SMike Christensen static struct modlinkage modlinkage = {
17030588217SMike Christensen 	MODREV_1,
17130588217SMike Christensen 	(void *)&modldrv,
17230588217SMike Christensen 	NULL
17330588217SMike Christensen };
17430588217SMike Christensen 
17530588217SMike Christensen /*
17630588217SMike Christensen  * Callback ops for user-land services.
17730588217SMike Christensen  */
17830588217SMike Christensen static ds_clnt_ops_t ds_user_ops = {
17930588217SMike Christensen 	vlds_user_reg_cb,		/* register */
18030588217SMike Christensen 	vlds_user_unreg_cb,		/* unregister */
18130588217SMike Christensen 	vlds_user_data_cb,		/* data */
18230588217SMike Christensen 	NULL				/* ds_ucap_init will fill in */
18330588217SMike Christensen };
18430588217SMike Christensen 
185ba9236fbSMike Christensen static size_t vlds_recvq_maxsize = DS_STREAM_MTU * 8;
186ba9236fbSMike Christensen static uint_t vlds_recvq_maxmsg = 16;
187ba9236fbSMike Christensen 
18830588217SMike Christensen #define	VLDS_MINOR_MAX			SHRT_MAX
18930588217SMike Christensen 
19030588217SMike Christensen /* Definitions for binding handle array */
19130588217SMike Christensen static ulong_t vlds_bitmap_initial = 1;	/* index 0 indicates error */
19230588217SMike Christensen static ulong_t *vlds_minor_bitmap = &vlds_bitmap_initial;
19330588217SMike Christensen static size_t vlds_minor_bits = BT_NBIPUL;
19430588217SMike Christensen static kmutex_t vlds_minor_mutex;
19530588217SMike Christensen 
19630588217SMike Christensen /*
19730588217SMike Christensen  * Following vlds_minor_* routines map a binding handle to a minor number.
19830588217SMike Christensen  * Has to be called w/ locks held.
19930588217SMike Christensen  */
20030588217SMike Christensen static ulong_t *
vlds_minor_alloc(void)20130588217SMike Christensen vlds_minor_alloc(void)
20230588217SMike Christensen {
20330588217SMike Christensen 	ulong_t *bhst = vlds_minor_bitmap;
20430588217SMike Christensen 
20530588217SMike Christensen 	/* Increase bitmap by one BT_NBIPUL */
20630588217SMike Christensen 	if (vlds_minor_bits + BT_NBIPUL > VLDS_MINOR_MAX) {
20730588217SMike Christensen 		return ((ulong_t *)NULL);
20830588217SMike Christensen 	}
20930588217SMike Christensen 	vlds_minor_bitmap = kmem_zalloc(
21030588217SMike Christensen 	    BT_SIZEOFMAP(vlds_minor_bits + BT_NBIPUL), KM_SLEEP);
21130588217SMike Christensen 	bcopy(bhst, vlds_minor_bitmap, BT_SIZEOFMAP(vlds_minor_bits));
21230588217SMike Christensen 	if (bhst != &vlds_bitmap_initial)
21330588217SMike Christensen 		kmem_free(bhst, BT_SIZEOFMAP(vlds_minor_bits));
21430588217SMike Christensen 	vlds_minor_bits += BT_NBIPUL;
21530588217SMike Christensen 
21630588217SMike Christensen 	return (vlds_minor_bitmap);
21730588217SMike Christensen }
21830588217SMike Christensen 
21930588217SMike Christensen static void
vlds_minor_free(ulong_t * bitmap)22030588217SMike Christensen vlds_minor_free(ulong_t *bitmap)
22130588217SMike Christensen {
22230588217SMike Christensen 	if (bitmap != &vlds_bitmap_initial)
22330588217SMike Christensen 		kmem_free(bitmap, BT_SIZEOFMAP(vlds_minor_bits));
22430588217SMike Christensen }
22530588217SMike Christensen 
22630588217SMike Christensen static index_t
vlds_minor_get(void)22730588217SMike Christensen vlds_minor_get(void)
22830588217SMike Christensen {
22930588217SMike Christensen 	index_t idx;
23030588217SMike Christensen 	ulong_t *bhst;
23130588217SMike Christensen 
23230588217SMike Christensen 	/* Search for an available index */
23330588217SMike Christensen 	mutex_enter(&vlds_minor_mutex);
23430588217SMike Christensen 	if ((idx = bt_availbit(vlds_minor_bitmap,
23530588217SMike Christensen 	    vlds_minor_bits)) == -1) {
23630588217SMike Christensen 		/* All busy - allocate additional binding handle bitmap space */
23730588217SMike Christensen 		if ((bhst = vlds_minor_alloc()) == NULL) {
23830588217SMike Christensen 			/* Reached our maximum of id's == SHRT_MAX */
23930588217SMike Christensen 			mutex_exit(&vlds_minor_mutex);
24030588217SMike Christensen 			return (0);
24130588217SMike Christensen 		} else {
24230588217SMike Christensen 			vlds_minor_bitmap = bhst;
24330588217SMike Christensen 		}
24430588217SMike Christensen 		idx = bt_availbit(vlds_minor_bitmap, vlds_minor_bits);
24530588217SMike Christensen 	}
24630588217SMike Christensen 	BT_SET(vlds_minor_bitmap, idx);
24730588217SMike Christensen 	mutex_exit(&vlds_minor_mutex);
24830588217SMike Christensen 	return (idx);
24930588217SMike Christensen }
25030588217SMike Christensen 
25130588217SMike Christensen static void
vlds_minor_rele(index_t idx)25230588217SMike Christensen vlds_minor_rele(index_t idx)
25330588217SMike Christensen {
25430588217SMike Christensen 	mutex_enter(&vlds_minor_mutex);
25530588217SMike Christensen 	ASSERT(BT_TEST(vlds_minor_bitmap, idx) == 1);
25630588217SMike Christensen 	BT_CLEAR(vlds_minor_bitmap, idx);
25730588217SMike Christensen 	mutex_exit(&vlds_minor_mutex);
25830588217SMike Christensen }
25930588217SMike Christensen 
26030588217SMike Christensen static void
vlds_minor_init(void)26130588217SMike Christensen vlds_minor_init(void)
26230588217SMike Christensen {
26330588217SMike Christensen 	mutex_init(&vlds_minor_mutex, NULL, MUTEX_DEFAULT, NULL);
26430588217SMike Christensen }
26530588217SMike Christensen 
26630588217SMike Christensen int
_init(void)26730588217SMike Christensen _init(void)
26830588217SMike Christensen {
26930588217SMike Christensen 	int s;
27030588217SMike Christensen 
27130588217SMike Christensen 	if ((s = ddi_soft_state_init(&vlds_statep, sizeof (vlds_state_t), 0))
27230588217SMike Christensen 	    != 0)
27330588217SMike Christensen 		return (s);
27430588217SMike Christensen 
27530588217SMike Christensen 	if ((s = mod_install(&modlinkage)) != 0) {
27630588217SMike Christensen 		ddi_soft_state_fini(&vlds_statep);
27730588217SMike Christensen 		return (s);
27830588217SMike Christensen 	}
27930588217SMike Christensen 
280a600f50dSMike Christensen 	vlds_mdeg_init();
28130588217SMike Christensen 
28230588217SMike Christensen 	return (s);
28330588217SMike Christensen }
28430588217SMike Christensen 
28530588217SMike Christensen int
_fini(void)28630588217SMike Christensen _fini(void)
28730588217SMike Christensen {
28830588217SMike Christensen 	int s;
28930588217SMike Christensen 
29030588217SMike Christensen 	if ((s = mod_remove(&modlinkage)) != 0)
29130588217SMike Christensen 		return (s);
29230588217SMike Christensen 
29330588217SMike Christensen 	ddi_soft_state_fini(&vlds_statep);
29430588217SMike Christensen 
29530588217SMike Christensen 	return (s);
29630588217SMike Christensen }
29730588217SMike Christensen 
29830588217SMike Christensen int
_info(struct modinfo * modinfop)29930588217SMike Christensen _info(struct modinfo *modinfop)
30030588217SMike Christensen {
30130588217SMike Christensen 	return (mod_info(&modlinkage, modinfop));
30230588217SMike Christensen }
30330588217SMike Christensen 
30430588217SMike Christensen 
30530588217SMike Christensen 
30630588217SMike Christensen /*ARGSUSED*/
30730588217SMike Christensen static int
vlds_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)30830588217SMike Christensen vlds_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
30930588217SMike Christensen {
31030588217SMike Christensen 	switch (cmd) {
31130588217SMike Christensen 	case DDI_INFO_DEVT2DEVINFO:
31230588217SMike Christensen 		*resultp = vlds_devi;
31330588217SMike Christensen 		return (DDI_SUCCESS);
31430588217SMike Christensen 	case DDI_INFO_DEVT2INSTANCE:
31530588217SMike Christensen 		*resultp = 0;
31630588217SMike Christensen 		return (DDI_SUCCESS);
31730588217SMike Christensen 	}
31830588217SMike Christensen 	return (DDI_FAILURE);
31930588217SMike Christensen }
32030588217SMike Christensen 
32130588217SMike Christensen 
32230588217SMike Christensen static int
vlds_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)32330588217SMike Christensen vlds_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
32430588217SMike Christensen {
32530588217SMike Christensen 	if (cmd != DDI_ATTACH) {
32630588217SMike Christensen 		return (DDI_FAILURE);
32730588217SMike Christensen 	}
32830588217SMike Christensen 
32930588217SMike Christensen 	if (ddi_create_minor_node(devi, VLDS_NAME, S_IFCHR,
33030588217SMike Christensen 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
33130588217SMike Christensen 		ddi_remove_minor_node(devi, NULL);
33230588217SMike Christensen 		return (DDI_FAILURE);
33330588217SMike Christensen 	}
33430588217SMike Christensen 	vlds_devi = devi;
33530588217SMike Christensen 
33630588217SMike Christensen 	vlds_minor_init();
33730588217SMike Christensen 
338*af4c679fSSean McEnroe 	(void) vlds_mdeg_register();
339*af4c679fSSean McEnroe 
34030588217SMike Christensen 	return (DDI_SUCCESS);
34130588217SMike Christensen }
34230588217SMike Christensen 
34330588217SMike Christensen 
34430588217SMike Christensen /*ARGSUSED*/
34530588217SMike Christensen static int
vlds_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)34630588217SMike Christensen vlds_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
34730588217SMike Christensen {
34830588217SMike Christensen 	if (cmd != DDI_DETACH) {
34930588217SMike Christensen 		return (DDI_FAILURE);
35030588217SMike Christensen 	}
35130588217SMike Christensen 
35230588217SMike Christensen 	vlds_minor_free(vlds_minor_bitmap);
35330588217SMike Christensen 	ddi_remove_minor_node(devi, NULL);
354*af4c679fSSean McEnroe 	(void) vlds_mdeg_unregister();
35530588217SMike Christensen 	return (DDI_SUCCESS);
35630588217SMike Christensen }
35730588217SMike Christensen 
35830588217SMike Christensen 
35930588217SMike Christensen /*ARGSUSED*/
36030588217SMike Christensen static int
vlds_open(dev_t * devp,int flag,int otyp,cred_t * credp)36130588217SMike Christensen vlds_open(dev_t *devp, int flag, int otyp, cred_t *credp)
36230588217SMike Christensen {
36330588217SMike Christensen 	int minor;
36430588217SMike Christensen 
36530588217SMike Christensen 	if (otyp != OTYP_CHR)
36630588217SMike Christensen 		return (EINVAL);
36730588217SMike Christensen 
36830588217SMike Christensen 	if (getminor(*devp) != 0)
36930588217SMike Christensen 		return (ENXIO);
37030588217SMike Christensen 
37130588217SMike Christensen 	minor = vlds_minor_get();
37230588217SMike Christensen 	if (minor == 0)
37330588217SMike Christensen 		/* All minors are busy */
37430588217SMike Christensen 		return (EBUSY);
37530588217SMike Christensen 
37630588217SMike Christensen 	if (ddi_soft_state_zalloc(vlds_statep, minor) != DDI_SUCCESS) {
37730588217SMike Christensen 		vlds_minor_rele(minor);
37830588217SMike Christensen 		return (ENOMEM);
37930588217SMike Christensen 	}
38030588217SMike Christensen 
38130588217SMike Christensen 	*devp = makedevice(getmajor(*devp), minor);
38230588217SMike Christensen 
38330588217SMike Christensen 	return (0);
38430588217SMike Christensen }
38530588217SMike Christensen 
38630588217SMike Christensen 
38730588217SMike Christensen /*ARGSUSED*/
38830588217SMike Christensen static int
vlds_close(dev_t dev,int flag,int otyp,cred_t * credp)38930588217SMike Christensen vlds_close(dev_t dev, int flag, int otyp, cred_t *credp)
39030588217SMike Christensen {
39130588217SMike Christensen 	int minor = (int)getminor(dev);
39230588217SMike Christensen 	vlds_state_t *sp;
39330588217SMike Christensen 
39430588217SMike Christensen 	DS_DBG_VLDS(CE_NOTE, "vlds_close");
39530588217SMike Christensen 
39630588217SMike Christensen 	/*
39730588217SMike Christensen 	 * Unregister all handles associated with this process.
39830588217SMike Christensen 	 */
39930588217SMike Christensen 	ds_unreg_all(minor);
40030588217SMike Christensen 
40130588217SMike Christensen 	if (otyp != OTYP_CHR)
40230588217SMike Christensen 		return (EINVAL);
40330588217SMike Christensen 
40430588217SMike Christensen 	sp = ddi_get_soft_state(vlds_statep, minor);
40530588217SMike Christensen 	if (sp == NULL) {
40630588217SMike Christensen 		return (ENXIO);
40730588217SMike Christensen 	}
40830588217SMike Christensen 
40930588217SMike Christensen 	if (sp->evchan) {
41049b225e1SGavin Maltby 		(void) sysevent_evc_unbind(sp->evchan);
41130588217SMike Christensen 		sp->evchan = NULL;
41230588217SMike Christensen 	}
41330588217SMike Christensen 
41430588217SMike Christensen 	ddi_soft_state_free(vlds_statep, minor);
41530588217SMike Christensen 	vlds_minor_rele(minor);
41630588217SMike Christensen 
41730588217SMike Christensen 	return (0);
41830588217SMike Christensen }
41930588217SMike Christensen 
42030588217SMike Christensen int
vlds_init_sysevent(vlds_state_t * sp,uint32_t flags)42130588217SMike Christensen vlds_init_sysevent(vlds_state_t *sp, uint32_t flags)
42230588217SMike Christensen {
42330588217SMike Christensen 	char evchan_name[MAX_CHNAME_LEN];
42430588217SMike Christensen 	int rv;
42530588217SMike Christensen 
42630588217SMike Christensen 	if (flags & DSSF_ANYCB_VALID) {
42730588217SMike Christensen 		if (sp->evchan) {
42830588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: sysevent already bound",
42930588217SMike Christensen 			    __func__);
43030588217SMike Christensen 			return (0);
43130588217SMike Christensen 		}
43230588217SMike Christensen 		(void) sprintf(evchan_name, VLDS_SYSEV_CHAN_FMT, ddi_get_pid());
43330588217SMike Christensen 		if ((rv = sysevent_evc_bind(evchan_name, &sp->evchan,
43430588217SMike Christensen 		    EVCH_CREAT|EVCH_HOLD_PEND)) != 0) {
43530588217SMike Christensen 			cmn_err(CE_WARN, "%s: can't bind to '%s' (%d)",
43630588217SMike Christensen 			    __func__, evchan_name, rv);
43730588217SMike Christensen 			return (rv);
43830588217SMike Christensen 		}
43930588217SMike Christensen 
44030588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: sysevent bind to '%s' successful",
44130588217SMike Christensen 		    __func__, evchan_name);
44230588217SMike Christensen 	}
44330588217SMike Christensen 	return (0);
44430588217SMike Christensen }
44530588217SMike Christensen 
44630588217SMike Christensen #define	ARGTOPTR(x)	((void *)((uintptr_t)(x)))
44730588217SMike Christensen #define	ARGTOUINT(x)	((uint_t)(x))
44830588217SMike Christensen #define	ARGTOINT(x)	((int)(x))
44930588217SMike Christensen 
45030588217SMike Christensen static int
vlds_get_string(vlds_string_t * strp,char ** rstrp,int mode)45130588217SMike Christensen vlds_get_string(vlds_string_t *strp, char **rstrp, int mode)
45230588217SMike Christensen {
45330588217SMike Christensen 	char *str;
45430588217SMike Christensen 	uint_t len = strp->vlds_strlen;
45530588217SMike Christensen 	uint_t slen;
45630588217SMike Christensen 
45730588217SMike Christensen 	if (len == 0) {
45830588217SMike Christensen 		*rstrp = NULL;
45930588217SMike Christensen 		return (0);
46030588217SMike Christensen 	}
46130588217SMike Christensen 	if (len > MAXNAMELEN) {
46230588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: invalid string length: %d", __func__,
46330588217SMike Christensen 		    len);
46430588217SMike Christensen 		return (EINVAL);
46530588217SMike Christensen 	}
46630588217SMike Christensen 	str = DS_MALLOC(len);
46730588217SMike Christensen 	if (ddi_copyin(ARGTOPTR(strp->vlds_strp), str, len, mode) != 0) {
46830588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: ddi copyin failed (%p)", __func__,
46930588217SMike Christensen 		    ARGTOPTR(strp->vlds_strp));
47030588217SMike Christensen 		DS_FREE(str, len);
47130588217SMike Christensen 		return (EFAULT);
47230588217SMike Christensen 	}
47330588217SMike Christensen 	slen = strlen(str) + 1;
47430588217SMike Christensen 	if (slen != len) {
47530588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: invalid string len: %d != len: %d",
47630588217SMike Christensen 		    __func__, slen, len);
47730588217SMike Christensen 		DS_FREE(str, len);
47830588217SMike Christensen 		return (EINVAL);
47930588217SMike Christensen 	}
48030588217SMike Christensen 	*rstrp = str;
48130588217SMike Christensen 	return (0);
48230588217SMike Christensen }
48330588217SMike Christensen 
48430588217SMike Christensen static int
vlds_put_string(char * str,vlds_string_t * strp,int mode)48530588217SMike Christensen vlds_put_string(char *str, vlds_string_t *strp, int mode)
48630588217SMike Christensen {
48730588217SMike Christensen 	uint_t len;
48830588217SMike Christensen 	char *tstr = NULL;
48930588217SMike Christensen 	int rv;
49030588217SMike Christensen 
49130588217SMike Christensen 	if (str == NULL) {
49230588217SMike Christensen 		str = "";
49330588217SMike Christensen 	}
49430588217SMike Christensen 	len = strlen(str) + 1;
49530588217SMike Christensen 
49630588217SMike Christensen 	/*
49730588217SMike Christensen 	 * If string is longer than user buffer, return a
49830588217SMike Christensen 	 * truncated, null-terminated string.
49930588217SMike Christensen 	 */
50030588217SMike Christensen 	if (len > strp->vlds_strlen) {
50130588217SMike Christensen 		len = strp->vlds_strlen;
50230588217SMike Christensen 		if (len > 0) {
50330588217SMike Christensen 			tstr = DS_MALLOC(len);
50430588217SMike Christensen 			(void) memcpy(tstr, str, len - 1);
50530588217SMike Christensen 			tstr[len - 1] = '\0';
50630588217SMike Christensen 			str = tstr;
50730588217SMike Christensen 		}
50830588217SMike Christensen 	}
50930588217SMike Christensen 	rv = ddi_copyout(str, ARGTOPTR(strp->vlds_strp), len, mode);
51030588217SMike Christensen 	if (tstr) {
51130588217SMike Christensen 		DS_FREE(tstr, len);
51230588217SMike Christensen 	}
51330588217SMike Christensen 	if (rv) {
51430588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: copyout (%p) failed", __func__,
51530588217SMike Christensen 		    ARGTOPTR(strp->vlds_strp));
51630588217SMike Christensen 		return (EFAULT);
51730588217SMike Christensen 	}
51830588217SMike Christensen 	return (0);
51930588217SMike Christensen }
52030588217SMike Christensen 
52130588217SMike Christensen static int
vlds_get_ucap(vlds_cap_t * capp,ds_capability_t * ucap,int mode)52230588217SMike Christensen vlds_get_ucap(vlds_cap_t *capp, ds_capability_t *ucap, int mode)
52330588217SMike Christensen {
52430588217SMike Christensen 	char *servp;
52530588217SMike Christensen 	vlds_ver_t *dsvp;
52630588217SMike Christensen 	vlds_cap_t vlds_cap;
52730588217SMike Christensen 	uint_t n;
52830588217SMike Christensen 	uint_t nver;
52930588217SMike Christensen 	int i;
53030588217SMike Christensen 	int rv;
53130588217SMike Christensen 
53230588217SMike Christensen 	if (ddi_copyin(capp, &vlds_cap, sizeof (vlds_cap), mode) != 0) {
53330588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: cap copyin failed (%p)", __func__,
53430588217SMike Christensen 		    (void *)capp);
53530588217SMike Christensen 		return (EFAULT);
53630588217SMike Christensen 	}
53730588217SMike Christensen 
53830588217SMike Christensen 	nver = ARGTOUINT(vlds_cap.vlds_nver);
53930588217SMike Christensen 
54030588217SMike Christensen 	if (nver > VLDS_MAX_VERS) {
54130588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: vlds_nver (%d) invalid", __func__,
54230588217SMike Christensen 		    nver);
54330588217SMike Christensen 		return (EINVAL);
54430588217SMike Christensen 	}
54530588217SMike Christensen 
54630588217SMike Christensen 	if ((rv = vlds_get_string(&vlds_cap.vlds_service, &servp, mode)) != 0) {
54730588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: vlds_get_string vlds_service failed "
54830588217SMike Christensen 		    "(%d)", __func__, rv);
54930588217SMike Christensen 		return (rv);
55030588217SMike Christensen 	} else if (servp == NULL) {
55130588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: vlds_get_string vlds_service is NULL",
55230588217SMike Christensen 		    __func__);
55330588217SMike Christensen 		return (EINVAL);
55430588217SMike Christensen 	}
55530588217SMike Christensen 
55630588217SMike Christensen 	n = nver * sizeof (vlds_ver_t);
55730588217SMike Christensen 	dsvp = DS_MALLOC(n);
55830588217SMike Christensen 
55930588217SMike Christensen 	if (ddi_copyin(ARGTOPTR(vlds_cap.vlds_versp), dsvp, n, mode) != 0) {
56030588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: copyin of vers (%p, %d) failed",
56130588217SMike Christensen 		    __func__, ARGTOPTR(vlds_cap.vlds_versp), n);
56230588217SMike Christensen 		DS_FREE(servp, strlen(servp) + 1);
56330588217SMike Christensen 		DS_FREE(dsvp, n);
56430588217SMike Christensen 		return (EFAULT);
56530588217SMike Christensen 	}
56630588217SMike Christensen 
56730588217SMike Christensen 	ucap->svc_id = servp;
56830588217SMike Christensen 	ucap->vers = DS_MALLOC(nver * sizeof (ds_ver_t));
56930588217SMike Christensen 	for (i = 0; i < nver; i++) {
57030588217SMike Christensen 		ucap->vers[i].major = dsvp[i].vlds_major;
57130588217SMike Christensen 		ucap->vers[i].minor = dsvp[i].vlds_minor;
57230588217SMike Christensen 	}
57330588217SMike Christensen 	ucap->nvers = nver;
57430588217SMike Christensen 	DS_FREE(dsvp, n);
57530588217SMike Christensen 	return (0);
57630588217SMike Christensen }
57730588217SMike Christensen 
57830588217SMike Christensen static void
vlds_free_ucap(ds_capability_t * ucap)57930588217SMike Christensen vlds_free_ucap(ds_capability_t *ucap)
58030588217SMike Christensen {
58130588217SMike Christensen 	kmem_free(ucap->svc_id, strlen(ucap->svc_id) + 1);
58230588217SMike Christensen 	kmem_free(ucap->vers, ucap->nvers * sizeof (ds_ver_t));
58330588217SMike Christensen }
58430588217SMike Christensen 
58530588217SMike Christensen /*ARGSUSED*/
58630588217SMike Christensen static int
vlds_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)58730588217SMike Christensen vlds_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
58830588217SMike Christensen     int *rvalp)
58930588217SMike Christensen {
59030588217SMike Christensen 	vlds_state_t *sp;
59130588217SMike Christensen 	ds_svc_hdl_t hdl;
59230588217SMike Christensen 	ds_domain_hdl_t dhdl;
59330588217SMike Christensen 	char *servicep;
59430588217SMike Christensen 	int rv;
59530588217SMike Christensen 	int minor = (int)getminor(dev);
59630588217SMike Christensen 
59730588217SMike Christensen 	if ((sp = ddi_get_soft_state(vlds_statep, minor)) == NULL)
59830588217SMike Christensen 		return (ENXIO);
59930588217SMike Christensen 
60030588217SMike Christensen 	switch (cmd) {
60130588217SMike Christensen 
60230588217SMike Christensen 	case VLDS_SVC_REG:
60330588217SMike Christensen 	{
60430588217SMike Christensen 		vlds_svc_reg_arg_t vlds_arg;
60530588217SMike Christensen 		ds_capability_t ucap;
60630588217SMike Christensen 		uint64_t hdl_arg;
60730588217SMike Christensen 		uint_t flags;
60830588217SMike Christensen 
60930588217SMike Christensen 		if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
61030588217SMike Christensen 		    mode) != 0) {
61130588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: SVC REG arg copyin failed",
61230588217SMike Christensen 			    __func__);
61330588217SMike Christensen 			return (EFAULT);
61430588217SMike Christensen 		}
61530588217SMike Christensen 
61630588217SMike Christensen 		if ((rv = vlds_get_ucap(ARGTOPTR(vlds_arg.vlds_capp), &ucap,
61730588217SMike Christensen 		    mode)) != 0) {
61830588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: SVC REG get_ucap failed (%d)",
61930588217SMike Christensen 			    __func__, rv);
62030588217SMike Christensen 			return (rv);
62130588217SMike Christensen 		}
62230588217SMike Christensen 
62330588217SMike Christensen 		flags = vlds_flags_to_svc(vlds_arg.vlds_reg_flags);
62430588217SMike Christensen 		if ((rv = vlds_init_sysevent(sp, flags)) != 0) {
62530588217SMike Christensen 			vlds_free_ucap(&ucap);
62630588217SMike Christensen 			return (rv);
62730588217SMike Christensen 		}
62830588217SMike Christensen 
62930588217SMike Christensen 		rv = ds_ucap_init(&ucap, &ds_user_ops,
63030588217SMike Christensen 		    vlds_flags_to_svc(vlds_arg.vlds_reg_flags) | DSSF_ISUSER,
63130588217SMike Christensen 		    minor, &hdl);
63230588217SMike Christensen 
63330588217SMike Christensen 		vlds_free_ucap(&ucap);
63430588217SMike Christensen 
63530588217SMike Christensen 		if (rv) {
63630588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: SVC REG ds_ucap_init failed "
63730588217SMike Christensen 			    "(%d)", __func__, rv);
63830588217SMike Christensen 			return (rv);
63930588217SMike Christensen 		}
64030588217SMike Christensen 
64130588217SMike Christensen 		hdl_arg = hdl;
64230588217SMike Christensen 		if (ddi_copyout(&hdl_arg, ARGTOPTR(vlds_arg.vlds_hdlp),
64330588217SMike Christensen 		    sizeof (hdl_arg), mode) != 0) {
64430588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: SVC REG copyout failed",
64530588217SMike Christensen 			    __func__);
64630588217SMike Christensen 			return (EFAULT);
64730588217SMike Christensen 		}
64830588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: SVC REG succeeded: hdl: %lx",
64930588217SMike Christensen 		    __func__, hdl);
65030588217SMike Christensen 		break;
65130588217SMike Christensen 	}
65230588217SMike Christensen 
65330588217SMike Christensen 	case VLDS_UNREG_HDL:
65430588217SMike Christensen 	{
65530588217SMike Christensen 		vlds_unreg_hdl_arg_t vlds_arg;
65630588217SMike Christensen 
65730588217SMike Christensen 		if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
65830588217SMike Christensen 		    mode) != 0) {
65930588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL arg copyin failed",
66030588217SMike Christensen 			    __func__);
66130588217SMike Christensen 			return (EFAULT);
66230588217SMike Christensen 		}
66330588217SMike Christensen 
66430588217SMike Christensen 		hdl = vlds_arg.vlds_hdl;
66530588217SMike Christensen 
66630588217SMike Christensen 		if ((rv = ds_is_my_hdl(hdl, minor)) != 0) {
66730588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL ds_is_my_hdl "
66830588217SMike Christensen 			    " hdl: %lx inst: %d failed (%d)", __func__,
66930588217SMike Christensen 			    hdl, rv, minor);
67030588217SMike Christensen 			return (rv);
67130588217SMike Christensen 		}
67230588217SMike Christensen 
67330588217SMike Christensen 		if ((rv = ds_unreg_hdl(hdl)) != 0) {
67430588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL ds_cap_unreg "
67530588217SMike Christensen 			    " hdl: %lx failed (%d)", __func__, hdl, rv);
67630588217SMike Christensen 			return (rv);
67730588217SMike Christensen 		}
67830588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL hdl: %lx succeeded",
67930588217SMike Christensen 		    __func__, hdl);
68030588217SMike Christensen 		break;
68130588217SMike Christensen 	}
68230588217SMike Christensen 
68330588217SMike Christensen 	case VLDS_HDL_LOOKUP:
68430588217SMike Christensen 	{
68530588217SMike Christensen 		vlds_hdl_lookup_arg_t vlds_arg;
68630588217SMike Christensen 		ds_svc_hdl_t *hdlsp;
68730588217SMike Christensen 		uint_t is_client, maxhdls, nhdls;
68830588217SMike Christensen 		uint64_t nhdls_arg;
68930588217SMike Christensen 
69030588217SMike Christensen 		if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
69130588217SMike Christensen 		    mode) != 0) {
69230588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP arg copyin failed",
69330588217SMike Christensen 			    __func__);
69430588217SMike Christensen 			return (EFAULT);
69530588217SMike Christensen 		}
69630588217SMike Christensen 
69730588217SMike Christensen 		is_client = ARGTOUINT(vlds_arg.vlds_isclient);
69830588217SMike Christensen 		maxhdls = ARGTOUINT(vlds_arg.vlds_maxhdls);
69930588217SMike Christensen 		if (maxhdls == 0) {
70030588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP invalid maxhdls "
70130588217SMike Christensen 			    "%d", __func__, maxhdls);
70230588217SMike Christensen 			return (EINVAL);
70330588217SMike Christensen 		}
70430588217SMike Christensen 
70530588217SMike Christensen 		if ((rv = vlds_get_string(&vlds_arg.vlds_service, &servicep,
70630588217SMike Christensen 		    mode)) != 0) {
70730588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP vlds_get_string "
70830588217SMike Christensen 			    "(service) failed (%d)", __func__, rv);
70930588217SMike Christensen 			return (EFAULT);
71030588217SMike Christensen 		} else if (servicep == NULL) {
71130588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP vlds_get_string "
71230588217SMike Christensen 			    " service is NULL", __func__);
71330588217SMike Christensen 			return (EINVAL);
71430588217SMike Christensen 		}
71530588217SMike Christensen 
71630588217SMike Christensen 		if (ARGTOPTR(vlds_arg.vlds_hdlsp) == 0) {
71730588217SMike Christensen 			hdlsp = NULL;
71830588217SMike Christensen 		} else {
71930588217SMike Christensen 			hdlsp = DS_MALLOC(maxhdls * sizeof (*hdlsp));
72030588217SMike Christensen 		}
72130588217SMike Christensen 
72230588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP (%s, %d) entered",
72330588217SMike Christensen 		    __func__, servicep, is_client);
72430588217SMike Christensen 		rv = ds_hdl_lookup(servicep, is_client, hdlsp, maxhdls, &nhdls);
72530588217SMike Christensen 
72630588217SMike Christensen 		DS_FREE(servicep, strlen(servicep) + 1);
72730588217SMike Christensen 		if (rv) {
72830588217SMike Christensen 			if (hdlsp) {
72930588217SMike Christensen 				DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp));
73030588217SMike Christensen 			}
73130588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP failed: (%d)",
73230588217SMike Christensen 			    __func__, rv);
73330588217SMike Christensen 			return (rv);
73430588217SMike Christensen 		}
73530588217SMike Christensen 
73630588217SMike Christensen 		if (hdlsp != NULL && nhdls > 0 &&
73730588217SMike Christensen 		    ddi_copyout(hdlsp, ARGTOPTR(vlds_arg.vlds_hdlsp),
73830588217SMike Christensen 		    nhdls * sizeof (ds_svc_hdl_t), mode) != 0) {
73930588217SMike Christensen 			if (hdlsp) {
74030588217SMike Christensen 				DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp));
74130588217SMike Christensen 			}
74230588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP copyout of hdls "
74330588217SMike Christensen 			    " failed", __func__);
74430588217SMike Christensen 			return (EFAULT);
74530588217SMike Christensen 		}
74630588217SMike Christensen 		if (hdlsp) {
74730588217SMike Christensen 			DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp));
74830588217SMike Christensen 		}
74930588217SMike Christensen 
75030588217SMike Christensen 		nhdls_arg = nhdls;
75130588217SMike Christensen 		if (ddi_copyout(&nhdls_arg, ARGTOPTR(vlds_arg.vlds_nhdlsp),
75230588217SMike Christensen 		    sizeof (nhdls_arg), mode) != 0) {
75330588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP copyout of nhdls "
75430588217SMike Christensen 			    " failed", __func__);
75530588217SMike Christensen 			return (EFAULT);
75630588217SMike Christensen 		}
75730588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP succeeded: nhdls: %d",
75830588217SMike Christensen 		    __func__, nhdls);
75930588217SMike Christensen 		break;
76030588217SMike Christensen 	}
76130588217SMike Christensen 
76230588217SMike Christensen 	case VLDS_DMN_LOOKUP:
76330588217SMike Christensen 	{
76430588217SMike Christensen 		vlds_dmn_lookup_arg_t vlds_arg;
76530588217SMike Christensen 		uint64_t dhdl_arg;
76630588217SMike Christensen 
76730588217SMike Christensen 		if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
76830588217SMike Christensen 		    mode) != 0) {
76930588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP arg copyin failed",
77030588217SMike Christensen 			    __func__);
77130588217SMike Christensen 			return (EFAULT);
77230588217SMike Christensen 		}
77330588217SMike Christensen 
77430588217SMike Christensen 		hdl = vlds_arg.vlds_hdl;
77530588217SMike Christensen 
77630588217SMike Christensen 		if ((rv = ds_domain_lookup(hdl, &dhdl)) != 0) {
77730588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP lookup hdl: 0x%lx "
77830588217SMike Christensen 			    "failed (%d)", __func__, hdl, rv);
77930588217SMike Christensen 			return (rv);
78030588217SMike Christensen 		}
78130588217SMike Christensen 
78230588217SMike Christensen 		dhdl_arg = dhdl;
78330588217SMike Christensen 
78430588217SMike Christensen 		if (ddi_copyout(&dhdl_arg, ARGTOPTR(vlds_arg.vlds_dhdlp),
78530588217SMike Christensen 		    sizeof (dhdl_arg), mode) != 0) {
78630588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP copyout "
78730588217SMike Christensen 			    "failed (%d)", __func__, rv);
78830588217SMike Christensen 			return (rv);
78930588217SMike Christensen 		}
79030588217SMike Christensen 
79130588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP hdl: 0x%lx, dhdl: 0x%lx "
79230588217SMike Christensen 		    "succeeded", __func__, hdl, dhdl);
79330588217SMike Christensen 		break;
79430588217SMike Christensen 	}
79530588217SMike Christensen 
79630588217SMike Christensen 	case VLDS_SEND_MSG:
79730588217SMike Christensen 	{
79830588217SMike Christensen 		vlds_send_msg_arg_t vlds_arg;
79930588217SMike Christensen 		size_t buflen;
80030588217SMike Christensen 		char *bufp;
80130588217SMike Christensen 
80230588217SMike Christensen 		if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
80330588217SMike Christensen 		    mode) != 0) {
80430588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG arg copyin failed",
80530588217SMike Christensen 			    __func__);
80630588217SMike Christensen 			return (EFAULT);
80730588217SMike Christensen 		}
80830588217SMike Christensen 
80930588217SMike Christensen 		hdl = vlds_arg.vlds_hdl;
81030588217SMike Christensen 		if ((rv = ds_is_my_hdl(hdl, minor)) != 0) {
81130588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG ds_is_my_hdl "
81230588217SMike Christensen 			    " hdl: %lx inst: %d failed (%d)", __func__,
81330588217SMike Christensen 			    hdl, rv, minor);
81430588217SMike Christensen 			return (rv);
81530588217SMike Christensen 		}
81630588217SMike Christensen 
81730588217SMike Christensen 		buflen = ARGTOUINT(vlds_arg.vlds_buflen);
81830588217SMike Christensen 		bufp = DS_MALLOC(buflen);
81930588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG (hdl: %lx, bufp: %p, "
82030588217SMike Christensen 		    "buflen: %ld", __func__, hdl, ARGTOPTR(vlds_arg.vlds_bufp),
82130588217SMike Christensen 		    buflen);
82230588217SMike Christensen 
82330588217SMike Christensen 		if (ddi_copyin(ARGTOPTR(vlds_arg.vlds_bufp), bufp, buflen,
82430588217SMike Christensen 		    mode) != 0) {
82530588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG buf (%p, %ld) "
82630588217SMike Christensen 			    "copyin failed", __func__,
82730588217SMike Christensen 			    ARGTOPTR(vlds_arg.vlds_bufp), buflen);
82830588217SMike Christensen 			DS_FREE(bufp, buflen);
82930588217SMike Christensen 			return (EFAULT);
83030588217SMike Christensen 		}
83130588217SMike Christensen 
83230588217SMike Christensen 		if ((rv = ds_cap_send(hdl, bufp, buflen)) != 0) {
83330588217SMike Christensen 			DS_FREE(bufp, buflen);
83430588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG ds_cap_send failed "
83530588217SMike Christensen 			    "(%d)", __func__, rv);
83630588217SMike Christensen 			return (rv);
83730588217SMike Christensen 		}
83830588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG hdl: %lx, bufp: %p, "
83930588217SMike Christensen 		    "buflen: %ld succeeded", __func__, hdl, (void *)bufp,
84030588217SMike Christensen 		    buflen);
84130588217SMike Christensen 		DS_DUMP_MSG(DS_DBG_FLAG_VLDS, bufp, buflen);
84230588217SMike Christensen 		DS_FREE(bufp, buflen);
84330588217SMike Christensen 		break;
84430588217SMike Christensen 	}
84530588217SMike Christensen 
84630588217SMike Christensen 	case VLDS_RECV_MSG:
84730588217SMike Christensen 	{
84830588217SMike Christensen 		vlds_recv_msg_arg_t vlds_arg;
84930588217SMike Christensen 		size_t buflen, msglen;
85030588217SMike Christensen 		uint64_t msglen_arg;
85130588217SMike Christensen 
85230588217SMike Christensen 		if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
85330588217SMike Christensen 		    mode) != 0) {
85430588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG arg copyin failed",
85530588217SMike Christensen 			    __func__);
85630588217SMike Christensen 			return (EFAULT);
85730588217SMike Christensen 		}
85830588217SMike Christensen 
85930588217SMike Christensen 		hdl = vlds_arg.vlds_hdl;
86030588217SMike Christensen 		if ((rv = ds_is_my_hdl(hdl, minor)) != 0) {
86130588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG ds_is_my_hdl "
86230588217SMike Christensen 			    " hdl: %lx inst: %d failed (%d)", __func__,
86330588217SMike Christensen 			    hdl, rv, minor);
86430588217SMike Christensen 			return (rv);
86530588217SMike Christensen 		}
86630588217SMike Christensen 
86730588217SMike Christensen 		buflen = ARGTOUINT(vlds_arg.vlds_buflen);
86830588217SMike Christensen 
86930588217SMike Christensen 		if ((rv = vlds_recv_msg(hdl, ARGTOPTR(vlds_arg.vlds_bufp),
87030588217SMike Christensen 		    buflen, &msglen, mode)) != 0 && rv != EFBIG) {
87130588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG vlds_recv_msg "
87230588217SMike Christensen 			    " failed (%d)", __func__, rv);
87330588217SMike Christensen 			return (rv);
87430588217SMike Christensen 		}
87530588217SMike Christensen 
87630588217SMike Christensen 		msglen_arg = msglen;
87730588217SMike Christensen 		if (ddi_copyout(&msglen_arg, ARGTOPTR(vlds_arg.vlds_msglenp),
87830588217SMike Christensen 		    sizeof (msglen_arg), mode) != 0) {
87930588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG copyout of msglen "
88030588217SMike Christensen 			    "failed", __func__);
88130588217SMike Christensen 			return (EFAULT);
88230588217SMike Christensen 		}
88330588217SMike Christensen 
88430588217SMike Christensen 		if (rv == EFBIG) {
88530588217SMike Christensen 			return (EFBIG);
88630588217SMike Christensen 		}
88730588217SMike Christensen 
88830588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG hdl: %lx, "
88930588217SMike Christensen 		    "msglen: %ld succeeded", __func__, hdl, buflen);
89030588217SMike Christensen 		break;
89130588217SMike Christensen 	}
89230588217SMike Christensen 
89330588217SMike Christensen 	case VLDS_HDL_ISREADY:
89430588217SMike Christensen 	{
89530588217SMike Christensen 		vlds_hdl_isready_arg_t vlds_arg;
89630588217SMike Christensen 		ds_svc_hdl_t hdl;
89730588217SMike Christensen 		uint64_t is_ready_arg;
89830588217SMike Christensen 		uint_t is_ready;
89930588217SMike Christensen 
90030588217SMike Christensen 		if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
90130588217SMike Christensen 		    mode) != 0) {
90230588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY arg copyin "
90330588217SMike Christensen 			    "failed", __func__);
90430588217SMike Christensen 			return (EFAULT);
90530588217SMike Christensen 		}
90630588217SMike Christensen 
90730588217SMike Christensen 		hdl = vlds_arg.vlds_hdl;
90830588217SMike Christensen 		if ((rv = ds_hdl_isready(hdl, &is_ready)) != 0) {
90930588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY ds_hdl_isready "
91030588217SMike Christensen 			    "error (%d)", __func__, rv);
91130588217SMike Christensen 			return (rv);
91230588217SMike Christensen 		}
91330588217SMike Christensen 
91430588217SMike Christensen 		is_ready_arg = is_ready;
91530588217SMike Christensen 		if (ddi_copyout(&is_ready_arg, ARGTOPTR(vlds_arg.vlds_isreadyp),
91630588217SMike Christensen 		    sizeof (is_ready_arg), mode) != 0) {
91730588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY copyout of "
91830588217SMike Christensen 			    "vlds_isready failed", __func__);
91930588217SMike Christensen 			return (EFAULT);
92030588217SMike Christensen 		}
92130588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY succeeded hdl: %lx, "
92230588217SMike Christensen 		    "is_ready: %d", __func__, hdl, is_ready);
92330588217SMike Christensen 		break;
92430588217SMike Christensen 	}
92530588217SMike Christensen 
92630588217SMike Christensen 	case VLDS_DOM_NAM2HDL:
92730588217SMike Christensen 	{
92830588217SMike Christensen 		vlds_dom_nam2hdl_arg_t vlds_arg;
92930588217SMike Christensen 		char *domain_name;
93030588217SMike Christensen 		uint64_t dhdl_arg;
93130588217SMike Christensen 		ds_domain_hdl_t dhdl;
93230588217SMike Christensen 
93330588217SMike Christensen 		if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
93430588217SMike Christensen 		    mode) != 0) {
93530588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL arg copyin "
93630588217SMike Christensen 			    "failed", __func__);
93730588217SMike Christensen 			return (EFAULT);
93830588217SMike Christensen 		}
93930588217SMike Christensen 
94030588217SMike Christensen 		if ((rv = vlds_get_string(&vlds_arg.vlds_domain_name,
94130588217SMike Christensen 		    &domain_name, mode)) != 0) {
94230588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL vlds_get_string "
94330588217SMike Christensen 			    "domain_name failed (%d)", __func__, rv);
94430588217SMike Christensen 			return (EFAULT);
94530588217SMike Christensen 		} else if (servicep == NULL) {
94630588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL vlds_get_string "
94730588217SMike Christensen 			    " domain_name is NULL", __func__);
94830588217SMike Christensen 			return (EINVAL);
94930588217SMike Christensen 		}
95030588217SMike Christensen 
95130588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL (%s) entered", __func__,
95230588217SMike Christensen 		    domain_name);
95330588217SMike Christensen 
95430588217SMike Christensen 		if ((rv = ds_dom_name_to_hdl(domain_name, &dhdl)) != 0) {
95530588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL name: '%s' "
95630588217SMike Christensen 			    "failed: (%d)", __func__, domain_name, rv);
95730588217SMike Christensen 			DS_FREE(domain_name, strlen(domain_name) + 1);
95830588217SMike Christensen 			return (rv);
95930588217SMike Christensen 		}
96030588217SMike Christensen 
96130588217SMike Christensen 		dhdl_arg = dhdl;
96230588217SMike Christensen 		if (ddi_copyout(&dhdl_arg, ARGTOPTR(vlds_arg.vlds_dhdlp),
96330588217SMike Christensen 		    sizeof (dhdl_arg), mode) != 0) {
96430588217SMike Christensen 			DS_FREE(domain_name, strlen(domain_name) + 1);
96530588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL copyout of dhdl "
96630588217SMike Christensen 			    " failed", __func__);
96730588217SMike Christensen 			return (EFAULT);
96830588217SMike Christensen 		}
96930588217SMike Christensen 
97030588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL succeeded: name: '%s', "
97130588217SMike Christensen 		    "dhdl: 0x%lx", __func__, domain_name, dhdl);
97230588217SMike Christensen 		DS_FREE(domain_name, strlen(domain_name) + 1);
97330588217SMike Christensen 		break;
97430588217SMike Christensen 	}
97530588217SMike Christensen 
97630588217SMike Christensen 	case VLDS_DOM_HDL2NAM:
97730588217SMike Christensen 	{
97830588217SMike Christensen 		vlds_dom_hdl2nam_arg_t vlds_arg;
97930588217SMike Christensen 		ds_domain_hdl_t dhdl;
98030588217SMike Christensen 		char *domain_name;
98130588217SMike Christensen 
98230588217SMike Christensen 		if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
98330588217SMike Christensen 		    mode) != 0) {
98430588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM arg copyin "
98530588217SMike Christensen 			    "failed", __func__);
98630588217SMike Christensen 			return (EFAULT);
98730588217SMike Christensen 		}
98830588217SMike Christensen 
98930588217SMike Christensen 		dhdl = vlds_arg.vlds_dhdl;
990a600f50dSMike Christensen 		if ((rv = ds_dom_hdl_to_name(dhdl, &domain_name)) != 0) {
99130588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM lookup dhdl: %lx "
99230588217SMike Christensen 			    "failed (%d)", __func__, dhdl, rv);
99330588217SMike Christensen 			return (rv);
99430588217SMike Christensen 		}
99530588217SMike Christensen 
99630588217SMike Christensen 		if ((rv = vlds_put_string(domain_name,
99730588217SMike Christensen 		    &vlds_arg.vlds_domain_name, mode)) != 0) {
99830588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM vlds_put_string "
99930588217SMike Christensen 			    "'%s' failed (%d)", __func__, domain_name, rv);
100030588217SMike Christensen 			return (rv);
100130588217SMike Christensen 		}
100230588217SMike Christensen 
100330588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM dhdl: 0x%lx name: '%s'",
100430588217SMike Christensen 		    __func__, dhdl, domain_name);
100530588217SMike Christensen 		break;
100630588217SMike Christensen 	}
100730588217SMike Christensen 
100830588217SMike Christensen 	default:
100930588217SMike Christensen 		return (EINVAL);
101030588217SMike Christensen 	}
101130588217SMike Christensen 	return (0);
101230588217SMike Christensen }
101330588217SMike Christensen 
101430588217SMike Christensen static uint_t
vlds_flags_to_svc(uint64_t flags)101530588217SMike Christensen vlds_flags_to_svc(uint64_t flags)
101630588217SMike Christensen {
101730588217SMike Christensen 	uint_t sflags = 0;
101830588217SMike Christensen 
101930588217SMike Christensen 	if (flags & VLDS_REG_CLIENT)
102030588217SMike Christensen 		sflags |= DSSF_ISCLIENT;
102130588217SMike Christensen 	if (flags & VLDS_REGCB_VALID)
102230588217SMike Christensen 		sflags |= DSSF_REGCB_VALID;
102330588217SMike Christensen 	if (flags & VLDS_UNREGCB_VALID)
102430588217SMike Christensen 		sflags |= DSSF_UNREGCB_VALID;
102530588217SMike Christensen 	if (flags & VLDS_DATACB_VALID)
102630588217SMike Christensen 		sflags |= DSSF_DATACB_VALID;
102730588217SMike Christensen 	return (sflags);
102830588217SMike Christensen }
102930588217SMike Christensen 
103030588217SMike Christensen /*
103130588217SMike Christensen  * MD registration code.
103230588217SMike Christensen  * Placed in vlds rather than ds module due to cirular dependency of
103330588217SMike Christensen  * platsvc module which contains the mdeg code.
103430588217SMike Christensen  */
1035a600f50dSMike Christensen mdeg_handle_t	vlds_mdeg_hdl;
103630588217SMike Christensen 
103730588217SMike Christensen /*
1038a600f50dSMike Christensen  * Look for "virtual-device-service" node among the
1039a600f50dSMike Christensen  * "virtual-device" nodes.
104030588217SMike Christensen  */
1041a600f50dSMike Christensen static mdeg_prop_spec_t vlds_prop_template[] = {
1042a600f50dSMike Christensen 	{ MDET_PROP_STR,	"name",	VLDS_MD_VIRT_ROOT_NAME },
104330588217SMike Christensen 	{ MDET_LIST_END,	NULL,	NULL    }
104430588217SMike Christensen };
104530588217SMike Christensen 
1046a600f50dSMike Christensen static mdeg_node_spec_t vlds_node_template =
1047a600f50dSMike Christensen 	{ VLDS_MD_VIRT_DEV_NAME,	vlds_prop_template };
104830588217SMike Christensen 
104930588217SMike Christensen /*
105030588217SMike Christensen  * Matching criteria passed to the MDEG to register interest
105130588217SMike Christensen  * in changes to domain services port nodes identified by their
105230588217SMike Christensen  * 'id' property.
105330588217SMike Christensen  */
1054a600f50dSMike Christensen static md_prop_match_t vlds_port_prop_match[] = {
105530588217SMike Christensen 	{ MDET_PROP_VAL,    "id"   },
105630588217SMike Christensen 	{ MDET_LIST_END,    NULL    }
105730588217SMike Christensen };
105830588217SMike Christensen 
1059a600f50dSMike Christensen static mdeg_node_match_t vlds_port_match = { VLDS_MD_VIRT_PORT_NAME,
1060a600f50dSMike Christensen 					vlds_port_prop_match };
106130588217SMike Christensen 
106230588217SMike Christensen /* mdeg callback */
106330588217SMike Christensen static int
vlds_mdeg_cb(void * cb_argp,mdeg_result_t * resp)1064a600f50dSMike Christensen vlds_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
106530588217SMike Christensen {
106630588217SMike Christensen 	_NOTE(ARGUNUSED(cb_argp))
106730588217SMike Christensen 	int		idx;
106830588217SMike Christensen 	uint64_t	portno;
106930588217SMike Christensen 	int		rv;
107030588217SMike Christensen 	md_t		*mdp;
107130588217SMike Christensen 	mde_cookie_t	node;
107230588217SMike Christensen 
107330588217SMike Christensen 	if (resp == NULL) {
1074a600f50dSMike Christensen 		DS_DBG_VLDS(CE_NOTE, "vlds_mdeg_cb: no result returned");
107530588217SMike Christensen 		return (MDEG_FAILURE);
107630588217SMike Christensen 	}
107730588217SMike Christensen 
107830588217SMike Christensen 	DS_DBG_VLDS(CE_NOTE, "%s: added=%d, removed=%d, matched=%d", __func__,
107930588217SMike Christensen 	    resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem);
108030588217SMike Christensen 
108130588217SMike Christensen 	/* process added ports */
108230588217SMike Christensen 	for (idx = 0; idx < resp->added.nelem; idx++) {
108330588217SMike Christensen 		mdp = resp->added.mdp;
108430588217SMike Christensen 		node = resp->added.mdep[idx];
108530588217SMike Christensen 
108630588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: processing added node 0x%lx",
108730588217SMike Christensen 		    __func__, node);
108830588217SMike Christensen 
108930588217SMike Christensen 		/* attempt to add a port */
1090a600f50dSMike Christensen 		if ((rv = vlds_add_mdeg_port(mdp, node)) != MDEG_SUCCESS) {
109130588217SMike Christensen 			if (vlds_ports_inited) {
109230588217SMike Christensen 				cmn_err(CE_NOTE, "%s: unable to add port, "
109330588217SMike Christensen 				    "err = %d", __func__, rv);
109430588217SMike Christensen 			}
109530588217SMike Christensen 		}
109630588217SMike Christensen 	}
109730588217SMike Christensen 
109830588217SMike Christensen 	/* process removed ports */
109930588217SMike Christensen 	for (idx = 0; idx < resp->removed.nelem; idx++) {
110030588217SMike Christensen 		mdp = resp->removed.mdp;
110130588217SMike Christensen 		node = resp->removed.mdep[idx];
110230588217SMike Christensen 
110330588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: processing removed node 0x%lx",
110430588217SMike Christensen 		    __func__, node);
110530588217SMike Christensen 
110630588217SMike Christensen 		/* read in the port's id property */
110730588217SMike Christensen 		if (md_get_prop_val(mdp, node, "id", &portno)) {
110830588217SMike Christensen 			cmn_err(CE_NOTE, "%s: node 0x%lx of removed list "
110930588217SMike Christensen 			    "has no 'id' property", __func__, node);
111030588217SMike Christensen 			continue;
111130588217SMike Christensen 		}
111230588217SMike Christensen 
111330588217SMike Christensen 		/* attempt to remove a port */
111430588217SMike Christensen 		if ((rv = ds_remove_port(portno, 0)) != 0) {
111530588217SMike Christensen 			cmn_err(CE_NOTE, "%s: unable to remove port %lu, "
111630588217SMike Christensen 			    " err %d", __func__, portno, rv);
111730588217SMike Christensen 		}
111830588217SMike Christensen 	}
111930588217SMike Christensen 
112030588217SMike Christensen 	vlds_ports_inited = 1;
112130588217SMike Christensen 
112230588217SMike Christensen 	return (MDEG_SUCCESS);
112330588217SMike Christensen }
112430588217SMike Christensen 
112530588217SMike Christensen /* register callback to mdeg */
112630588217SMike Christensen static int
vlds_mdeg_register(void)1127a600f50dSMike Christensen vlds_mdeg_register(void)
112830588217SMike Christensen {
112930588217SMike Christensen 	int		rv;
113030588217SMike Christensen 
1131a600f50dSMike Christensen 	DS_DBG_VLDS(CE_NOTE, "vlds_mdeg_register: entered");
113230588217SMike Christensen 
113330588217SMike Christensen 	/* perform the registration */
1134a600f50dSMike Christensen 	rv = mdeg_register(&vlds_node_template, &vlds_port_match, vlds_mdeg_cb,
1135a600f50dSMike Christensen 	    NULL, &vlds_mdeg_hdl);
113630588217SMike Christensen 
113730588217SMike Christensen 	if (rv != MDEG_SUCCESS) {
1138a600f50dSMike Christensen 		cmn_err(CE_NOTE, "vlds_mdeg_register: mdeg_register "
113930588217SMike Christensen 		    "failed, err = %d", rv);
114030588217SMike Christensen 		return (DDI_FAILURE);
114130588217SMike Christensen 	}
114230588217SMike Christensen 
114330588217SMike Christensen 	return (DDI_SUCCESS);
114430588217SMike Christensen }
114530588217SMike Christensen 
114630588217SMike Christensen /* unregister callback from mdeg */
114730588217SMike Christensen static int
vlds_mdeg_unregister(void)1148a600f50dSMike Christensen vlds_mdeg_unregister(void)
114930588217SMike Christensen {
1150a600f50dSMike Christensen 	DS_DBG_VLDS(CE_NOTE, "vlds_mdeg_unregister: hdl=0x%lx", vlds_mdeg_hdl);
115130588217SMike Christensen 
1152a600f50dSMike Christensen 	return (mdeg_unregister(vlds_mdeg_hdl));
115330588217SMike Christensen }
115430588217SMike Christensen 
115530588217SMike Christensen static int
vlds_get_port_channel(md_t * mdp,mde_cookie_t node,uint64_t * ldc_id)1156a600f50dSMike Christensen vlds_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id)
115730588217SMike Christensen {
115830588217SMike Christensen 	int num_nodes, nchan;
115930588217SMike Christensen 	size_t listsz;
116030588217SMike Christensen 	mde_cookie_t *listp;
116130588217SMike Christensen 
116230588217SMike Christensen 	/*
116330588217SMike Christensen 	 * Find the channel-endpoint node(s) (which should be under this
116430588217SMike Christensen 	 * port node) which contain the channel id(s).
116530588217SMike Christensen 	 */
116630588217SMike Christensen 	if ((num_nodes = md_node_count(mdp)) <= 0) {
116730588217SMike Christensen 		cmn_err(CE_NOTE, "%s: invalid number of channel-endpoint nodes "
116830588217SMike Christensen 		    "found (%d)", __func__, num_nodes);
116930588217SMike Christensen 		return (-1);
117030588217SMike Christensen 	}
117130588217SMike Christensen 
117230588217SMike Christensen 	/* allocate space for node list */
117330588217SMike Christensen 	listsz = num_nodes * sizeof (mde_cookie_t);
117430588217SMike Christensen 	listp = kmem_alloc(listsz, KM_SLEEP);
117530588217SMike Christensen 
117630588217SMike Christensen 	nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"),
117730588217SMike Christensen 	    md_find_name(mdp, "fwd"), listp);
117830588217SMike Christensen 
117930588217SMike Christensen 	if (nchan <= 0) {
118030588217SMike Christensen 		cmn_err(CE_NOTE, "%s: no channel-endpoint nodes found",
118130588217SMike Christensen 		    __func__);
118230588217SMike Christensen 		kmem_free(listp, listsz);
118330588217SMike Christensen 		return (-1);
118430588217SMike Christensen 	}
118530588217SMike Christensen 
118630588217SMike Christensen 	DS_DBG_VLDS(CE_NOTE, "%s: %d channel-endpoint nodes found", __func__,
118730588217SMike Christensen 	    nchan);
118830588217SMike Christensen 
118930588217SMike Christensen 	/* use property from first node found */
119030588217SMike Christensen 	if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) {
119130588217SMike Christensen 		cmn_err(CE_NOTE, "%s: channel-endpoint has no 'id' property",
119230588217SMike Christensen 		    __func__);
119330588217SMike Christensen 		kmem_free(listp, listsz);
119430588217SMike Christensen 		return (-1);
119530588217SMike Christensen 	}
119630588217SMike Christensen 
119730588217SMike Christensen 	kmem_free(listp, listsz);
119830588217SMike Christensen 
119930588217SMike Christensen 	return (0);
120030588217SMike Christensen }
120130588217SMike Christensen 
120230588217SMike Christensen /* add a DS services port */
120330588217SMike Christensen static int
vlds_add_mdeg_port(md_t * mdp,mde_cookie_t node)1204a600f50dSMike Christensen vlds_add_mdeg_port(md_t *mdp, mde_cookie_t node)
120530588217SMike Christensen {
120630588217SMike Christensen 	uint64_t	portno;
120730588217SMike Christensen 	uint64_t	ldc_id;
120830588217SMike Christensen 	int		rv;
120930588217SMike Christensen 	uint64_t	dhdl;
121030588217SMike Christensen 	char		*dom_name;
121130588217SMike Christensen 
121230588217SMike Christensen 	/* read in the port's id property */
121330588217SMike Christensen 	if (md_get_prop_val(mdp, node, "id", &portno)) {
121430588217SMike Christensen 		cmn_err(CE_NOTE, "%s: node 0x%lx of added list has no "
121530588217SMike Christensen 		    "'id' property", __func__, node);
121630588217SMike Christensen 		return (MDEG_FAILURE);
121730588217SMike Christensen 	}
121830588217SMike Christensen 
121930588217SMike Christensen 	if (portno >= DS_MAX_PORTS) {
122030588217SMike Christensen 		cmn_err(CE_NOTE, "%s: found port number (%lu) "
122130588217SMike Christensen 		    "larger than maximum supported number of ports", __func__,
122230588217SMike Christensen 		    portno);
122330588217SMike Christensen 		return (MDEG_FAILURE);
122430588217SMike Christensen 	}
122530588217SMike Christensen 
122630588217SMike Christensen 	/* get all channels for this device (currently only one) */
1227a600f50dSMike Christensen 	if (vlds_get_port_channel(mdp, node, &ldc_id) == -1) {
122830588217SMike Christensen 		return (MDEG_FAILURE);
122930588217SMike Christensen 	}
123030588217SMike Christensen 
1231a600f50dSMike Christensen 	if (md_get_prop_val(mdp, node, VLDS_MD_REM_DOMAIN_HDL, &dhdl) != 0) {
1232a600f50dSMike Christensen 		cmn_err(CE_NOTE, "!ds%lx: %s no %s property", portno, __func__,
1233a600f50dSMike Christensen 		    VLDS_MD_REM_DOMAIN_HDL);
123430588217SMike Christensen 		dhdl = DS_DHDL_INVALID;
123530588217SMike Christensen 	}
123630588217SMike Christensen 
1237a600f50dSMike Christensen 	if (md_get_prop_str(mdp, node, VLDS_MD_REM_DOMAIN_NAME, &dom_name)
1238a600f50dSMike Christensen 	    != 0) {
1239a600f50dSMike Christensen 		cmn_err(CE_NOTE, "!ds%lx: %s no %s property", portno, __func__,
1240a600f50dSMike Christensen 		    VLDS_MD_REM_DOMAIN_NAME);
124130588217SMike Christensen 		dom_name = NULL;
124230588217SMike Christensen 	}
124330588217SMike Christensen 
124430588217SMike Christensen 	rv = ds_add_port(portno, ldc_id, dhdl, dom_name, vlds_ports_inited);
124530588217SMike Christensen 
124630588217SMike Christensen 	if (rv != 0) {
124730588217SMike Christensen 		if (vlds_ports_inited) {
124830588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "ds%lx: %s LDC chan: %lx "
124930588217SMike Christensen 			    "failed err = %d", portno, __func__, ldc_id, rv);
125030588217SMike Christensen 		}
125130588217SMike Christensen 		return (MDEG_FAILURE);
125230588217SMike Christensen 	}
125330588217SMike Christensen 
125430588217SMike Christensen 	DS_DBG_VLDS(CE_NOTE, "ds%lx: %s LDC chan: %lx inited", portno,
125530588217SMike Christensen 	    __func__, ldc_id);
125630588217SMike Christensen 
125730588217SMike Christensen 	return (MDEG_SUCCESS);
125830588217SMike Christensen }
125930588217SMike Christensen 
126030588217SMike Christensen static void
vlds_mdeg_init(void)1261a600f50dSMike Christensen vlds_mdeg_init(void)
1262a600f50dSMike Christensen {
1263a600f50dSMike Christensen 	md_t		*mdp;
1264a600f50dSMike Christensen 	int		num_nodes;
1265a600f50dSMike Christensen 	int		listsz;
1266a600f50dSMike Christensen 	mde_cookie_t	rootnode;
1267a600f50dSMike Christensen 	mde_cookie_t	vldsnode;
1268a600f50dSMike Christensen 	mde_cookie_t	*vlds_nodes = NULL;
1269a600f50dSMike Christensen 	int		nvlds;
1270a600f50dSMike Christensen 	int		i;
1271a600f50dSMike Christensen 	ds_domain_hdl_t	dhdl;
1272a600f50dSMike Christensen 	char		*dom_name;
1273a600f50dSMike Christensen 	char		*svc_name;
1274a600f50dSMike Christensen 
1275a600f50dSMike Christensen 	if ((mdp = md_get_handle()) == NULL) {
1276a600f50dSMike Christensen 		cmn_err(CE_NOTE, "Unable to initialize machine description");
1277a600f50dSMike Christensen 		return;
1278a600f50dSMike Christensen 	}
1279a600f50dSMike Christensen 
1280a600f50dSMike Christensen 	num_nodes = md_node_count(mdp);
1281a600f50dSMike Christensen 	ASSERT(num_nodes > 0);
1282a600f50dSMike Christensen 
1283a600f50dSMike Christensen 	listsz = num_nodes * sizeof (mde_cookie_t);
1284a600f50dSMike Christensen 
1285a600f50dSMike Christensen 	/* allocate temporary storage for MD scans */
1286a600f50dSMike Christensen 	vlds_nodes = kmem_zalloc(listsz, KM_SLEEP);
1287a600f50dSMike Christensen 
1288a600f50dSMike Christensen 	rootnode = md_root_node(mdp);
1289a600f50dSMike Christensen 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
1290a600f50dSMike Christensen 
1291a600f50dSMike Christensen 	/*
1292a600f50dSMike Christensen 	 * Search for Virtual Domain Service node.
1293a600f50dSMike Christensen 	 */
1294a600f50dSMike Christensen 	nvlds = md_scan_dag(mdp, rootnode, md_find_name(mdp,
1295a600f50dSMike Christensen 	    VLDS_MD_VIRT_DEV_NAME), md_find_name(mdp, "fwd"), vlds_nodes);
1296a600f50dSMike Christensen 
1297a600f50dSMike Christensen 	if (nvlds <= 0) {
1298a600f50dSMike Christensen 		DS_DBG_MD(CE_NOTE, "No '%s' nodes in MD",
1299a600f50dSMike Christensen 		    VLDS_MD_VIRT_DEV_NAME);
1300a600f50dSMike Christensen 		goto done;
1301a600f50dSMike Christensen 	}
1302a600f50dSMike Christensen 
1303a600f50dSMike Christensen 	for (i = 0; i < nvlds; i++) {
1304a600f50dSMike Christensen 		if (md_get_prop_str(mdp, vlds_nodes[i], "name", &svc_name)) {
1305a600f50dSMike Christensen 			DS_DBG_MD(CE_NOTE, "%s: missing 'name' property for"
1306a600f50dSMike Christensen 			    " IO node %d\n", __func__, i);
1307a600f50dSMike Christensen 			continue;
1308a600f50dSMike Christensen 		}
1309a600f50dSMike Christensen 
1310a600f50dSMike Christensen 		if (strcmp(svc_name, VLDS_MD_VIRT_ROOT_NAME) == 0) {
1311a600f50dSMike Christensen 			vldsnode = vlds_nodes[i];
1312a600f50dSMike Christensen 			break;
1313a600f50dSMike Christensen 		}
1314a600f50dSMike Christensen 	}
1315a600f50dSMike Christensen 
1316a600f50dSMike Christensen 	if (i >= nvlds) {
1317a600f50dSMike Christensen 		DS_DBG_MD(CE_NOTE, "No '%s' node in MD",
1318a600f50dSMike Christensen 		    VLDS_MD_VIRT_ROOT_NAME);
1319a600f50dSMike Christensen 		goto done;
1320a600f50dSMike Christensen 	}
1321a600f50dSMike Christensen 
1322a600f50dSMike Christensen 	if (md_get_prop_val(mdp, vldsnode, VLDS_MD_DOMAIN_HDL, &dhdl) != 0) {
1323a600f50dSMike Christensen 		DS_DBG_MD(CE_NOTE, "No '%s' property for '%s' node in MD",
1324a600f50dSMike Christensen 		    VLDS_MD_DOMAIN_HDL, VLDS_MD_VIRT_ROOT_NAME);
1325a600f50dSMike Christensen 		dhdl = DS_DHDL_INVALID;
1326a600f50dSMike Christensen 	}
1327a600f50dSMike Christensen 	if (md_get_prop_str(mdp, vldsnode, VLDS_MD_DOMAIN_NAME, &dom_name)
1328a600f50dSMike Christensen 	    != 0) {
1329a600f50dSMike Christensen 		DS_DBG_MD(CE_NOTE, "No '%s' property for '%s' node in MD",
1330a600f50dSMike Christensen 		    VLDS_MD_DOMAIN_NAME, VLDS_MD_VIRT_ROOT_NAME);
1331a600f50dSMike Christensen 		dom_name = NULL;
1332a600f50dSMike Christensen 	}
1333a600f50dSMike Christensen 	DS_DBG_MD(CE_NOTE, "My Domain Hdl: 0x%lx, Name: '%s'", dhdl,
1334a600f50dSMike Christensen 	    dom_name == NULL ? "NULL" : dom_name);
1335a600f50dSMike Christensen 	ds_set_my_dom_hdl_name(dhdl, dom_name);
1336a600f50dSMike Christensen 
1337a600f50dSMike Christensen done:
1338a600f50dSMike Christensen 	DS_FREE(vlds_nodes, listsz);
1339a600f50dSMike Christensen 
1340a600f50dSMike Christensen 	(void) md_fini_handle(mdp);
1341a600f50dSMike Christensen }
1342a600f50dSMike Christensen 
1343a600f50dSMike Christensen static void
vlds_user_reg_cb(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)134430588217SMike Christensen vlds_user_reg_cb(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
134530588217SMike Christensen {
134630588217SMike Christensen 	nvlist_t *nvl = NULL;
134730588217SMike Christensen 	ds_domain_hdl_t dhdl;
134830588217SMike Christensen 	char *servicep;
134930588217SMike Christensen 	uint32_t flags;
135030588217SMike Christensen 	int minor;
135130588217SMike Christensen 	vlds_state_t *sp;
135230588217SMike Christensen 	vlds_svc_info_t *dpsp;
135330588217SMike Christensen 
135430588217SMike Christensen 	ds_cbarg_get_flags(arg, &flags);
135530588217SMike Christensen 	ASSERT((flags & DSSF_ISUSER) != 0);
135630588217SMike Christensen 
135730588217SMike Christensen 	if ((flags & DSSF_DATACB_VALID) == 0) {
135830588217SMike Christensen 		/*
135930588217SMike Christensen 		 * must allocate and init the svc read queue.
136030588217SMike Christensen 		 */
136130588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: hdl: 0x%lx initing recvq", __func__,
136230588217SMike Christensen 		    hdl);
136330588217SMike Christensen 		dpsp = DS_MALLOC(sizeof (vlds_svc_info_t));
136430588217SMike Christensen 		vlds_recvq_init(dpsp);
136530588217SMike Christensen 		ds_cbarg_set_drv_per_svc_ptr(arg, dpsp);
136630588217SMike Christensen 	}
136730588217SMike Christensen 
136830588217SMike Christensen 	if ((flags & DSSF_REGCB_VALID) != 0) {
136930588217SMike Christensen 		ds_cbarg_get_drv_info(arg, &minor);
137030588217SMike Christensen 		sp = ddi_get_soft_state(vlds_statep, minor);
137130588217SMike Christensen 		ASSERT(sp != NULL);
137230588217SMike Christensen 		ASSERT(sp->evchan != NULL);
137330588217SMike Christensen 		ds_cbarg_get_domain(arg, &dhdl);
137430588217SMike Christensen 		ds_cbarg_get_service_id(arg, &servicep);
137530588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: regcb: hdl: 0x%lx, ver%d.%d, "
137630588217SMike Christensen 		    " dhdl: 0x%lx", __func__, hdl, ver->major,
137730588217SMike Christensen 		    ver->minor, dhdl);
137830588217SMike Christensen 		if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
137930588217SMike Christensen 		    nvlist_add_uint64(nvl, VLDS_HDL, hdl) ||
138030588217SMike Christensen 		    nvlist_add_uint16(nvl, VLDS_VER_MAJOR, ver->major) ||
138130588217SMike Christensen 		    nvlist_add_uint16(nvl, VLDS_VER_MINOR, ver->minor) ||
138230588217SMike Christensen 		    nvlist_add_uint64(nvl, VLDS_DOMAIN_HDL, dhdl) ||
138330588217SMike Christensen 		    nvlist_add_string(nvl, VLDS_SERVICE_ID, servicep) ||
138430588217SMike Christensen 		    nvlist_add_boolean_value(nvl, VLDS_ISCLIENT,
138530588217SMike Christensen 		    (flags & DSSF_ISCLIENT) != 0) ||
138630588217SMike Christensen 		    sysevent_evc_publish(sp->evchan, EC_VLDS,
138730588217SMike Christensen 		    ESC_VLDS_REGISTER, "sun.com", "kernel", nvl, EVCH_SLEEP)) {
138830588217SMike Christensen 			cmn_err(CE_WARN, "Failed to send REG Callback");
138930588217SMike Christensen 		} else {
139030588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: sysevent_evc_publish "
139130588217SMike Christensen 			    "succeeded", __func__);
139230588217SMike Christensen 		}
139330588217SMike Christensen 		nvlist_free(nvl);
139430588217SMike Christensen 	}
139530588217SMike Christensen }
139630588217SMike Christensen 
139730588217SMike Christensen static void
vlds_user_unreg_cb(ds_cb_arg_t arg)139830588217SMike Christensen vlds_user_unreg_cb(ds_cb_arg_t arg)
139930588217SMike Christensen {
140030588217SMike Christensen 	nvlist_t *nvl = NULL;
140130588217SMike Christensen 	int minor;
140230588217SMike Christensen 	ds_svc_hdl_t hdl;
140330588217SMike Christensen 	vlds_state_t *sp;
140430588217SMike Christensen 	void *dpsp;
140530588217SMike Christensen 	uint32_t flags;
140630588217SMike Christensen 
140730588217SMike Christensen 	ds_cbarg_get_flags(arg, &flags);
140830588217SMike Christensen 	ASSERT((flags & DSSF_ISUSER) != 0);
140930588217SMike Christensen 
141030588217SMike Christensen 	if ((flags & DSSF_DATACB_VALID) == 0) {
141130588217SMike Christensen 		ds_cbarg_get_drv_per_svc_ptr(arg, &dpsp);
141230588217SMike Christensen 		if (dpsp) {
141330588217SMike Christensen 			DS_DBG_VLDS(CE_NOTE, "%s: unregcb draining recvq",
141430588217SMike Christensen 			    __func__);
141530588217SMike Christensen 			vlds_recvq_drain(dpsp);
141630588217SMike Christensen 			vlds_recvq_destroy(dpsp);
141730588217SMike Christensen 			ds_cbarg_set_drv_per_svc_ptr(arg, NULL);
141830588217SMike Christensen 		}
141930588217SMike Christensen 	}
142030588217SMike Christensen 
142130588217SMike Christensen 	if ((flags & DSSF_UNREGCB_VALID) != 0) {
142230588217SMike Christensen 		ds_cbarg_get_hdl(arg, &hdl);
142330588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: unregcb hdl: 0x%lx", __func__,
142430588217SMike Christensen 		    hdl);
142530588217SMike Christensen 		ds_cbarg_get_drv_info(arg, &minor);
142630588217SMike Christensen 		sp = ddi_get_soft_state(vlds_statep, minor);
142730588217SMike Christensen 		ASSERT(sp != NULL);
142830588217SMike Christensen 		ASSERT(sp->evchan != NULL);
142930588217SMike Christensen 		if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
143030588217SMike Christensen 		    nvlist_add_uint64(nvl, VLDS_HDL, hdl) ||
143130588217SMike Christensen 		    sysevent_evc_publish(sp->evchan, EC_VLDS,
143230588217SMike Christensen 		    ESC_VLDS_UNREGISTER, "sun.com", "kernel", nvl,
143330588217SMike Christensen 		    EVCH_SLEEP)) {
143430588217SMike Christensen 			cmn_err(CE_WARN, "Failed to send UNREG Callback");
143530588217SMike Christensen 		}
143630588217SMike Christensen 		nvlist_free(nvl);
143730588217SMike Christensen 	}
143830588217SMike Christensen }
143930588217SMike Christensen 
144030588217SMike Christensen static void
vlds_user_data_cb(ds_cb_arg_t arg,void * buf,size_t buflen)144130588217SMike Christensen vlds_user_data_cb(ds_cb_arg_t arg, void *buf, size_t buflen)
144230588217SMike Christensen {
144330588217SMike Christensen 	nvlist_t *nvl = NULL;
144430588217SMike Christensen 	ds_svc_hdl_t hdl;
144530588217SMike Christensen 	int minor;
144630588217SMike Christensen 	void *dpsp;
144730588217SMike Christensen 	vlds_state_t *sp;
144830588217SMike Christensen 	uint32_t flags;
144930588217SMike Christensen 
145030588217SMike Christensen 	ds_cbarg_get_flags(arg, &flags);
145130588217SMike Christensen 	ASSERT((flags & DSSF_ISUSER) != 0);
145230588217SMike Christensen 
145330588217SMike Christensen 	if ((flags & DSSF_DATACB_VALID) == 0) {
145430588217SMike Christensen 		ds_cbarg_get_drv_per_svc_ptr(arg, &dpsp);
145530588217SMike Christensen 		ASSERT(dpsp != NULL);
145630588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: datacb: to recvq: buflen: %ld",
145730588217SMike Christensen 		    __func__, buflen);
145830588217SMike Christensen 		(void) vlds_recvq_put_data(dpsp, buf, buflen);
145930588217SMike Christensen 	} else {
146030588217SMike Christensen 		ds_cbarg_get_hdl(arg, &hdl);
146130588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: datacb: usercb: hdl: 0x%lx, "
146230588217SMike Christensen 		    " buflen: %ld", __func__, hdl, buflen);
146330588217SMike Christensen 		ds_cbarg_get_drv_info(arg, &minor);
146430588217SMike Christensen 		sp = ddi_get_soft_state(vlds_statep, minor);
146530588217SMike Christensen 		ASSERT(sp != NULL);
146630588217SMike Christensen 		ASSERT(sp->evchan != NULL);
146730588217SMike Christensen 		if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
146830588217SMike Christensen 		    nvlist_add_uint64(nvl, VLDS_HDL, hdl) ||
146930588217SMike Christensen 		    nvlist_add_byte_array(nvl, VLDS_DATA, buf, buflen) ||
147030588217SMike Christensen 		    sysevent_evc_publish(sp->evchan, EC_VLDS,
147130588217SMike Christensen 		    ESC_VLDS_DATA, "sun.com", "kernel", nvl, EVCH_SLEEP)) {
147230588217SMike Christensen 			cmn_err(CE_WARN, "Failed to send DATA Callback");
147330588217SMike Christensen 		}
147430588217SMike Christensen 	}
147530588217SMike Christensen 	nvlist_free(nvl);
147630588217SMike Christensen }
147730588217SMike Christensen 
147830588217SMike Christensen /*
147930588217SMike Christensen  * Initialize receive queue if request is from user land but
148030588217SMike Christensen  * data callback is null (implying user will be using ds_recv_msg).
148130588217SMike Christensen  */
148230588217SMike Christensen static void
vlds_recvq_init(vlds_svc_info_t * dpsp)148330588217SMike Christensen vlds_recvq_init(vlds_svc_info_t *dpsp)
148430588217SMike Christensen {
148530588217SMike Christensen 	dpsp->state = VLDS_RECV_OK;
148630588217SMike Christensen 	mutex_init(&dpsp->recv_lock, NULL, MUTEX_DRIVER, NULL);
148730588217SMike Christensen 	cv_init(&dpsp->recv_cv, NULL, CV_DRIVER, NULL);
148830588217SMike Christensen 	dpsp->recv_headp = NULL;
148930588217SMike Christensen 	dpsp->recv_tailp = NULL;
149030588217SMike Christensen 	dpsp->recv_size = 0;
1491ba9236fbSMike Christensen 	dpsp->recv_cnt = 0;
149230588217SMike Christensen }
149330588217SMike Christensen 
149430588217SMike Christensen static void
vlds_recvq_destroy(vlds_svc_info_t * dpsp)149530588217SMike Christensen vlds_recvq_destroy(vlds_svc_info_t *dpsp)
149630588217SMike Christensen {
149730588217SMike Christensen 	ASSERT(dpsp->state == VLDS_RECV_UNREG_PENDING);
149830588217SMike Christensen 	ASSERT(dpsp->recv_size == 0);
1499ba9236fbSMike Christensen 	ASSERT(dpsp->recv_cnt == 0);
150030588217SMike Christensen 	ASSERT(dpsp->recv_headp == NULL);
150130588217SMike Christensen 	ASSERT(dpsp->recv_tailp == NULL);
150230588217SMike Christensen 
150330588217SMike Christensen 	mutex_destroy(&dpsp->recv_lock);
150430588217SMike Christensen 	cv_destroy(&dpsp->recv_cv);
150530588217SMike Christensen 	DS_FREE(dpsp, sizeof (vlds_svc_info_t));
150630588217SMike Christensen }
150730588217SMike Christensen 
150830588217SMike Christensen static int
vlds_recvq_get_data(vlds_svc_info_t * dpsp,void * buf,size_t buflen,size_t * msglenp,int mode)150930588217SMike Christensen vlds_recvq_get_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen,
151030588217SMike Christensen     size_t *msglenp, int mode)
151130588217SMike Christensen {
151230588217SMike Christensen 	vlds_recv_hdr_t *rhp;
151330588217SMike Christensen 	int rv;
151430588217SMike Christensen 	size_t msglen;
151530588217SMike Christensen 
151630588217SMike Christensen 	mutex_enter(&dpsp->recv_lock);
151730588217SMike Christensen 	while (dpsp->recv_size == 0) {
1518ba9236fbSMike Christensen 		ASSERT(dpsp->recv_cnt == 0);
151930588217SMike Christensen 		if (dpsp->state == VLDS_RECV_UNREG_PENDING)
152030588217SMike Christensen 			break;
1521ba9236fbSMike Christensen 
1522ba9236fbSMike Christensen 		if (dpsp->state == VLDS_RECV_OVERFLOW) {
1523ba9236fbSMike Christensen 			DS_DBG_RCVQ(CE_NOTE, "%s: user data queue overflow",
1524ba9236fbSMike Christensen 			    __func__);
1525ba9236fbSMike Christensen 			dpsp->state = VLDS_RECV_OK;
1526ba9236fbSMike Christensen 			mutex_exit(&dpsp->recv_lock);
1527ba9236fbSMike Christensen 			return (ENOBUFS);
1528ba9236fbSMike Christensen 		}
152930588217SMike Christensen 		/*
153030588217SMike Christensen 		 * Passing in a buflen of 0 allows user to poll for msgs.
153130588217SMike Christensen 		 */
153230588217SMike Christensen 		if (buflen == 0) {
153330588217SMike Christensen 			mutex_exit(&dpsp->recv_lock);
153430588217SMike Christensen 			*msglenp = 0;
153530588217SMike Christensen 			return (EFBIG);
153630588217SMike Christensen 		}
153730588217SMike Christensen 		dpsp->recv_nreaders += 1;
153830588217SMike Christensen 		rv = cv_wait_sig(&dpsp->recv_cv, &dpsp->recv_lock);
153930588217SMike Christensen 		dpsp->recv_nreaders -= 1;
154030588217SMike Christensen 		if (rv == 0) {
154130588217SMike Christensen 			DS_DBG_RCVQ(CE_NOTE, "%s: signal EINTR", __func__);
154230588217SMike Christensen 			mutex_exit(&dpsp->recv_lock);
154330588217SMike Christensen 			return (EINTR);
154430588217SMike Christensen 		}
154530588217SMike Christensen 	}
154630588217SMike Christensen 	if (dpsp->state == VLDS_RECV_UNREG_PENDING) {
154730588217SMike Christensen 		DS_DBG_RCVQ(CE_NOTE, "%s: unreg pending", __func__);
154830588217SMike Christensen 		cv_broadcast(&dpsp->recv_cv);
154930588217SMike Christensen 		mutex_exit(&dpsp->recv_lock);
155030588217SMike Christensen 		return (EINVAL);
155130588217SMike Christensen 	}
155230588217SMike Christensen 	ASSERT(dpsp->recv_headp != NULL);
155330588217SMike Christensen 	rhp = dpsp->recv_headp;
155430588217SMike Christensen 
155530588217SMike Christensen 	/*
155630588217SMike Christensen 	 * Don't transfer truncated data, return EFBIG error if user-supplied
155730588217SMike Christensen 	 * buffer is too small.
155830588217SMike Christensen 	 */
155930588217SMike Christensen 	if (rhp->datasz > buflen) {
156030588217SMike Christensen 		*msglenp = rhp->datasz;
156130588217SMike Christensen 		mutex_exit(&dpsp->recv_lock);
156230588217SMike Christensen 		return (EFBIG);
156330588217SMike Christensen 	}
156430588217SMike Christensen 	if (rhp == dpsp->recv_tailp) {
156530588217SMike Christensen 		dpsp->recv_headp = NULL;
156630588217SMike Christensen 		dpsp->recv_tailp = NULL;
156730588217SMike Christensen 	} else {
156830588217SMike Christensen 		dpsp->recv_headp = rhp->next;
156930588217SMike Christensen 		ASSERT(dpsp->recv_headp != NULL);
157030588217SMike Christensen 	}
1571ba9236fbSMike Christensen 	ASSERT(dpsp->recv_cnt > 0);
157230588217SMike Christensen 	dpsp->recv_size -= rhp->datasz;
1573ba9236fbSMike Christensen 	dpsp->recv_cnt -= 1;
157430588217SMike Christensen 	mutex_exit(&dpsp->recv_lock);
157530588217SMike Christensen 
157630588217SMike Christensen 	msglen = rhp->datasz;
157730588217SMike Christensen 	rv = ddi_copyout(rhp->data, buf, msglen, mode);
157830588217SMike Christensen 
157930588217SMike Christensen 	if (rv == 0) {
158030588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: user data dequeued msglen: %ld",
158130588217SMike Christensen 		    __func__, rhp->datasz);
158230588217SMike Christensen 		DS_DUMP_MSG(DS_DBG_FLAG_VLDS, rhp->data, rhp->datasz);
158330588217SMike Christensen 	}
158430588217SMike Christensen 
158530588217SMike Christensen 	DS_FREE(rhp->data, rhp->datasz);
158630588217SMike Christensen 	DS_FREE(rhp, sizeof (vlds_recv_hdr_t));
158730588217SMike Christensen 
158830588217SMike Christensen 	if (rv != 0) {
158930588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: copyout failed", __func__);
159030588217SMike Christensen 		return (EFAULT);
159130588217SMike Christensen 	}
159230588217SMike Christensen 
159330588217SMike Christensen 	*msglenp = msglen;
159430588217SMike Christensen 	return (0);
159530588217SMike Christensen }
159630588217SMike Christensen 
159730588217SMike Christensen uint64_t vlds_recv_drain_delay_time = 1 * MILLISEC;
159830588217SMike Christensen 
159930588217SMike Christensen static void
vlds_recvq_drain(vlds_svc_info_t * dpsp)160030588217SMike Christensen vlds_recvq_drain(vlds_svc_info_t *dpsp)
160130588217SMike Christensen {
160230588217SMike Christensen 	vlds_recv_hdr_t	*rhp, *nextp;
160330588217SMike Christensen 
160430588217SMike Christensen 	mutex_enter(&dpsp->recv_lock);
160530588217SMike Christensen 	dpsp->state = VLDS_RECV_UNREG_PENDING;
160630588217SMike Christensen 	for (rhp = dpsp->recv_tailp; rhp != NULL; rhp = nextp) {
160730588217SMike Christensen 		nextp = rhp->next;
160830588217SMike Christensen 		DS_FREE(rhp->data, rhp->datasz);
160930588217SMike Christensen 		DS_FREE(rhp, sizeof (vlds_recv_hdr_t));
161030588217SMike Christensen 	}
161130588217SMike Christensen 	dpsp->recv_headp = NULL;
161230588217SMike Christensen 	dpsp->recv_tailp = NULL;
161330588217SMike Christensen 	dpsp->recv_size = 0;
1614ba9236fbSMike Christensen 	dpsp->recv_cnt = 0;
161530588217SMike Christensen 
161630588217SMike Christensen 	/*
161730588217SMike Christensen 	 * Make sure other readers have exited.
161830588217SMike Christensen 	 */
161930588217SMike Christensen 	while (dpsp->recv_nreaders > 0) {
162030588217SMike Christensen 		cv_broadcast(&dpsp->recv_cv);
162130588217SMike Christensen 		mutex_exit(&dpsp->recv_lock);
162230588217SMike Christensen 		delay(vlds_recv_drain_delay_time);
162330588217SMike Christensen 		mutex_enter(&dpsp->recv_lock);
162430588217SMike Christensen 	}
162530588217SMike Christensen 
162630588217SMike Christensen 	mutex_exit(&dpsp->recv_lock);
162730588217SMike Christensen }
162830588217SMike Christensen 
162930588217SMike Christensen static int
vlds_recvq_put_data(vlds_svc_info_t * dpsp,void * buf,size_t buflen)163030588217SMike Christensen vlds_recvq_put_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen)
163130588217SMike Christensen {
163230588217SMike Christensen 	vlds_recv_hdr_t	*rhp;
163330588217SMike Christensen 
163430588217SMike Christensen 	mutex_enter(&dpsp->recv_lock);
163530588217SMike Christensen 	if (dpsp->state != VLDS_RECV_UNREG_PENDING) {
1636ba9236fbSMike Christensen 		/*
1637ba9236fbSMike Christensen 		 * If we've already encountered an overflow, or there
1638ba9236fbSMike Christensen 		 * are pending messages and either queue size and
1639ba9236fbSMike Christensen 		 * message limits will be exceeded with this message,
1640ba9236fbSMike Christensen 		 * we mark the recvq as overflowed and return an ENOBUFS
1641ba9236fbSMike Christensen 		 * error.  This allows the enqueuing of one big message
1642ba9236fbSMike Christensen 		 * or several little messages.
1643ba9236fbSMike Christensen 		 */
1644ba9236fbSMike Christensen 		if ((dpsp->state == VLDS_RECV_OVERFLOW) ||
1645ba9236fbSMike Christensen 		    ((dpsp->recv_cnt != 0) &&
1646ba9236fbSMike Christensen 		    ((dpsp->recv_size + buflen) > vlds_recvq_maxsize) ||
1647ba9236fbSMike Christensen 		    ((dpsp->recv_cnt + 1) > vlds_recvq_maxmsg))) {
1648ba9236fbSMike Christensen 			DS_DBG_RCVQ(CE_NOTE, "%s: user data queue overflow",
1649ba9236fbSMike Christensen 			    __func__);
1650ba9236fbSMike Christensen 			dpsp->state = VLDS_RECV_OVERFLOW;
1651ba9236fbSMike Christensen 			cv_broadcast(&dpsp->recv_cv);
1652ba9236fbSMike Christensen 			mutex_exit(&dpsp->recv_lock);
1653ba9236fbSMike Christensen 			return (ENOBUFS);
1654ba9236fbSMike Christensen 		}
1655ba9236fbSMike Christensen 
165630588217SMike Christensen 		DS_DBG_RCVQ(CE_NOTE, "%s: user data enqueued msglen: %ld",
165730588217SMike Christensen 		    __func__, buflen);
165830588217SMike Christensen 		DS_DUMP_MSG(DS_DBG_FLAG_RCVQ, buf, buflen);
165930588217SMike Christensen 		rhp = DS_MALLOC(sizeof (vlds_recv_hdr_t));
166030588217SMike Christensen 		rhp->data = DS_MALLOC(buflen);
166130588217SMike Christensen 		(void) memcpy(rhp->data, buf, buflen);
166230588217SMike Christensen 		rhp->datasz = buflen;
166330588217SMike Christensen 		rhp->next = NULL;
166430588217SMike Christensen 		if (dpsp->recv_headp == NULL) {
166530588217SMike Christensen 			dpsp->recv_headp = rhp;
166630588217SMike Christensen 			dpsp->recv_tailp = rhp;
166730588217SMike Christensen 		} else {
166830588217SMike Christensen 			dpsp->recv_tailp->next = rhp;
166930588217SMike Christensen 			dpsp->recv_tailp = rhp;
167030588217SMike Christensen 		}
167130588217SMike Christensen 		dpsp->recv_size += rhp->datasz;
1672ba9236fbSMike Christensen 		dpsp->recv_cnt += 1;
167330588217SMike Christensen 		cv_broadcast(&dpsp->recv_cv);
167430588217SMike Christensen 	}
167530588217SMike Christensen 	mutex_exit(&dpsp->recv_lock);
167630588217SMike Christensen 	return (0);
167730588217SMike Christensen }
167830588217SMike Christensen 
167930588217SMike Christensen static int
vlds_recv_msg(ds_svc_hdl_t hdl,void * buf,size_t buflen,size_t * msglenp,int mode)168030588217SMike Christensen vlds_recv_msg(ds_svc_hdl_t hdl, void *buf, size_t buflen, size_t *msglenp,
168130588217SMike Christensen     int mode)
168230588217SMike Christensen {
168330588217SMike Christensen 	void *dpsp;
168430588217SMike Christensen 	ds_cb_arg_t cbarg;
168530588217SMike Christensen 	uint32_t flags;
168630588217SMike Christensen 	int rv;
168730588217SMike Christensen 
168830588217SMike Christensen 	if ((rv = ds_hdl_get_cbarg(hdl, &cbarg)) != 0) {
168930588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: handle %lx not found (%d)", __func__,
169030588217SMike Christensen 		    hdl, rv);
169130588217SMike Christensen 		return (rv);
169230588217SMike Christensen 	}
169330588217SMike Christensen 	ds_cbarg_get_flags(cbarg, &flags);
169430588217SMike Christensen 	if ((flags & DSSF_ISUSER) == 0 || (flags & DSSF_DATACB_VALID) != 0) {
169530588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: invalid flags: %x", __func__, flags);
169630588217SMike Christensen 		return (EINVAL);
169730588217SMike Christensen 	}
169830588217SMike Christensen 	ds_cbarg_get_drv_per_svc_ptr(cbarg, &dpsp);
169930588217SMike Christensen 	if (dpsp == NULL) {
170030588217SMike Christensen 		DS_DBG_VLDS(CE_NOTE, "%s: recv on non-ready handle: %x",
170130588217SMike Christensen 		    __func__, flags);
170230588217SMike Christensen 		return (ENXIO);
170330588217SMike Christensen 	}
170430588217SMike Christensen 	rv = vlds_recvq_get_data(dpsp, buf, buflen, msglenp, mode);
170530588217SMike Christensen 	return (rv);
170630588217SMike Christensen }
1707