xref: /onnv-gate/usr/src/uts/sun4v/io/ds_pri.c (revision 9387:1778bc8df1f4)
13530Srb144127 /*
23530Srb144127  * CDDL HEADER START
33530Srb144127  *
43530Srb144127  * The contents of this file are subject to the terms of the
53530Srb144127  * Common Development and Distribution License (the "License").
63530Srb144127  * You may not use this file except in compliance with the License.
73530Srb144127  *
83530Srb144127  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93530Srb144127  * or http://www.opensolaris.org/os/licensing.
103530Srb144127  * See the License for the specific language governing permissions
113530Srb144127  * and limitations under the License.
123530Srb144127  *
133530Srb144127  * When distributing Covered Code, include this CDDL HEADER in each
143530Srb144127  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153530Srb144127  * If applicable, add the following below this CDDL HEADER, with the
163530Srb144127  * fields enclosed by brackets "[]" replaced with your own identifying
173530Srb144127  * information: Portions Copyright [yyyy] [name of copyright owner]
183530Srb144127  *
193530Srb144127  * CDDL HEADER END
203530Srb144127  */
213530Srb144127 /*
22*9387SSree.Vemuri@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
233530Srb144127  * Use is subject to license terms.
243530Srb144127  */
253530Srb144127 
263530Srb144127 
273530Srb144127 /*
283530Srb144127  * sun4v domain services PRI driver
293530Srb144127  */
303530Srb144127 
313530Srb144127 #include <sys/types.h>
323530Srb144127 #include <sys/file.h>
333530Srb144127 #include <sys/errno.h>
343530Srb144127 #include <sys/open.h>
353530Srb144127 #include <sys/cred.h>
363530Srb144127 #include <sys/uio.h>
373530Srb144127 #include <sys/stat.h>
383530Srb144127 #include <sys/ksynch.h>
393530Srb144127 #include <sys/modctl.h>
403530Srb144127 #include <sys/conf.h>
413530Srb144127 #include <sys/devops.h>
423530Srb144127 #include <sys/debug.h>
433530Srb144127 #include <sys/cmn_err.h>
443530Srb144127 #include <sys/ddi.h>
453530Srb144127 #include <sys/sunddi.h>
463530Srb144127 #include <sys/ds.h>
473530Srb144127 
483530Srb144127 #include <sys/ds_pri.h>
493530Srb144127 
503530Srb144127 static uint_t ds_pri_debug = 0;
513530Srb144127 #define	DS_PRI_DBG	if (ds_pri_debug) printf
523530Srb144127 
533530Srb144127 #define	DS_PRI_NAME	"ds_pri"
543530Srb144127 
553530Srb144127 #define	TEST_HARNESS
563530Srb144127 #ifdef TEST_HARNESS
573530Srb144127 #define	DS_PRI_MAX_PRI_SIZE	(64 * 1024)
583530Srb144127 
593530Srb144127 #define	DSIOC_TEST_REG	97
603530Srb144127 #define	DSIOC_TEST_UNREG	98
613530Srb144127 #define	DSIOC_TEST_DATA	99
623530Srb144127 
633530Srb144127 struct ds_pri_test_data {
643530Srb144127 	size_t		size;
653530Srb144127 	void		*data;
663530Srb144127 };
673530Srb144127 
683530Srb144127 struct ds_pri_test_data32 {
693530Srb144127 	size32_t	size;
703530Srb144127 	caddr32_t	data;
713530Srb144127 };
723530Srb144127 #endif /* TEST_HARNESS */
733530Srb144127 
743530Srb144127 typedef	enum {
753530Srb144127 	DS_PRI_REQUEST	= 0,
763530Srb144127 	DS_PRI_DATA	= 1,
773530Srb144127 	DS_PRI_UPDATE	= 2
783530Srb144127 } ds_pri_msg_type_t;
793530Srb144127 
803530Srb144127 typedef	struct {
813530Srb144127 	struct {
823530Srb144127 		uint64_t	seq_num;
833530Srb144127 		uint64_t	type;
843530Srb144127 	} hdr;
853530Srb144127 	uint8_t		data[1];
863530Srb144127 } ds_pri_msg_t;
873530Srb144127 
883530Srb144127 	/* The following are bit field flags */
893530Srb144127 	/* No service implies no PRI and no outstanding request */
903530Srb144127 typedef enum {
913530Srb144127 	DS_PRI_NO_SERVICE = 0x0,
923530Srb144127 	DS_PRI_HAS_SERVICE = 0x1,
933530Srb144127 	DS_PRI_REQUESTED = 0x2,
943530Srb144127 	DS_PRI_HAS_PRI = 0x4
953530Srb144127 } ds_pri_flags_t;
963530Srb144127 
973530Srb144127 struct ds_pri_state {
983530Srb144127 	dev_info_t	*dip;
993530Srb144127 	int		instance;
1003530Srb144127 
1013530Srb144127 	kmutex_t	lock;
1023530Srb144127 	kcondvar_t	cv;
1033530Srb144127 
1043530Srb144127 	/* PRI/DS */
1053530Srb144127 	ds_pri_flags_t	state;
1063530Srb144127 	uint64_t	gencount;
1073530Srb144127 	ds_svc_hdl_t	ds_pri_handle;
1083530Srb144127 	void		*ds_pri;
1093530Srb144127 	size_t		ds_pri_len;
1103530Srb144127 	uint64_t	req_id;
1113530Srb144127 	uint64_t	last_req_id;
1123941Svenki 	int		num_opens;
1133530Srb144127 };
1143530Srb144127 
1153530Srb144127 typedef struct ds_pri_state ds_pri_state_t;
1163530Srb144127 
1173530Srb144127 static void *ds_pri_statep;
1183530Srb144127 
1193530Srb144127 static void request_pri(ds_pri_state_t *sp);
1203530Srb144127 
1213530Srb144127 static int ds_pri_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1223530Srb144127 static int ds_pri_attach(dev_info_t *, ddi_attach_cmd_t);
1233530Srb144127 static int ds_pri_detach(dev_info_t *, ddi_detach_cmd_t);
1243530Srb144127 static int ds_pri_open(dev_t *, int, int, cred_t *);
1253530Srb144127 static int ds_pri_close(dev_t, int, int, cred_t *);
1263530Srb144127 static int ds_pri_read(dev_t, struct uio *, cred_t *);
1273530Srb144127 static int ds_pri_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1283530Srb144127 
1293530Srb144127 /*
1303530Srb144127  * DS Callbacks
1313530Srb144127  */
1323530Srb144127 static void ds_pri_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
1333530Srb144127 static void ds_pri_unreg_handler(ds_cb_arg_t arg);
1343530Srb144127 static void ds_pri_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
1353530Srb144127 
1363530Srb144127 /*
1373530Srb144127  * PRI DS capability registration
1383530Srb144127  */
1393530Srb144127 
1403530Srb144127 static ds_ver_t ds_pri_ver_1_0 = { 1, 0 };
1413530Srb144127 
1423530Srb144127 static ds_capability_t ds_pri_cap = {
1433530Srb144127 	"pri",
1443530Srb144127 	&ds_pri_ver_1_0,
1453530Srb144127 	1
1463530Srb144127 };
1473530Srb144127 
1483530Srb144127 /*
1493530Srb144127  * PRI DS Client callback vector
1503530Srb144127  */
1513530Srb144127 static ds_clnt_ops_t ds_pri_ops = {
1523530Srb144127 	ds_pri_reg_handler,	/* ds_reg_cb */
1533530Srb144127 	ds_pri_unreg_handler,	/* ds_unreg_cb */
1543530Srb144127 	ds_pri_data_handler,	/* ds_data_cb */
1553530Srb144127 	NULL			/* cb_arg */
1563530Srb144127 };
1573530Srb144127 
1583530Srb144127 /*
1593530Srb144127  * DS PRI driver Ops Vector
1603530Srb144127  */
1613530Srb144127 static struct cb_ops ds_pri_cb_ops = {
1623530Srb144127 	ds_pri_open,		/* cb_open */
1633530Srb144127 	ds_pri_close,		/* cb_close */
1643530Srb144127 	nodev,			/* cb_strategy */
1653530Srb144127 	nodev,			/* cb_print */
1663530Srb144127 	nodev,			/* cb_dump */
1673530Srb144127 	ds_pri_read,		/* cb_read */
1683530Srb144127 	nodev,			/* cb_write */
1693530Srb144127 	ds_pri_ioctl,		/* cb_ioctl */
1703530Srb144127 	nodev,			/* cb_devmap */
1713530Srb144127 	nodev,			/* cb_mmap */
1723530Srb144127 	nodev,			/* cb_segmap */
1733530Srb144127 	nochpoll,		/* cb_chpoll */
1743530Srb144127 	ddi_prop_op,		/* cb_prop_op */
1753530Srb144127 	(struct streamtab *)NULL, /* cb_str */
1763530Srb144127 	D_MP | D_64BIT,		/* cb_flag */
1773530Srb144127 	CB_REV,			/* cb_rev */
1783530Srb144127 	nodev,			/* cb_aread */
1793530Srb144127 	nodev			/* cb_awrite */
1803530Srb144127 };
1813530Srb144127 
1823530Srb144127 static struct dev_ops ds_pri_dev_ops = {
1833530Srb144127 	DEVO_REV,		/* devo_rev */
1843530Srb144127 	0,			/* devo_refcnt */
1853530Srb144127 	ds_pri_getinfo,		/* devo_getinfo */
1863530Srb144127 	nulldev,		/* devo_identify */
1873530Srb144127 	nulldev,		/* devo_probe */
1883530Srb144127 	ds_pri_attach,		/* devo_attach */
1893530Srb144127 	ds_pri_detach,		/* devo_detach */
1903530Srb144127 	nodev,			/* devo_reset */
1913530Srb144127 	&ds_pri_cb_ops,		/* devo_cb_ops */
1923530Srb144127 	(struct bus_ops *)NULL,	/* devo_bus_ops */
1937656SSherry.Moore@Sun.COM 	nulldev,		/* devo_power */
1947656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* devo_quiesce */
1953530Srb144127 };
1963530Srb144127 
1973530Srb144127 static struct modldrv modldrv = {
1983530Srb144127 	&mod_driverops,
1997656SSherry.Moore@Sun.COM 	"Domain Services PRI Driver",
2003530Srb144127 	&ds_pri_dev_ops
2013530Srb144127 };
2023530Srb144127 
2033530Srb144127 static struct modlinkage modlinkage = {
2043530Srb144127 	MODREV_1,
2053530Srb144127 	(void *)&modldrv,
2063530Srb144127 	NULL
2073530Srb144127 };
2083530Srb144127 
2093530Srb144127 
2103530Srb144127 int
2113530Srb144127 _init(void)
2123530Srb144127 {
2133530Srb144127 	int retval;
2143530Srb144127 
2153530Srb144127 	retval = ddi_soft_state_init(&ds_pri_statep,
2163530Srb144127 	    sizeof (ds_pri_state_t), 0);
2173530Srb144127 	if (retval != 0)
2183530Srb144127 		return (retval);
2193530Srb144127 
2203530Srb144127 	retval = mod_install(&modlinkage);
2213530Srb144127 	if (retval != 0) {
2223530Srb144127 		ddi_soft_state_fini(&ds_pri_statep);
2233530Srb144127 		return (retval);
2243530Srb144127 	}
2253530Srb144127 
2263530Srb144127 	return (retval);
2273530Srb144127 }
2283530Srb144127 
2293530Srb144127 
2303530Srb144127 int
2313530Srb144127 _info(struct modinfo *modinfop)
2323530Srb144127 {
2333530Srb144127 	return (mod_info(&modlinkage, modinfop));
2343530Srb144127 }
2353530Srb144127 
2363530Srb144127 
2373530Srb144127 int
2383530Srb144127 _fini(void)
2393530Srb144127 {
2403530Srb144127 	int retval;
2413530Srb144127 
2423530Srb144127 	if ((retval = mod_remove(&modlinkage)) != 0)
2433530Srb144127 		return (retval);
2443530Srb144127 
2453530Srb144127 	ddi_soft_state_fini(&ds_pri_statep);
2463530Srb144127 
2473530Srb144127 	return (retval);
2483530Srb144127 }
2493530Srb144127 
2503530Srb144127 
2513530Srb144127 /*ARGSUSED*/
2523530Srb144127 static int
2533530Srb144127 ds_pri_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2543530Srb144127 {
2553530Srb144127 	ds_pri_state_t *sp;
2563530Srb144127 	int retval = DDI_FAILURE;
2573530Srb144127 
2583530Srb144127 	ASSERT(resultp != NULL);
2593530Srb144127 
2603530Srb144127 	switch (cmd) {
2613530Srb144127 	case DDI_INFO_DEVT2DEVINFO:
2623530Srb144127 		sp = ddi_get_soft_state(ds_pri_statep, getminor((dev_t)arg));
2633530Srb144127 		if (sp != NULL) {
2643530Srb144127 			*resultp = sp->dip;
2653530Srb144127 			retval = DDI_SUCCESS;
2663530Srb144127 		} else
2673530Srb144127 			*resultp = NULL;
2683530Srb144127 		break;
2693530Srb144127 
2703530Srb144127 	case DDI_INFO_DEVT2INSTANCE:
2713530Srb144127 		*resultp = (void *)(uintptr_t)getminor((dev_t)arg);
2723530Srb144127 		retval = DDI_SUCCESS;
2733530Srb144127 		break;
2743530Srb144127 
2753530Srb144127 	default:
2763530Srb144127 		break;
2773530Srb144127 	}
2783530Srb144127 
2793530Srb144127 	return (retval);
2803530Srb144127 }
2813530Srb144127 
2823530Srb144127 
2833530Srb144127 static int
2843530Srb144127 ds_pri_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2853530Srb144127 {
2863530Srb144127 	int instance;
2873530Srb144127 	ds_pri_state_t *sp;
2883530Srb144127 	int rv;
2893530Srb144127 
2903530Srb144127 	switch (cmd) {
2913530Srb144127 	case DDI_ATTACH:
2923530Srb144127 		break;
2933530Srb144127 
2943530Srb144127 	case DDI_RESUME:
2953530Srb144127 		return (DDI_SUCCESS);
2963530Srb144127 
2973530Srb144127 	default:
2983530Srb144127 		return (DDI_FAILURE);
2993530Srb144127 	}
3003530Srb144127 
3013530Srb144127 	instance = ddi_get_instance(dip);
3023530Srb144127 
3033530Srb144127 	if (ddi_soft_state_zalloc(ds_pri_statep, instance) !=
3043530Srb144127 	    DDI_SUCCESS) {
3053530Srb144127 		cmn_err(CE_WARN, "%s@%d: Unable to allocate state",
3063530Srb144127 		    DS_PRI_NAME, instance);
3073530Srb144127 		return (DDI_FAILURE);
3083530Srb144127 	}
3093530Srb144127 	sp = ddi_get_soft_state(ds_pri_statep, instance);
3103530Srb144127 
3113530Srb144127 	mutex_init(&sp->lock, NULL, MUTEX_DEFAULT, NULL);
3123530Srb144127 	cv_init(&sp->cv, NULL, CV_DEFAULT, NULL);
3133530Srb144127 
3143530Srb144127 	if (ddi_create_minor_node(dip, DS_PRI_NAME, S_IFCHR, instance,
3154407Snarayan 	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
3163530Srb144127 		cmn_err(CE_WARN, "%s@%d: Unable to create minor node",
3173530Srb144127 		    DS_PRI_NAME, instance);
3183530Srb144127 		goto fail;
3193530Srb144127 	}
3203530Srb144127 
3213530Srb144127 	if (ds_pri_ops.cb_arg != NULL)
3223530Srb144127 		goto fail;
3233530Srb144127 	ds_pri_ops.cb_arg = dip;
3243530Srb144127 
3253530Srb144127 	sp->state = DS_PRI_NO_SERVICE;
3263530Srb144127 
3273530Srb144127 	/* Until the service registers the handle is invalid */
3283530Srb144127 	sp->ds_pri_handle = DS_INVALID_HDL;
3293530Srb144127 
3303530Srb144127 	sp->ds_pri = NULL;
3313530Srb144127 	sp->ds_pri_len = 0;
3323530Srb144127 	sp->req_id = 0;
3333941Svenki 	sp->num_opens = 0;
3343530Srb144127 
3353530Srb144127 	if ((rv = ds_cap_init(&ds_pri_cap, &ds_pri_ops)) != 0) {
3363530Srb144127 		cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
3373530Srb144127 		goto fail;
3383530Srb144127 	}
3393530Srb144127 
3403530Srb144127 	ddi_report_dev(dip);
3413530Srb144127 
3423530Srb144127 	return (DDI_SUCCESS);
3433530Srb144127 
3443530Srb144127 fail:
3453530Srb144127 	ddi_remove_minor_node(dip, NULL);
3463530Srb144127 	cv_destroy(&sp->cv);
3473530Srb144127 	mutex_destroy(&sp->lock);
3483530Srb144127 	ddi_soft_state_free(ds_pri_statep, instance);
3493530Srb144127 	return (DDI_FAILURE);
3503530Srb144127 
3513530Srb144127 }
3523530Srb144127 
3533530Srb144127 
3543530Srb144127 /*ARGSUSED*/
3553530Srb144127 static int
3563530Srb144127 ds_pri_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3573530Srb144127 {
3583530Srb144127 	ds_pri_state_t *sp;
3593530Srb144127 	int instance;
3603530Srb144127 	int rv;
3613530Srb144127 
3623530Srb144127 	instance = ddi_get_instance(dip);
3633530Srb144127 	sp = ddi_get_soft_state(ds_pri_statep, instance);
3643530Srb144127 
3653530Srb144127 	switch (cmd) {
3663530Srb144127 	case DDI_DETACH:
3673530Srb144127 		break;
3683530Srb144127 
3693530Srb144127 	case DDI_SUSPEND:
3703530Srb144127 		return (DDI_SUCCESS);
3713530Srb144127 
3723530Srb144127 	default:
3733530Srb144127 		return (DDI_FAILURE);
3743530Srb144127 	}
3753530Srb144127 
3763530Srb144127 	/* This really shouldn't fail - but check anyway */
3773530Srb144127 	if ((rv = ds_cap_fini(&ds_pri_cap)) != 0) {
3783530Srb144127 		cmn_err(CE_WARN, "ds_cap_fini failed: %d", rv);
3793530Srb144127 	}
3803530Srb144127 
3813530Srb144127 	if (sp != NULL && sp->ds_pri_len != 0)
3823530Srb144127 		kmem_free(sp->ds_pri, sp->ds_pri_len);
3833530Srb144127 
3844407Snarayan 	ds_pri_ops.cb_arg = NULL;
3854407Snarayan 
3863530Srb144127 	ddi_remove_minor_node(dip, NULL);
3873530Srb144127 	cv_destroy(&sp->cv);
3883530Srb144127 	mutex_destroy(&sp->lock);
3893530Srb144127 	ddi_soft_state_free(ds_pri_statep, instance);
3903530Srb144127 
3913530Srb144127 	return (DDI_SUCCESS);
3923530Srb144127 }
3933530Srb144127 
3943530Srb144127 
3953530Srb144127 /*ARGSUSED*/
3963530Srb144127 static int
3973530Srb144127 ds_pri_open(dev_t *devp, int flag, int otyp, cred_t *credp)
3983530Srb144127 {
3993530Srb144127 	ds_pri_state_t *sp;
4003530Srb144127 	int instance;
4013530Srb144127 
4023530Srb144127 	if (otyp != OTYP_CHR)
4033530Srb144127 		return (EINVAL);
4043530Srb144127 
4053530Srb144127 	instance = getminor(*devp);
4063530Srb144127 	sp = ddi_get_soft_state(ds_pri_statep, instance);
4073530Srb144127 	if (sp == NULL)
4083530Srb144127 		return (ENXIO);
4093530Srb144127 
4103941Svenki 	mutex_enter(&sp->lock);
4113941Svenki 
4123941Svenki 	/*
4133941Svenki 	 * If we're here and the state is DS_PRI_NO_SERVICE then this
4143941Svenki 	 * means that ds hasn't yet called the registration callback.
4154109Skellena 	 * A while loop is necessary as we might have been woken up
4164109Skellena 	 * prematurely, e.g., due to a debugger or "pstack" etc.
4173941Svenki 	 * Wait here and the callback will signal us when it has completed
4183941Svenki 	 * its work.
4193941Svenki 	 */
4204109Skellena 	while (sp->state == DS_PRI_NO_SERVICE) {
4213941Svenki 		if (cv_wait_sig(&sp->cv, &sp->lock) == 0) {
4223941Svenki 			mutex_exit(&sp->lock);
4233941Svenki 			return (EINTR);
4243941Svenki 		}
4253941Svenki 	}
4263941Svenki 
4273941Svenki 	sp->num_opens++;
4283941Svenki 	mutex_exit(&sp->lock);
4293941Svenki 
4303530Srb144127 	/*
4313530Srb144127 	 * On open we dont fetch the PRI even if we have a valid service
4323530Srb144127 	 * handle. PRI fetch is essentially lazy and on-demand.
4333530Srb144127 	 */
4343530Srb144127 
4353530Srb144127 	DS_PRI_DBG("ds_pri_open: state = 0x%x\n", sp->state);
4363530Srb144127 
4373530Srb144127 	return (0);
4383530Srb144127 }
4393530Srb144127 
4403530Srb144127 
4413530Srb144127 /*ARGSUSED*/
4423530Srb144127 static int
4433530Srb144127 ds_pri_close(dev_t dev, int flag, int otyp, cred_t *credp)
4443530Srb144127 {
4453530Srb144127 	int instance;
4463941Svenki 	ds_pri_state_t *sp;
4473530Srb144127 
4483530Srb144127 	if (otyp != OTYP_CHR)
4493530Srb144127 		return (EINVAL);
4503530Srb144127 
4513530Srb144127 	DS_PRI_DBG("ds_pri_close\n");
4523530Srb144127 
4533530Srb144127 	instance = getminor(dev);
4543941Svenki 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
4553530Srb144127 		return (ENXIO);
4563530Srb144127 
4573941Svenki 	mutex_enter(&sp->lock);
4583941Svenki 	if (!(sp->state & DS_PRI_HAS_SERVICE)) {
4593941Svenki 		mutex_exit(&sp->lock);
4603941Svenki 		return (0);
4613941Svenki 	}
4623941Svenki 
4633941Svenki 	if (--sp->num_opens > 0) {
4643941Svenki 		mutex_exit(&sp->lock);
4653941Svenki 		return (0);
4663941Svenki 	}
4673941Svenki 
4683941Svenki 	/* If we have an old PRI - remove it */
4693941Svenki 	if (sp->state & DS_PRI_HAS_PRI) {
4703941Svenki 		if (sp->ds_pri != NULL && sp->ds_pri_len > 0) {
4713941Svenki 			/*
4723941Svenki 			 * remove the old data if we have an
4733941Svenki 			 * outstanding request
4743941Svenki 			 */
4753941Svenki 			kmem_free(sp->ds_pri, sp->ds_pri_len);
4763941Svenki 			sp->ds_pri_len = 0;
4773941Svenki 			sp->ds_pri = NULL;
4783941Svenki 		}
4793941Svenki 		sp->state &= ~DS_PRI_HAS_PRI;
4803941Svenki 	}
4813941Svenki 	sp->state &= ~DS_PRI_REQUESTED;
4823941Svenki 	mutex_exit(&sp->lock);
4833530Srb144127 	return (0);
4843530Srb144127 }
4853530Srb144127 
4863530Srb144127 
4873530Srb144127 /*ARGSUSED*/
4883530Srb144127 static int
4893530Srb144127 ds_pri_read(dev_t dev, struct uio *uiop, cred_t *credp)
4903530Srb144127 {
4913530Srb144127 	ds_pri_state_t *sp;
4923530Srb144127 	int instance;
4933530Srb144127 	size_t len;
4943530Srb144127 	int retval;
4953530Srb144127 	caddr_t tmpbufp;
496*9387SSree.Vemuri@Sun.COM 	offset_t off = uiop->uio_offset;
4973530Srb144127 
4983530Srb144127 	instance = getminor(dev);
4993530Srb144127 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
5003530Srb144127 		return (ENXIO);
5013530Srb144127 
5023530Srb144127 	len = uiop->uio_resid;
5033530Srb144127 
5043530Srb144127 	if (len == 0)
5053530Srb144127 		return (0);
5063530Srb144127 
5073530Srb144127 	mutex_enter(&sp->lock);
5083530Srb144127 
5093530Srb144127 	DS_PRI_DBG("ds_pri_read: state = 0x%x\n", sp->state);
5103530Srb144127 
5113530Srb144127 	/* block or bail if there is no current PRI */
5123530Srb144127 	if (!(sp->state & DS_PRI_HAS_PRI)) {
5133530Srb144127 		DS_PRI_DBG("ds_pri_read: no PRI held\n");
5143530Srb144127 
5153530Srb144127 		if (uiop->uio_fmode & (FNDELAY | FNONBLOCK)) {
5163530Srb144127 			mutex_exit(&sp->lock);
5173530Srb144127 			return (EAGAIN);
5183530Srb144127 		}
5193530Srb144127 
5203530Srb144127 		while (!(sp->state & DS_PRI_HAS_PRI)) {
5213530Srb144127 			DS_PRI_DBG("ds_pri_read: state = 0x%x\n", sp->state);
5223530Srb144127 			request_pri(sp);
5233530Srb144127 			if (cv_wait_sig(&sp->cv, &sp->lock) == 0) {
5243530Srb144127 				mutex_exit(&sp->lock);
5253530Srb144127 				return (EINTR);
5263530Srb144127 			}
5273530Srb144127 		}
5283530Srb144127 	}
5293530Srb144127 
530*9387SSree.Vemuri@Sun.COM 	if (len > sp->ds_pri_len)
531*9387SSree.Vemuri@Sun.COM 		len = sp->ds_pri_len;
5323530Srb144127 
5333530Srb144127 	if (len == 0) {
5343530Srb144127 		mutex_exit(&sp->lock);
5353530Srb144127 		return (0);
5363530Srb144127 	}
5373530Srb144127 
5383530Srb144127 	/*
5393530Srb144127 	 * We're supposed to move the data out to userland, but
5403530Srb144127 	 * that can suspend because of page faults etc., and meanwhile
5413530Srb144127 	 * other parts of this driver want to update the PRI buffer ...
5423530Srb144127 	 * we could hold the data buffer locked with a flag etc.,
5433530Srb144127 	 * but that's still a lock ... a simpler mechanism - if not quite
5443530Srb144127 	 * as performance efficient is to simply clone here the part of
5453530Srb144127 	 * the buffer we care about and then the original can be released
5463530Srb144127 	 * for further updates while the uiomove continues.
5473530Srb144127 	 */
5483530Srb144127 
5493530Srb144127 	tmpbufp = kmem_alloc(len, KM_SLEEP);
550*9387SSree.Vemuri@Sun.COM 	bcopy(((caddr_t)sp->ds_pri), tmpbufp, len);
5513530Srb144127 	mutex_exit(&sp->lock);
5523530Srb144127 
5533530Srb144127 	retval = uiomove(tmpbufp, len, UIO_READ, uiop);
5543530Srb144127 
5553530Srb144127 	kmem_free(tmpbufp, len);
5563530Srb144127 
557*9387SSree.Vemuri@Sun.COM 	/*
558*9387SSree.Vemuri@Sun.COM 	 * restore uio_offset after uiomove since the driver
559*9387SSree.Vemuri@Sun.COM 	 * does not support the concept of position.
560*9387SSree.Vemuri@Sun.COM 	 */
561*9387SSree.Vemuri@Sun.COM 	uiop->uio_offset = off;
562*9387SSree.Vemuri@Sun.COM 
5633530Srb144127 	return (retval);
5643530Srb144127 }
5653530Srb144127 
5663530Srb144127 
5673530Srb144127 /*ARGSUSED*/
5683530Srb144127 static int
5693530Srb144127 ds_pri_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
5703530Srb144127     int *rvalp)
5713530Srb144127 {
5723530Srb144127 	ds_pri_state_t *sp;
5733530Srb144127 	int instance;
5743530Srb144127 
5753530Srb144127 	instance = getminor(dev);
5763530Srb144127 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
5773530Srb144127 		return (ENXIO);
5783530Srb144127 
5793530Srb144127 	switch (cmd) {
5803530Srb144127 	case DSPRI_GETINFO: {
5813530Srb144127 		struct dspri_info info;
5823530Srb144127 
5833530Srb144127 		if (!(mode & FREAD))
5843530Srb144127 			return (EACCES);
5853530Srb144127 
5863530Srb144127 		/*
5873530Srb144127 		 * We are not guaranteed that ddi_copyout(9F) will read
5883530Srb144127 		 * atomically anything larger than a byte.  Therefore we
5893530Srb144127 		 * must duplicate the size before copying it out to the user.
5903530Srb144127 		 */
5913530Srb144127 		mutex_enter(&sp->lock);
5923530Srb144127 
5933530Srb144127 loop:;
5943530Srb144127 		if (sp->state & DS_PRI_HAS_PRI) {
5953530Srb144127 			/* If we have a PRI simply return the info */
5963530Srb144127 			info.size = sp->ds_pri_len;
5973530Srb144127 			info.token = sp->gencount;
5983530Srb144127 		} else
5993530Srb144127 		if (!(sp->state & DS_PRI_HAS_SERVICE)) {
6003530Srb144127 			/* If we have no service return a nil response */
6013530Srb144127 			info.size = 0;
6023530Srb144127 			info.token = 0;
6033530Srb144127 		} else {
6043530Srb144127 			request_pri(sp);
6053530Srb144127 			/* wait for something & check again */
6063530Srb144127 			if (cv_wait_sig(&sp->cv, &sp->lock) == 0) {
6073530Srb144127 				mutex_exit(&sp->lock);
6083530Srb144127 				return (EINTR);
6093530Srb144127 			}
6103530Srb144127 			goto loop;
6113530Srb144127 		}
6123530Srb144127 		DS_PRI_DBG("ds_pri_ioctl: DSPRI_GETINFO sz=0x%lx tok=0x%lx\n",
6134407Snarayan 		    info.size, info.token);
6143530Srb144127 		mutex_exit(&sp->lock);
6153530Srb144127 
6163530Srb144127 		if (ddi_copyout(&info, (void *)arg, sizeof (info), mode) != 0)
6173530Srb144127 			return (EFAULT);
6183530Srb144127 		break;
6193530Srb144127 	}
6203530Srb144127 
6213530Srb144127 	case DSPRI_WAIT: {
6223530Srb144127 		uint64_t gencount;
6233530Srb144127 
6243530Srb144127 		if (ddi_copyin((void *)arg, &gencount, sizeof (gencount),
6253530Srb144127 		    mode) != 0)
6263530Srb144127 			return (EFAULT);
6273530Srb144127 
6283530Srb144127 		mutex_enter(&sp->lock);
6293530Srb144127 
6303530Srb144127 		DS_PRI_DBG("ds_pri_ioctl: DSPRI_WAIT gen=0x%lx sp->gen=0x%lx\n",
6314407Snarayan 		    gencount, sp->gencount);
6323530Srb144127 
6333530Srb144127 		while ((sp->state & DS_PRI_HAS_PRI) == 0 ||
6344407Snarayan 		    gencount == sp->gencount) {
6354109Skellena 			if ((sp->state & DS_PRI_HAS_PRI) == 0)
6364109Skellena 				request_pri(sp);
6373530Srb144127 			if (cv_wait_sig(&sp->cv, &sp->lock) == 0) {
6383530Srb144127 				mutex_exit(&sp->lock);
6393530Srb144127 				return (EINTR);
6403530Srb144127 			}
6413530Srb144127 		}
6423530Srb144127 		mutex_exit(&sp->lock);
6433530Srb144127 		break;
6443530Srb144127 	}
6453530Srb144127 
6463530Srb144127 	default:
6473530Srb144127 		return (ENOTTY);
6483530Srb144127 	}
6493530Srb144127 	return (0);
6503530Srb144127 }
6513530Srb144127 
6523530Srb144127 
6533530Srb144127 	/* assumes sp->lock is held when called */
6543530Srb144127 static void
6553530Srb144127 request_pri(ds_pri_state_t *sp)
6563530Srb144127 {
6573530Srb144127 	ds_pri_msg_t reqmsg;
6583530Srb144127 
6593530Srb144127 	ASSERT(MUTEX_HELD(&sp->lock));
6603530Srb144127 
6613530Srb144127 	/* If a request is already pending we're done */
6623530Srb144127 	if (!(sp->state & DS_PRI_HAS_SERVICE))
6633530Srb144127 		return;
6643530Srb144127 	if (sp->state & DS_PRI_REQUESTED)
6653530Srb144127 		return;
6663530Srb144127 
6673530Srb144127 	/* If we have an old PRI - remove it */
6683530Srb144127 	if (sp->state & DS_PRI_HAS_PRI) {
6693530Srb144127 		ASSERT(sp->ds_pri_len != 0);
6703530Srb144127 		ASSERT(sp->ds_pri != NULL);
6713530Srb144127 
6723530Srb144127 		/* remove the old data if we have an outstanding request */
6733530Srb144127 		kmem_free(sp->ds_pri, sp->ds_pri_len);
6743530Srb144127 		sp->ds_pri_len = 0;
6753530Srb144127 		sp->ds_pri = NULL;
6763530Srb144127 		sp->state &= ~DS_PRI_HAS_PRI;
6773530Srb144127 	} else {
6783530Srb144127 		ASSERT(sp->ds_pri == NULL);
6793530Srb144127 		ASSERT(sp->ds_pri_len == 0);
6803530Srb144127 	}
6813530Srb144127 
6823530Srb144127 	reqmsg.hdr.seq_num = ++(sp->req_id);
6833530Srb144127 	reqmsg.hdr.type = DS_PRI_REQUEST;
6843530Srb144127 
6853530Srb144127 	DS_PRI_DBG("request_pri: request id 0x%lx\n", sp->req_id);
6863530Srb144127 
6873530Srb144127 		/*
6883530Srb144127 		 * Request consists of header only.
6893530Srb144127 		 * We don't care about fail status for ds_send;
6903530Srb144127 		 * if it does fail we will get an unregister callback
6913530Srb144127 		 * from the DS framework and we handle the state change
6923530Srb144127 		 * there.
6933530Srb144127 		 */
6943530Srb144127 	(void) ds_cap_send(sp->ds_pri_handle, &reqmsg, sizeof (reqmsg.hdr));
6953530Srb144127 
6963530Srb144127 	sp->state |= DS_PRI_REQUESTED;
6973530Srb144127 	sp->last_req_id = sp->req_id;
6983530Srb144127 }
6993530Srb144127 
7003530Srb144127 /*
7013530Srb144127  * DS Callbacks
7023530Srb144127  */
7033530Srb144127 /*ARGSUSED*/
7043530Srb144127 static void
7053530Srb144127 ds_pri_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
7063530Srb144127 {
7073530Srb144127 	dev_info_t *dip = arg;
7083530Srb144127 	ds_pri_state_t *sp;
7093530Srb144127 	int instance;
7103530Srb144127 
7113530Srb144127 	instance = ddi_get_instance(dip);
7123530Srb144127 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
7133530Srb144127 		return;
7143530Srb144127 
7153530Srb144127 	DS_PRI_DBG("ds_pri_reg_handler: registering handle 0x%lx for version "
7164407Snarayan 	    "0x%x:0x%x\n", (uint64_t)hdl, ver->major, ver->minor);
7173530Srb144127 
7183530Srb144127 	/* When the domain service comes up automatically req the pri */
7193530Srb144127 	mutex_enter(&sp->lock);
7203530Srb144127 
7213530Srb144127 	ASSERT(sp->ds_pri_handle == DS_INVALID_HDL);
7223530Srb144127 	sp->ds_pri_handle = hdl;
7233530Srb144127 
7243530Srb144127 	ASSERT(sp->state == DS_PRI_NO_SERVICE);
7253530Srb144127 	ASSERT(sp->ds_pri == NULL);
7263530Srb144127 	ASSERT(sp->ds_pri_len == 0);
7273530Srb144127 
7283530Srb144127 	/* have service, but no PRI */
7293530Srb144127 	sp->state |= DS_PRI_HAS_SERVICE;
7303530Srb144127 
7313941Svenki 	/*
7323941Svenki 	 * Cannot request a PRI here, because the reg handler cannot
7333941Svenki 	 * do a DS send operation - we take care of this later.
7343941Svenki 	 */
7353941Svenki 
7363941Svenki 	/* Wake up anyone waiting in open() */
7373941Svenki 	cv_broadcast(&sp->cv);
7383941Svenki 
7393530Srb144127 	mutex_exit(&sp->lock);
7403530Srb144127 }
7413530Srb144127 
7423530Srb144127 
7433530Srb144127 static void
7443530Srb144127 ds_pri_unreg_handler(ds_cb_arg_t arg)
7453530Srb144127 {
7463530Srb144127 	dev_info_t *dip = arg;
7473530Srb144127 	ds_pri_state_t *sp;
7483530Srb144127 	int instance;
7493530Srb144127 
7503530Srb144127 	instance = ddi_get_instance(dip);
7513530Srb144127 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
7523530Srb144127 		return;
7533530Srb144127 
7543530Srb144127 	DS_PRI_DBG("ds_pri_unreg_handler: un-registering ds_pri service\n");
7553530Srb144127 
7563530Srb144127 	mutex_enter(&sp->lock);
7573530Srb144127 
7583530Srb144127 	/* Once the service goes - if we have a PRI at hand free it up */
7593530Srb144127 	if (sp->ds_pri_len != 0) {
7603530Srb144127 		kmem_free(sp->ds_pri, sp->ds_pri_len);
7613530Srb144127 		sp->ds_pri_len = 0;
7623530Srb144127 		sp->ds_pri = NULL;
7633530Srb144127 	}
7643530Srb144127 	sp->ds_pri_handle = DS_INVALID_HDL;
7653530Srb144127 	sp->state = DS_PRI_NO_SERVICE;
7663530Srb144127 
7673530Srb144127 	mutex_exit(&sp->lock);
7683530Srb144127 }
7693530Srb144127 
7703530Srb144127 
7713530Srb144127 static void
7723530Srb144127 ds_pri_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
7733530Srb144127 {
7743530Srb144127 	dev_info_t *dip = arg;
7753530Srb144127 	ds_pri_state_t *sp;
7763530Srb144127 	int instance;
7773530Srb144127 	void *data;
7783530Srb144127 	ds_pri_msg_t	*msgp;
7793530Srb144127 	size_t	pri_size;
7803530Srb144127 
7813530Srb144127 	msgp = (ds_pri_msg_t *)buf;
7823530Srb144127 
7833530Srb144127 	/* make sure the header is at least valid */
7843530Srb144127 	if (buflen < sizeof (msgp->hdr))
7853530Srb144127 		return;
7863530Srb144127 
7873530Srb144127 	DS_PRI_DBG("ds_pri_data_handler: msg buf len 0x%lx : type 0x%lx, "
7884407Snarayan 	    "seqn 0x%lx\n", buflen, msgp->hdr.type, msgp->hdr.seq_num);
7893530Srb144127 
7903530Srb144127 	instance = ddi_get_instance(dip);
7913530Srb144127 	if ((sp = ddi_get_soft_state(ds_pri_statep, instance)) == NULL)
7923530Srb144127 		return;
7933530Srb144127 
7943530Srb144127 	mutex_enter(&sp->lock);
7953530Srb144127 
7963530Srb144127 	ASSERT(sp->state & DS_PRI_HAS_SERVICE);
7973530Srb144127 
7983530Srb144127 	switch (msgp->hdr.type) {
7993530Srb144127 	case DS_PRI_DATA:	/* in response to a request from us */
8003530Srb144127 		break;
8013530Srb144127 	case DS_PRI_UPDATE:	/* aynch notification */
8023530Srb144127 			/* our default response to this is to request the PRI */
8033530Srb144127 		/* simply issue a request for the new PRI */
8043530Srb144127 		request_pri(sp);
8053530Srb144127 		goto done;
8063530Srb144127 	default:	/* ignore garbage or unknown message types */
8073530Srb144127 		goto done;
8083530Srb144127 	}
8093530Srb144127 
8103530Srb144127 	/*
8113530Srb144127 	 * If there is no pending PRI request, then we've received a
8123530Srb144127 	 * bogus data message ... so ignore it.
8133530Srb144127 	 */
8143530Srb144127 
8153530Srb144127 	if (!(sp->state & DS_PRI_REQUESTED)) {
8163530Srb144127 		cmn_err(CE_WARN, "Received DS pri data without request");
8173530Srb144127 		goto done;
8183530Srb144127 	}
8193530Srb144127 
8203530Srb144127 	/* response to a request therefore old PRI must be gone */
8213530Srb144127 	ASSERT(!(sp->state & DS_PRI_HAS_PRI));
8223530Srb144127 	ASSERT(sp->ds_pri_len == 0);
8233530Srb144127 	ASSERT(sp->ds_pri == NULL);
8243530Srb144127 
8253530Srb144127 	/* response seq_num should match our request seq_num */
8263530Srb144127 	if (msgp->hdr.seq_num != sp->last_req_id) {
8273530Srb144127 		cmn_err(CE_WARN, "Received DS pri data out of sequence with "
8284407Snarayan 		    "request");
8293530Srb144127 		goto done;
8303530Srb144127 	}
8313530Srb144127 
8323530Srb144127 	pri_size = buflen - sizeof (msgp->hdr);
8333530Srb144127 	data = kmem_alloc(pri_size, KM_SLEEP);
8343530Srb144127 	sp->ds_pri = data;
8353530Srb144127 	sp->ds_pri_len = pri_size;
8363530Srb144127 	bcopy(msgp->data, data, sp->ds_pri_len);
8373530Srb144127 	sp->state &= ~DS_PRI_REQUESTED;
8383530Srb144127 	sp->state |= DS_PRI_HAS_PRI;
8393530Srb144127 
8403530Srb144127 	sp->gencount++;
8413530Srb144127 	cv_broadcast(&sp->cv);
8423530Srb144127 
8433530Srb144127 done:;
8443530Srb144127 	mutex_exit(&sp->lock);
8453530Srb144127 }
846