xref: /onnv-gate/usr/src/uts/common/avs/ns/sv/sv.c (revision 9093:cd587b0bd19c)
17836SJohn.Forte@Sun.COM /*
27836SJohn.Forte@Sun.COM  * CDDL HEADER START
37836SJohn.Forte@Sun.COM  *
47836SJohn.Forte@Sun.COM  * The contents of this file are subject to the terms of the
57836SJohn.Forte@Sun.COM  * Common Development and Distribution License (the "License").
67836SJohn.Forte@Sun.COM  * You may not use this file except in compliance with the License.
77836SJohn.Forte@Sun.COM  *
87836SJohn.Forte@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97836SJohn.Forte@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107836SJohn.Forte@Sun.COM  * See the License for the specific language governing permissions
117836SJohn.Forte@Sun.COM  * and limitations under the License.
127836SJohn.Forte@Sun.COM  *
137836SJohn.Forte@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147836SJohn.Forte@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157836SJohn.Forte@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167836SJohn.Forte@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177836SJohn.Forte@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187836SJohn.Forte@Sun.COM  *
197836SJohn.Forte@Sun.COM  * CDDL HEADER END
207836SJohn.Forte@Sun.COM  */
217836SJohn.Forte@Sun.COM /*
22*9093SRamana.Srikanth@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237836SJohn.Forte@Sun.COM  * Use is subject to license terms.
247836SJohn.Forte@Sun.COM  */
257836SJohn.Forte@Sun.COM 
267836SJohn.Forte@Sun.COM /*
277836SJohn.Forte@Sun.COM  * Storage Volume Character and Block Driver (SV)
287836SJohn.Forte@Sun.COM  *
297836SJohn.Forte@Sun.COM  * This driver implements a simplistic /dev/{r}dsk/ interface to a
307836SJohn.Forte@Sun.COM  * specified disk volume that is otherwise managed by the Prism
317836SJohn.Forte@Sun.COM  * software.  The SV driver layers itself onto the underlying disk
327836SJohn.Forte@Sun.COM  * device driver by changing function pointers in the cb_ops
337836SJohn.Forte@Sun.COM  * structure.
347836SJohn.Forte@Sun.COM  *
357836SJohn.Forte@Sun.COM  * CONFIGURATION:
367836SJohn.Forte@Sun.COM  *
377836SJohn.Forte@Sun.COM  * 1. Configure the driver using the svadm utility.
387836SJohn.Forte@Sun.COM  * 2. Access the device as before through /dev/rdsk/c?t?d?s?
397836SJohn.Forte@Sun.COM  *
407836SJohn.Forte@Sun.COM  * LIMITATIONS:
417836SJohn.Forte@Sun.COM  *
427836SJohn.Forte@Sun.COM  * This driver should NOT be used to share a device between another
437836SJohn.Forte@Sun.COM  * DataServices user interface module (e.g., STE) and a user accessing
447836SJohn.Forte@Sun.COM  * the device through the block device in O_WRITE mode.  This is because
457836SJohn.Forte@Sun.COM  * writes through the block device are asynchronous (due to the page
467836SJohn.Forte@Sun.COM  * cache) and so consistency between the block device user and the
477836SJohn.Forte@Sun.COM  * STE user cannot be guaranteed.
487836SJohn.Forte@Sun.COM  *
497836SJohn.Forte@Sun.COM  * Data is copied between system struct buf(9s) and nsc_vec_t.  This is
507836SJohn.Forte@Sun.COM  * wasteful and slow.
517836SJohn.Forte@Sun.COM  */
527836SJohn.Forte@Sun.COM 
537836SJohn.Forte@Sun.COM #include <sys/debug.h>
547836SJohn.Forte@Sun.COM #include <sys/types.h>
557836SJohn.Forte@Sun.COM 
567836SJohn.Forte@Sun.COM #include <sys/ksynch.h>
577836SJohn.Forte@Sun.COM #include <sys/kmem.h>
587836SJohn.Forte@Sun.COM #include <sys/errno.h>
597836SJohn.Forte@Sun.COM #include <sys/varargs.h>
607836SJohn.Forte@Sun.COM #include <sys/file.h>
617836SJohn.Forte@Sun.COM #include <sys/open.h>
627836SJohn.Forte@Sun.COM #include <sys/conf.h>
637836SJohn.Forte@Sun.COM #include <sys/cred.h>
647836SJohn.Forte@Sun.COM #include <sys/buf.h>
657836SJohn.Forte@Sun.COM #include <sys/uio.h>
667836SJohn.Forte@Sun.COM #ifndef DS_DDICT
677836SJohn.Forte@Sun.COM #include <sys/pathname.h>
687836SJohn.Forte@Sun.COM #endif
697836SJohn.Forte@Sun.COM #include <sys/aio_req.h>
707836SJohn.Forte@Sun.COM #include <sys/dkio.h>
717836SJohn.Forte@Sun.COM #include <sys/vtoc.h>
727836SJohn.Forte@Sun.COM #include <sys/cmn_err.h>
737836SJohn.Forte@Sun.COM #include <sys/modctl.h>
747836SJohn.Forte@Sun.COM #include <sys/ddi.h>
757836SJohn.Forte@Sun.COM #include <sys/sunddi.h>
767836SJohn.Forte@Sun.COM #include <sys/sunldi.h>
777836SJohn.Forte@Sun.COM #include <sys/nsctl/nsvers.h>
787836SJohn.Forte@Sun.COM 
797836SJohn.Forte@Sun.COM #include <sys/nsc_thread.h>
807836SJohn.Forte@Sun.COM #include <sys/unistat/spcs_s.h>
817836SJohn.Forte@Sun.COM #include <sys/unistat/spcs_s_k.h>
827836SJohn.Forte@Sun.COM #include <sys/unistat/spcs_errors.h>
837836SJohn.Forte@Sun.COM 
847836SJohn.Forte@Sun.COM #ifdef DS_DDICT
857836SJohn.Forte@Sun.COM #include "../contract.h"
867836SJohn.Forte@Sun.COM #endif
877836SJohn.Forte@Sun.COM 
887836SJohn.Forte@Sun.COM #include "../nsctl.h"
897836SJohn.Forte@Sun.COM 
907836SJohn.Forte@Sun.COM 
917836SJohn.Forte@Sun.COM #include <sys/sdt.h>		/* dtrace is S10 or later */
927836SJohn.Forte@Sun.COM 
937836SJohn.Forte@Sun.COM #include "sv.h"
947836SJohn.Forte@Sun.COM #include "sv_impl.h"
957836SJohn.Forte@Sun.COM #include "sv_efi.h"
967836SJohn.Forte@Sun.COM 
977836SJohn.Forte@Sun.COM #define	MAX_EINTR_COUNT 1000
987836SJohn.Forte@Sun.COM 
997836SJohn.Forte@Sun.COM /*
1007836SJohn.Forte@Sun.COM  * sv_mod_status
1017836SJohn.Forte@Sun.COM  */
1027836SJohn.Forte@Sun.COM #define	SV_PREVENT_UNLOAD 1
1037836SJohn.Forte@Sun.COM #define	SV_ALLOW_UNLOAD	2
1047836SJohn.Forte@Sun.COM 
1057836SJohn.Forte@Sun.COM static const int sv_major_rev = ISS_VERSION_MAJ;	/* Major number */
1067836SJohn.Forte@Sun.COM static const int sv_minor_rev = ISS_VERSION_MIN;	/* Minor number */
1077836SJohn.Forte@Sun.COM static const int sv_micro_rev = ISS_VERSION_MIC;	/* Micro number */
1087836SJohn.Forte@Sun.COM static const int sv_baseline_rev = ISS_VERSION_NUM;	/* Baseline number */
1097836SJohn.Forte@Sun.COM 
1107836SJohn.Forte@Sun.COM #ifdef DKIOCPARTITION
1117836SJohn.Forte@Sun.COM /*
1127836SJohn.Forte@Sun.COM  * CRC32 polynomial table needed for computing the checksums
1137836SJohn.Forte@Sun.COM  * in an EFI vtoc.
1147836SJohn.Forte@Sun.COM  */
1157836SJohn.Forte@Sun.COM static const uint32_t sv_crc32_table[256] = { CRC32_TABLE };
1167836SJohn.Forte@Sun.COM #endif
1177836SJohn.Forte@Sun.COM 
1187836SJohn.Forte@Sun.COM static clock_t sv_config_time;		/* Time of successful {en,dis}able */
1197836SJohn.Forte@Sun.COM static int sv_debug;			/* Set non-zero for debug to syslog */
1207836SJohn.Forte@Sun.COM static int sv_mod_status;		/* Set to prevent modunload */
1217836SJohn.Forte@Sun.COM 
1227836SJohn.Forte@Sun.COM static dev_info_t *sv_dip;		/* Single DIP for driver */
1237836SJohn.Forte@Sun.COM static kmutex_t sv_mutex;		/* Protect global lists, etc. */
1247836SJohn.Forte@Sun.COM 
1257836SJohn.Forte@Sun.COM static nsc_mem_t	*sv_mem;	/* nsctl memory allocator token */
1267836SJohn.Forte@Sun.COM 
1277836SJohn.Forte@Sun.COM 
1287836SJohn.Forte@Sun.COM /*
1297836SJohn.Forte@Sun.COM  * Per device and per major state.
1307836SJohn.Forte@Sun.COM  */
1317836SJohn.Forte@Sun.COM 
1327836SJohn.Forte@Sun.COM #ifndef _SunOS_5_6
1337836SJohn.Forte@Sun.COM #define	UNSAFE_ENTER()
1347836SJohn.Forte@Sun.COM #define	UNSAFE_EXIT()
1357836SJohn.Forte@Sun.COM #else
1367836SJohn.Forte@Sun.COM #define	UNSAFE_ENTER()	mutex_enter(&unsafe_driver)
1377836SJohn.Forte@Sun.COM #define	UNSAFE_EXIT()	mutex_exit(&unsafe_driver)
1387836SJohn.Forte@Sun.COM #endif
1397836SJohn.Forte@Sun.COM 
1407836SJohn.Forte@Sun.COM 					/* hash table of major dev structures */
1417836SJohn.Forte@Sun.COM static sv_maj_t *sv_majors[SV_MAJOR_HASH_CNT] = {0};
1427836SJohn.Forte@Sun.COM static sv_dev_t *sv_devs;		/* array of per device structures */
1437836SJohn.Forte@Sun.COM static int sv_max_devices;		/* SV version of nsc_max_devices() */
1447836SJohn.Forte@Sun.COM static int sv_ndevices;			/* number of SV enabled devices */
1457836SJohn.Forte@Sun.COM 
1467836SJohn.Forte@Sun.COM /*
1477836SJohn.Forte@Sun.COM  * Threading.
1487836SJohn.Forte@Sun.COM  */
1497836SJohn.Forte@Sun.COM 
1507836SJohn.Forte@Sun.COM int sv_threads_max = 1024;		/* maximum # to dynamically alloc */
1517836SJohn.Forte@Sun.COM int sv_threads = 32;			/* # to pre-allocate (see sv.conf) */
1527836SJohn.Forte@Sun.COM int sv_threads_extra = 0;		/* addl # we would have alloc'ed */
1537836SJohn.Forte@Sun.COM 
1547836SJohn.Forte@Sun.COM static nstset_t *sv_tset;		/* the threadset pointer */
1557836SJohn.Forte@Sun.COM 
1567836SJohn.Forte@Sun.COM static int sv_threads_hysteresis = 4;	/* hysteresis for threadset resizing */
1577836SJohn.Forte@Sun.COM static int sv_threads_dev = 2;		/* # of threads to alloc per device */
1587836SJohn.Forte@Sun.COM static int sv_threads_inc = 8;		/* increment for changing the set */
1597836SJohn.Forte@Sun.COM static int sv_threads_needed;		/* number of threads needed */
1607836SJohn.Forte@Sun.COM static int sv_no_threads;		/* number of nsc_create errors */
1617836SJohn.Forte@Sun.COM static int sv_max_nlive;		/* max number of threads running */
1627836SJohn.Forte@Sun.COM 
1637836SJohn.Forte@Sun.COM 
1647836SJohn.Forte@Sun.COM 
1657836SJohn.Forte@Sun.COM /*
1667836SJohn.Forte@Sun.COM  * nsctl fd callbacks.
1677836SJohn.Forte@Sun.COM  */
1687836SJohn.Forte@Sun.COM 
1697836SJohn.Forte@Sun.COM static int svattach_fd(blind_t);
1707836SJohn.Forte@Sun.COM static int svdetach_fd(blind_t);
1717836SJohn.Forte@Sun.COM 
1727836SJohn.Forte@Sun.COM static nsc_def_t sv_fd_def[] = {
1737836SJohn.Forte@Sun.COM 	{ "Attach",	(uintptr_t)svattach_fd, },
1747836SJohn.Forte@Sun.COM 	{ "Detach",	(uintptr_t)svdetach_fd, },
1757836SJohn.Forte@Sun.COM 	{ 0, 0, }
1767836SJohn.Forte@Sun.COM };
1777836SJohn.Forte@Sun.COM 
1787836SJohn.Forte@Sun.COM /*
1797836SJohn.Forte@Sun.COM  * cb_ops functions.
1807836SJohn.Forte@Sun.COM  */
1817836SJohn.Forte@Sun.COM 
1827836SJohn.Forte@Sun.COM static int svopen(dev_t *, int, int, cred_t *);
1837836SJohn.Forte@Sun.COM static int svclose(dev_t, int, int, cred_t *);
1847836SJohn.Forte@Sun.COM static int svioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1857836SJohn.Forte@Sun.COM static int svprint(dev_t, char *);
1867836SJohn.Forte@Sun.COM 
1877836SJohn.Forte@Sun.COM /*
1887836SJohn.Forte@Sun.COM  * These next functions are layered into the underlying driver's devops.
1897836SJohn.Forte@Sun.COM  */
1907836SJohn.Forte@Sun.COM 
1917836SJohn.Forte@Sun.COM static int sv_lyr_open(dev_t *, int, int, cred_t *);
1927836SJohn.Forte@Sun.COM static int sv_lyr_close(dev_t, int, int, cred_t *);
1937836SJohn.Forte@Sun.COM static int sv_lyr_strategy(struct buf *);
1947836SJohn.Forte@Sun.COM static int sv_lyr_read(dev_t, struct uio *, cred_t *);
1957836SJohn.Forte@Sun.COM static int sv_lyr_write(dev_t, struct uio *, cred_t *);
1967836SJohn.Forte@Sun.COM static int sv_lyr_aread(dev_t, struct aio_req *, cred_t *);
1977836SJohn.Forte@Sun.COM static int sv_lyr_awrite(dev_t, struct aio_req *, cred_t *);
1987836SJohn.Forte@Sun.COM static int sv_lyr_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1997836SJohn.Forte@Sun.COM 
2007836SJohn.Forte@Sun.COM static struct cb_ops sv_cb_ops = {
2017836SJohn.Forte@Sun.COM 	svopen,		/* open */
2027836SJohn.Forte@Sun.COM 	svclose,	/* close */
2037836SJohn.Forte@Sun.COM 	nulldev,	/* strategy */
2047836SJohn.Forte@Sun.COM 	svprint,
2057836SJohn.Forte@Sun.COM 	nodev,		/* dump */
2067836SJohn.Forte@Sun.COM 	nodev,		/* read */
2077836SJohn.Forte@Sun.COM 	nodev,		/* write */
2087836SJohn.Forte@Sun.COM 	svioctl,
2097836SJohn.Forte@Sun.COM 	nodev,		/* devmap */
2107836SJohn.Forte@Sun.COM 	nodev,		/* mmap */
2117836SJohn.Forte@Sun.COM 	nodev,		/* segmap */
2127836SJohn.Forte@Sun.COM 	nochpoll,	/* poll */
2137836SJohn.Forte@Sun.COM 	ddi_prop_op,
2147836SJohn.Forte@Sun.COM 	NULL,		/* NOT a stream */
2157836SJohn.Forte@Sun.COM 	D_NEW | D_MP | D_64BIT,
2167836SJohn.Forte@Sun.COM 	CB_REV,
2177836SJohn.Forte@Sun.COM 	nodev,		/* aread */
2187836SJohn.Forte@Sun.COM 	nodev,		/* awrite */
2197836SJohn.Forte@Sun.COM };
2207836SJohn.Forte@Sun.COM 
2217836SJohn.Forte@Sun.COM 
2227836SJohn.Forte@Sun.COM /*
2237836SJohn.Forte@Sun.COM  * dev_ops functions.
2247836SJohn.Forte@Sun.COM  */
2257836SJohn.Forte@Sun.COM 
2267836SJohn.Forte@Sun.COM static int sv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
2277836SJohn.Forte@Sun.COM static int sv_attach(dev_info_t *, ddi_attach_cmd_t);
2287836SJohn.Forte@Sun.COM static int sv_detach(dev_info_t *, ddi_detach_cmd_t);
2297836SJohn.Forte@Sun.COM 
2307836SJohn.Forte@Sun.COM static struct dev_ops sv_ops = {
2317836SJohn.Forte@Sun.COM 	DEVO_REV,
2327836SJohn.Forte@Sun.COM 	0,
2337836SJohn.Forte@Sun.COM 	sv_getinfo,
2347836SJohn.Forte@Sun.COM 	nulldev,	/* identify */
2357836SJohn.Forte@Sun.COM 	nulldev,	/* probe */
2367836SJohn.Forte@Sun.COM 	sv_attach,
2377836SJohn.Forte@Sun.COM 	sv_detach,
2387836SJohn.Forte@Sun.COM 	nodev,		/* reset */
2397836SJohn.Forte@Sun.COM 	&sv_cb_ops,
2407836SJohn.Forte@Sun.COM 	(struct bus_ops *)0
2417836SJohn.Forte@Sun.COM };
2427836SJohn.Forte@Sun.COM 
2437836SJohn.Forte@Sun.COM /*
2447836SJohn.Forte@Sun.COM  * Module linkage.
2457836SJohn.Forte@Sun.COM  */
2467836SJohn.Forte@Sun.COM 
2477836SJohn.Forte@Sun.COM extern struct mod_ops mod_driverops;
2487836SJohn.Forte@Sun.COM 
2497836SJohn.Forte@Sun.COM static struct modldrv modldrv = {
2507836SJohn.Forte@Sun.COM 	&mod_driverops,
2517836SJohn.Forte@Sun.COM 	"nws:Storage Volume:" ISS_VERSION_STR,
2527836SJohn.Forte@Sun.COM 	&sv_ops
2537836SJohn.Forte@Sun.COM };
2547836SJohn.Forte@Sun.COM 
2557836SJohn.Forte@Sun.COM static struct modlinkage modlinkage = {
2567836SJohn.Forte@Sun.COM 	MODREV_1,
2577836SJohn.Forte@Sun.COM 	&modldrv,
2587836SJohn.Forte@Sun.COM 	0
2597836SJohn.Forte@Sun.COM };
2607836SJohn.Forte@Sun.COM 
2617836SJohn.Forte@Sun.COM 
2627836SJohn.Forte@Sun.COM int
_init(void)2637836SJohn.Forte@Sun.COM _init(void)
2647836SJohn.Forte@Sun.COM {
2657836SJohn.Forte@Sun.COM 	int error;
2667836SJohn.Forte@Sun.COM 
2677836SJohn.Forte@Sun.COM 	mutex_init(&sv_mutex, NULL, MUTEX_DRIVER, NULL);
2687836SJohn.Forte@Sun.COM 
2697836SJohn.Forte@Sun.COM 	if ((error = mod_install(&modlinkage)) != 0) {
2707836SJohn.Forte@Sun.COM 		mutex_destroy(&sv_mutex);
2717836SJohn.Forte@Sun.COM 		return (error);
2727836SJohn.Forte@Sun.COM 	}
2737836SJohn.Forte@Sun.COM 
2747836SJohn.Forte@Sun.COM #ifdef DEBUG
275*9093SRamana.Srikanth@Sun.COM 	cmn_err(CE_CONT, "!sv (revision %d.%d.%d.%d, %s, %s)\n",
2767836SJohn.Forte@Sun.COM 	    sv_major_rev, sv_minor_rev, sv_micro_rev, sv_baseline_rev,
2777836SJohn.Forte@Sun.COM 	    ISS_VERSION_STR, BUILD_DATE_STR);
2787836SJohn.Forte@Sun.COM #else
2797836SJohn.Forte@Sun.COM 	if (sv_micro_rev) {
280*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_CONT, "!sv (revision %d.%d.%d, %s, %s)\n",
2817836SJohn.Forte@Sun.COM 		    sv_major_rev, sv_minor_rev, sv_micro_rev,
2827836SJohn.Forte@Sun.COM 		    ISS_VERSION_STR, BUILD_DATE_STR);
2837836SJohn.Forte@Sun.COM 	} else {
284*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_CONT, "!sv (revision %d.%d, %s, %s)\n",
2857836SJohn.Forte@Sun.COM 		    sv_major_rev, sv_minor_rev,
2867836SJohn.Forte@Sun.COM 		    ISS_VERSION_STR, BUILD_DATE_STR);
2877836SJohn.Forte@Sun.COM 	}
2887836SJohn.Forte@Sun.COM #endif
2897836SJohn.Forte@Sun.COM 
2907836SJohn.Forte@Sun.COM 	return (error);
2917836SJohn.Forte@Sun.COM }
2927836SJohn.Forte@Sun.COM 
2937836SJohn.Forte@Sun.COM 
2947836SJohn.Forte@Sun.COM int
_fini(void)2957836SJohn.Forte@Sun.COM _fini(void)
2967836SJohn.Forte@Sun.COM {
2977836SJohn.Forte@Sun.COM 	int error;
2987836SJohn.Forte@Sun.COM 
2997836SJohn.Forte@Sun.COM 	if ((error = mod_remove(&modlinkage)) != 0)
3007836SJohn.Forte@Sun.COM 		return (error);
3017836SJohn.Forte@Sun.COM 
3027836SJohn.Forte@Sun.COM 	mutex_destroy(&sv_mutex);
3037836SJohn.Forte@Sun.COM 
3047836SJohn.Forte@Sun.COM 	return (error);
3057836SJohn.Forte@Sun.COM }
3067836SJohn.Forte@Sun.COM 
3077836SJohn.Forte@Sun.COM 
3087836SJohn.Forte@Sun.COM int
_info(struct modinfo * modinfop)3097836SJohn.Forte@Sun.COM _info(struct modinfo *modinfop)
3107836SJohn.Forte@Sun.COM {
3117836SJohn.Forte@Sun.COM 	return (mod_info(&modlinkage, modinfop));
3127836SJohn.Forte@Sun.COM }
3137836SJohn.Forte@Sun.COM 
3147836SJohn.Forte@Sun.COM 
3157836SJohn.Forte@Sun.COM /*
3167836SJohn.Forte@Sun.COM  * Locking & State.
3177836SJohn.Forte@Sun.COM  *
3187836SJohn.Forte@Sun.COM  * sv_mutex protects config information - sv_maj_t and sv_dev_t lists;
3197836SJohn.Forte@Sun.COM  * threadset creation and sizing; sv_ndevices.
3207836SJohn.Forte@Sun.COM  *
3217836SJohn.Forte@Sun.COM  * If we need to hold both sv_mutex and sv_lock, then the sv_mutex
3227836SJohn.Forte@Sun.COM  * must be acquired first.
3237836SJohn.Forte@Sun.COM  *
3247836SJohn.Forte@Sun.COM  * sv_lock protects the sv_dev_t structure for an individual device.
3257836SJohn.Forte@Sun.COM  *
3267836SJohn.Forte@Sun.COM  * sv_olock protects the otyp/open members of the sv_dev_t.  If we need
3277836SJohn.Forte@Sun.COM  * to hold both sv_lock and sv_olock, then the sv_lock must be acquired
3287836SJohn.Forte@Sun.COM  * first.
3297836SJohn.Forte@Sun.COM  *
3307836SJohn.Forte@Sun.COM  * nsc_reserve/nsc_release are used in NSC_MULTI mode to allow multiple
3317836SJohn.Forte@Sun.COM  * I/O operations to a device simultaneously, as above.
3327836SJohn.Forte@Sun.COM  *
3337836SJohn.Forte@Sun.COM  * All nsc_open/nsc_close/nsc_reserve/nsc_release operations that occur
3347836SJohn.Forte@Sun.COM  * with sv_lock write-locked must be done with (sv_state == SV_PENDING)
3357836SJohn.Forte@Sun.COM  * and (sv_pending == curthread) so that any recursion through
3367836SJohn.Forte@Sun.COM  * sv_lyr_open/sv_lyr_close can be detected.
3377836SJohn.Forte@Sun.COM  */
3387836SJohn.Forte@Sun.COM 
3397836SJohn.Forte@Sun.COM 
3407836SJohn.Forte@Sun.COM static int
sv_init_devs(void)3417836SJohn.Forte@Sun.COM sv_init_devs(void)
3427836SJohn.Forte@Sun.COM {
3437836SJohn.Forte@Sun.COM 	int i;
3447836SJohn.Forte@Sun.COM 
3457836SJohn.Forte@Sun.COM 	ASSERT(MUTEX_HELD(&sv_mutex));
3467836SJohn.Forte@Sun.COM 
3477836SJohn.Forte@Sun.COM 	if (sv_max_devices > 0)
3487836SJohn.Forte@Sun.COM 		return (0);
3497836SJohn.Forte@Sun.COM 
3507836SJohn.Forte@Sun.COM 	sv_max_devices = nsc_max_devices();
3517836SJohn.Forte@Sun.COM 
3527836SJohn.Forte@Sun.COM 	if (sv_max_devices <= 0) {
3537836SJohn.Forte@Sun.COM 		/* nsctl is not attached (nskernd not running) */
3547836SJohn.Forte@Sun.COM 		if (sv_debug > 0)
355*9093SRamana.Srikanth@Sun.COM 			cmn_err(CE_CONT, "!sv: nsc_max_devices = 0\n");
3567836SJohn.Forte@Sun.COM 		return (EAGAIN);
3577836SJohn.Forte@Sun.COM 	}
3587836SJohn.Forte@Sun.COM 
3597836SJohn.Forte@Sun.COM 	sv_devs = nsc_kmem_zalloc((sv_max_devices * sizeof (*sv_devs)),
3607836SJohn.Forte@Sun.COM 	    KM_NOSLEEP, sv_mem);
3617836SJohn.Forte@Sun.COM 
3627836SJohn.Forte@Sun.COM 	if (sv_devs == NULL) {
363*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_WARN, "!sv: could not allocate sv_devs array");
3647836SJohn.Forte@Sun.COM 		return (ENOMEM);
3657836SJohn.Forte@Sun.COM 	}
3667836SJohn.Forte@Sun.COM 
3677836SJohn.Forte@Sun.COM 	for (i = 0; i < sv_max_devices; i++) {
3687836SJohn.Forte@Sun.COM 		mutex_init(&sv_devs[i].sv_olock, NULL, MUTEX_DRIVER, NULL);
3697836SJohn.Forte@Sun.COM 		rw_init(&sv_devs[i].sv_lock, NULL, RW_DRIVER, NULL);
3707836SJohn.Forte@Sun.COM 	}
3717836SJohn.Forte@Sun.COM 
3727836SJohn.Forte@Sun.COM 	if (sv_debug > 0)
373*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_CONT, "!sv: sv_init_devs successful\n");
3747836SJohn.Forte@Sun.COM 
3757836SJohn.Forte@Sun.COM 	return (0);
3767836SJohn.Forte@Sun.COM }
3777836SJohn.Forte@Sun.COM 
3787836SJohn.Forte@Sun.COM 
3797836SJohn.Forte@Sun.COM static int
sv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3807836SJohn.Forte@Sun.COM sv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3817836SJohn.Forte@Sun.COM {
3827836SJohn.Forte@Sun.COM 	int rc;
3837836SJohn.Forte@Sun.COM 
3847836SJohn.Forte@Sun.COM 	switch (cmd) {
3857836SJohn.Forte@Sun.COM 
3867836SJohn.Forte@Sun.COM 	case DDI_ATTACH:
3877836SJohn.Forte@Sun.COM 		sv_dip = dip;
3887836SJohn.Forte@Sun.COM 
3897836SJohn.Forte@Sun.COM 		if (ddi_create_minor_node(dip, "sv", S_IFCHR,
390*9093SRamana.Srikanth@Sun.COM 		    0, DDI_PSEUDO, 0) != DDI_SUCCESS)
3917836SJohn.Forte@Sun.COM 			goto failed;
3927836SJohn.Forte@Sun.COM 
3937836SJohn.Forte@Sun.COM 		mutex_enter(&sv_mutex);
3947836SJohn.Forte@Sun.COM 
3957836SJohn.Forte@Sun.COM 		sv_mem = nsc_register_mem("SV", NSC_MEM_LOCAL, 0);
3967836SJohn.Forte@Sun.COM 		if (sv_mem == NULL) {
3977836SJohn.Forte@Sun.COM 			mutex_exit(&sv_mutex);
3987836SJohn.Forte@Sun.COM 			goto failed;
3997836SJohn.Forte@Sun.COM 		}
4007836SJohn.Forte@Sun.COM 
4017836SJohn.Forte@Sun.COM 		rc = sv_init_devs();
4027836SJohn.Forte@Sun.COM 		if (rc != 0 && rc != EAGAIN) {
4037836SJohn.Forte@Sun.COM 			mutex_exit(&sv_mutex);
4047836SJohn.Forte@Sun.COM 			goto failed;
4057836SJohn.Forte@Sun.COM 		}
4067836SJohn.Forte@Sun.COM 
4077836SJohn.Forte@Sun.COM 		mutex_exit(&sv_mutex);
4087836SJohn.Forte@Sun.COM 
4097836SJohn.Forte@Sun.COM 
4107836SJohn.Forte@Sun.COM 		ddi_report_dev(dip);
4117836SJohn.Forte@Sun.COM 
4127836SJohn.Forte@Sun.COM 		sv_threads = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
4137836SJohn.Forte@Sun.COM 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
4147836SJohn.Forte@Sun.COM 		    "sv_threads", sv_threads);
4157836SJohn.Forte@Sun.COM 
4167836SJohn.Forte@Sun.COM 		if (sv_debug > 0)
417*9093SRamana.Srikanth@Sun.COM 			cmn_err(CE_CONT, "!sv: sv_threads=%d\n", sv_threads);
4187836SJohn.Forte@Sun.COM 
4197836SJohn.Forte@Sun.COM 		if (sv_threads > sv_threads_max)
4207836SJohn.Forte@Sun.COM 			sv_threads_max = sv_threads;
4217836SJohn.Forte@Sun.COM 
4227836SJohn.Forte@Sun.COM 		return (DDI_SUCCESS);
4237836SJohn.Forte@Sun.COM 
4247836SJohn.Forte@Sun.COM 	default:
4257836SJohn.Forte@Sun.COM 		return (DDI_FAILURE);
4267836SJohn.Forte@Sun.COM 	}
4277836SJohn.Forte@Sun.COM 
4287836SJohn.Forte@Sun.COM failed:
4297836SJohn.Forte@Sun.COM 	DTRACE_PROBE(sv_attach_failed);
4307836SJohn.Forte@Sun.COM 	(void) sv_detach(dip, DDI_DETACH);
4317836SJohn.Forte@Sun.COM 	return (DDI_FAILURE);
4327836SJohn.Forte@Sun.COM }
4337836SJohn.Forte@Sun.COM 
4347836SJohn.Forte@Sun.COM 
4357836SJohn.Forte@Sun.COM static int
sv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4367836SJohn.Forte@Sun.COM sv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4377836SJohn.Forte@Sun.COM {
4387836SJohn.Forte@Sun.COM 	sv_dev_t *svp;
4397836SJohn.Forte@Sun.COM 	int i;
4407836SJohn.Forte@Sun.COM 
4417836SJohn.Forte@Sun.COM 	switch (cmd) {
4427836SJohn.Forte@Sun.COM 
4437836SJohn.Forte@Sun.COM 	case DDI_DETACH:
4447836SJohn.Forte@Sun.COM 
4457836SJohn.Forte@Sun.COM 		/*
4467836SJohn.Forte@Sun.COM 		 * Check that everything is disabled.
4477836SJohn.Forte@Sun.COM 		 */
4487836SJohn.Forte@Sun.COM 
4497836SJohn.Forte@Sun.COM 		mutex_enter(&sv_mutex);
4507836SJohn.Forte@Sun.COM 
4517836SJohn.Forte@Sun.COM 		if (sv_mod_status == SV_PREVENT_UNLOAD) {
4527836SJohn.Forte@Sun.COM 			mutex_exit(&sv_mutex);
4537836SJohn.Forte@Sun.COM 			DTRACE_PROBE(sv_detach_err_prevent);
4547836SJohn.Forte@Sun.COM 			return (DDI_FAILURE);
4557836SJohn.Forte@Sun.COM 		}
4567836SJohn.Forte@Sun.COM 
4577836SJohn.Forte@Sun.COM 		for (i = 0; sv_devs && i < sv_max_devices; i++) {
4587836SJohn.Forte@Sun.COM 			svp = &sv_devs[i];
4597836SJohn.Forte@Sun.COM 
4607836SJohn.Forte@Sun.COM 			if (svp->sv_state != SV_DISABLE) {
4617836SJohn.Forte@Sun.COM 				mutex_exit(&sv_mutex);
4627836SJohn.Forte@Sun.COM 				DTRACE_PROBE(sv_detach_err_busy);
4637836SJohn.Forte@Sun.COM 				return (DDI_FAILURE);
4647836SJohn.Forte@Sun.COM 			}
4657836SJohn.Forte@Sun.COM 		}
4667836SJohn.Forte@Sun.COM 
4677836SJohn.Forte@Sun.COM 
4687836SJohn.Forte@Sun.COM 		for (i = 0; sv_devs && i < sv_max_devices; i++) {
4697836SJohn.Forte@Sun.COM 			mutex_destroy(&sv_devs[i].sv_olock);
4707836SJohn.Forte@Sun.COM 			rw_destroy(&sv_devs[i].sv_lock);
4717836SJohn.Forte@Sun.COM 		}
4727836SJohn.Forte@Sun.COM 
4737836SJohn.Forte@Sun.COM 		if (sv_devs) {
4747836SJohn.Forte@Sun.COM 			nsc_kmem_free(sv_devs,
4757836SJohn.Forte@Sun.COM 			    (sv_max_devices * sizeof (*sv_devs)));
4767836SJohn.Forte@Sun.COM 			sv_devs = NULL;
4777836SJohn.Forte@Sun.COM 		}
4787836SJohn.Forte@Sun.COM 		sv_max_devices = 0;
4797836SJohn.Forte@Sun.COM 
4807836SJohn.Forte@Sun.COM 		if (sv_mem) {
4817836SJohn.Forte@Sun.COM 			nsc_unregister_mem(sv_mem);
4827836SJohn.Forte@Sun.COM 			sv_mem = NULL;
4837836SJohn.Forte@Sun.COM 		}
4847836SJohn.Forte@Sun.COM 
4857836SJohn.Forte@Sun.COM 		mutex_exit(&sv_mutex);
4867836SJohn.Forte@Sun.COM 
4877836SJohn.Forte@Sun.COM 		/*
4887836SJohn.Forte@Sun.COM 		 * Remove all minor nodes.
4897836SJohn.Forte@Sun.COM 		 */
4907836SJohn.Forte@Sun.COM 
4917836SJohn.Forte@Sun.COM 		ddi_remove_minor_node(dip, NULL);
4927836SJohn.Forte@Sun.COM 		sv_dip = NULL;
4937836SJohn.Forte@Sun.COM 
4947836SJohn.Forte@Sun.COM 		return (DDI_SUCCESS);
4957836SJohn.Forte@Sun.COM 
4967836SJohn.Forte@Sun.COM 	default:
4977836SJohn.Forte@Sun.COM 		return (DDI_FAILURE);
4987836SJohn.Forte@Sun.COM 	}
4997836SJohn.Forte@Sun.COM }
5007836SJohn.Forte@Sun.COM 
5017836SJohn.Forte@Sun.COM static sv_maj_t *
sv_getmajor(const dev_t dev)5027836SJohn.Forte@Sun.COM sv_getmajor(const dev_t dev)
5037836SJohn.Forte@Sun.COM {
5047836SJohn.Forte@Sun.COM 	sv_maj_t **insert, *maj;
5057836SJohn.Forte@Sun.COM 	major_t umaj = getmajor(dev);
5067836SJohn.Forte@Sun.COM 
5077836SJohn.Forte@Sun.COM 	/*
5087836SJohn.Forte@Sun.COM 	 * See if the hash table entry, or one of the hash chains
5097836SJohn.Forte@Sun.COM 	 * is already allocated for this major number
5107836SJohn.Forte@Sun.COM 	 */
5117836SJohn.Forte@Sun.COM 	if ((maj = sv_majors[SV_MAJOR_HASH(umaj)]) != 0) {
5127836SJohn.Forte@Sun.COM 		do {
5137836SJohn.Forte@Sun.COM 			if (maj->sm_major == umaj)
5147836SJohn.Forte@Sun.COM 				return (maj);
5157836SJohn.Forte@Sun.COM 		} while ((maj = maj->sm_next) != 0);
5167836SJohn.Forte@Sun.COM 	}
5177836SJohn.Forte@Sun.COM 
5187836SJohn.Forte@Sun.COM 	/*
5197836SJohn.Forte@Sun.COM 	 * If the sv_mutex is held, there is design flaw, as the only non-mutex
5207836SJohn.Forte@Sun.COM 	 * held callers can be sv_enable() or sv_dev_to_sv()
5217836SJohn.Forte@Sun.COM 	 * Return an error, instead of panicing the system
5227836SJohn.Forte@Sun.COM 	 */
5237836SJohn.Forte@Sun.COM 	if (MUTEX_HELD(&sv_mutex)) {
524*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_WARN, "!sv: could not allocate sv_maj_t");
5257836SJohn.Forte@Sun.COM 		return (NULL);
5267836SJohn.Forte@Sun.COM 	}
5277836SJohn.Forte@Sun.COM 
5287836SJohn.Forte@Sun.COM 	/*
5297836SJohn.Forte@Sun.COM 	 * Determine where to allocate a new element in the hash table
5307836SJohn.Forte@Sun.COM 	 */
5317836SJohn.Forte@Sun.COM 	mutex_enter(&sv_mutex);
5327836SJohn.Forte@Sun.COM 	insert = &(sv_majors[SV_MAJOR_HASH(umaj)]);
5337836SJohn.Forte@Sun.COM 	for (maj = *insert; maj; maj = maj->sm_next) {
5347836SJohn.Forte@Sun.COM 
5357836SJohn.Forte@Sun.COM 		/* Did another thread beat us to it? */
5367836SJohn.Forte@Sun.COM 		if (maj->sm_major == umaj)
5377836SJohn.Forte@Sun.COM 			return (maj);
5387836SJohn.Forte@Sun.COM 
5397836SJohn.Forte@Sun.COM 		/* Find a NULL insert point? */
5407836SJohn.Forte@Sun.COM 		if (maj->sm_next == NULL)
5417836SJohn.Forte@Sun.COM 			insert = &maj->sm_next;
5427836SJohn.Forte@Sun.COM 	}
5437836SJohn.Forte@Sun.COM 
5447836SJohn.Forte@Sun.COM 	/*
5457836SJohn.Forte@Sun.COM 	 * Located the new insert point
5467836SJohn.Forte@Sun.COM 	 */
5477836SJohn.Forte@Sun.COM 	*insert = nsc_kmem_zalloc(sizeof (*maj), KM_NOSLEEP, sv_mem);
5487836SJohn.Forte@Sun.COM 	if ((maj = *insert) != 0)
5497836SJohn.Forte@Sun.COM 		maj->sm_major = umaj;
5507836SJohn.Forte@Sun.COM 	else
551*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_WARN, "!sv: could not allocate sv_maj_t");
5527836SJohn.Forte@Sun.COM 
5537836SJohn.Forte@Sun.COM 	mutex_exit(&sv_mutex);
5547836SJohn.Forte@Sun.COM 
5557836SJohn.Forte@Sun.COM 	return (maj);
5567836SJohn.Forte@Sun.COM }
5577836SJohn.Forte@Sun.COM 
5587836SJohn.Forte@Sun.COM /* ARGSUSED */
5597836SJohn.Forte@Sun.COM 
5607836SJohn.Forte@Sun.COM static int
sv_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)5617836SJohn.Forte@Sun.COM sv_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
5627836SJohn.Forte@Sun.COM {
5637836SJohn.Forte@Sun.COM 	int rc = DDI_FAILURE;
5647836SJohn.Forte@Sun.COM 
5657836SJohn.Forte@Sun.COM 	switch (infocmd) {
5667836SJohn.Forte@Sun.COM 
5677836SJohn.Forte@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
5687836SJohn.Forte@Sun.COM 		*result = sv_dip;
5697836SJohn.Forte@Sun.COM 		rc = DDI_SUCCESS;
5707836SJohn.Forte@Sun.COM 		break;
5717836SJohn.Forte@Sun.COM 
5727836SJohn.Forte@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
5737836SJohn.Forte@Sun.COM 		/*
5747836SJohn.Forte@Sun.COM 		 * We only have a single instance.
5757836SJohn.Forte@Sun.COM 		 */
5767836SJohn.Forte@Sun.COM 		*result = 0;
5777836SJohn.Forte@Sun.COM 		rc = DDI_SUCCESS;
5787836SJohn.Forte@Sun.COM 		break;
5797836SJohn.Forte@Sun.COM 
5807836SJohn.Forte@Sun.COM 	default:
5817836SJohn.Forte@Sun.COM 		break;
5827836SJohn.Forte@Sun.COM 	}
5837836SJohn.Forte@Sun.COM 
5847836SJohn.Forte@Sun.COM 	return (rc);
5857836SJohn.Forte@Sun.COM }
5867836SJohn.Forte@Sun.COM 
5877836SJohn.Forte@Sun.COM 
5887836SJohn.Forte@Sun.COM /*
5897836SJohn.Forte@Sun.COM  * Hashing of devices onto major device structures.
5907836SJohn.Forte@Sun.COM  *
5917836SJohn.Forte@Sun.COM  * Individual device structures are hashed onto one of the sm_hash[]
5927836SJohn.Forte@Sun.COM  * buckets in the relevant major device structure.
5937836SJohn.Forte@Sun.COM  *
5947836SJohn.Forte@Sun.COM  * Hash insertion and deletion -must- be done with sv_mutex held.  Hash
5957836SJohn.Forte@Sun.COM  * searching does not require the mutex because of the sm_seq member.
5967836SJohn.Forte@Sun.COM  * sm_seq is incremented on each insertion (-after- hash chain pointer
5977836SJohn.Forte@Sun.COM  * manipulation) and each deletion (-before- hash chain pointer
5987836SJohn.Forte@Sun.COM  * manipulation).  When searching the hash chain, the seq number is
5997836SJohn.Forte@Sun.COM  * checked before accessing each device structure, if the seq number has
6007836SJohn.Forte@Sun.COM  * changed, then we restart the search from the top of the hash chain.
6017836SJohn.Forte@Sun.COM  * If we restart more than SV_HASH_RETRY times, we take sv_mutex and search
6027836SJohn.Forte@Sun.COM  * the hash chain (we are guaranteed that this search cannot be
6037836SJohn.Forte@Sun.COM  * interrupted).
6047836SJohn.Forte@Sun.COM  */
6057836SJohn.Forte@Sun.COM 
6067836SJohn.Forte@Sun.COM #define	SV_HASH_RETRY	16
6077836SJohn.Forte@Sun.COM 
6087836SJohn.Forte@Sun.COM static sv_dev_t *
sv_dev_to_sv(const dev_t dev,sv_maj_t ** majpp)6097836SJohn.Forte@Sun.COM sv_dev_to_sv(const dev_t dev, sv_maj_t **majpp)
6107836SJohn.Forte@Sun.COM {
6117836SJohn.Forte@Sun.COM 	minor_t umin = getminor(dev);
6127836SJohn.Forte@Sun.COM 	sv_dev_t **hb, *next, *svp;
6137836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
6147836SJohn.Forte@Sun.COM 	int seq;
6157836SJohn.Forte@Sun.COM 	int try;
6167836SJohn.Forte@Sun.COM 
6177836SJohn.Forte@Sun.COM 	/* Get major hash table */
6187836SJohn.Forte@Sun.COM 	maj = sv_getmajor(dev);
6197836SJohn.Forte@Sun.COM 	if (majpp)
6207836SJohn.Forte@Sun.COM 		*majpp = maj;
6217836SJohn.Forte@Sun.COM 	if (maj == NULL)
6227836SJohn.Forte@Sun.COM 		return (NULL);
6237836SJohn.Forte@Sun.COM 
6247836SJohn.Forte@Sun.COM 	if (maj->sm_inuse == 0) {
6257836SJohn.Forte@Sun.COM 		DTRACE_PROBE1(
6267836SJohn.Forte@Sun.COM 		    sv_dev_to_sv_end,
6277836SJohn.Forte@Sun.COM 		    dev_t, dev);
6287836SJohn.Forte@Sun.COM 		return (NULL);
6297836SJohn.Forte@Sun.COM 	}
6307836SJohn.Forte@Sun.COM 
6317836SJohn.Forte@Sun.COM 	hb = &(maj->sm_hash[SV_MINOR_HASH(umin)]);
6327836SJohn.Forte@Sun.COM 	try = 0;
6337836SJohn.Forte@Sun.COM 
6347836SJohn.Forte@Sun.COM retry:
6357836SJohn.Forte@Sun.COM 	if (try > SV_HASH_RETRY)
6367836SJohn.Forte@Sun.COM 		mutex_enter(&sv_mutex);
6377836SJohn.Forte@Sun.COM 
6387836SJohn.Forte@Sun.COM 	seq = maj->sm_seq;
6397836SJohn.Forte@Sun.COM 	for (svp = *hb; svp; svp = next) {
6407836SJohn.Forte@Sun.COM 		next = svp->sv_hash;
6417836SJohn.Forte@Sun.COM 
6427836SJohn.Forte@Sun.COM 		nsc_membar_stld();	/* preserve register load order */
6437836SJohn.Forte@Sun.COM 
6447836SJohn.Forte@Sun.COM 		if (maj->sm_seq != seq) {
645*9093SRamana.Srikanth@Sun.COM 			DTRACE_PROBE1(sv_dev_to_sv_retry, dev_t, dev);
6467836SJohn.Forte@Sun.COM 			try++;
6477836SJohn.Forte@Sun.COM 			goto retry;
6487836SJohn.Forte@Sun.COM 		}
6497836SJohn.Forte@Sun.COM 
6507836SJohn.Forte@Sun.COM 		if (svp->sv_dev == dev)
6517836SJohn.Forte@Sun.COM 			break;
6527836SJohn.Forte@Sun.COM 	}
6537836SJohn.Forte@Sun.COM 
6547836SJohn.Forte@Sun.COM 	if (try > SV_HASH_RETRY)
6557836SJohn.Forte@Sun.COM 		mutex_exit(&sv_mutex);
6567836SJohn.Forte@Sun.COM 
6577836SJohn.Forte@Sun.COM 	return (svp);
6587836SJohn.Forte@Sun.COM }
6597836SJohn.Forte@Sun.COM 
6607836SJohn.Forte@Sun.COM 
6617836SJohn.Forte@Sun.COM /*
6627836SJohn.Forte@Sun.COM  * Must be called with sv_mutex held.
6637836SJohn.Forte@Sun.COM  */
6647836SJohn.Forte@Sun.COM 
6657836SJohn.Forte@Sun.COM static int
sv_get_state(const dev_t udev,sv_dev_t ** svpp)6667836SJohn.Forte@Sun.COM sv_get_state(const dev_t udev, sv_dev_t **svpp)
6677836SJohn.Forte@Sun.COM {
6687836SJohn.Forte@Sun.COM 	sv_dev_t **hb, **insert, *svp;
6697836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
6707836SJohn.Forte@Sun.COM 	minor_t umin;
6717836SJohn.Forte@Sun.COM 	int i;
6727836SJohn.Forte@Sun.COM 
6737836SJohn.Forte@Sun.COM 	/* Get major hash table */
6747836SJohn.Forte@Sun.COM 	if ((maj = sv_getmajor(udev)) == NULL)
6757836SJohn.Forte@Sun.COM 		return (NULL);
6767836SJohn.Forte@Sun.COM 
6777836SJohn.Forte@Sun.COM 	/* Determine which minor hash table */
6787836SJohn.Forte@Sun.COM 	umin = getminor(udev);
6797836SJohn.Forte@Sun.COM 	hb = &(maj->sm_hash[SV_MINOR_HASH(umin)]);
6807836SJohn.Forte@Sun.COM 
6817836SJohn.Forte@Sun.COM 	/* look for clash */
6827836SJohn.Forte@Sun.COM 
6837836SJohn.Forte@Sun.COM 	insert = hb;
6847836SJohn.Forte@Sun.COM 
6857836SJohn.Forte@Sun.COM 	for (svp = *hb; svp; svp = svp->sv_hash) {
6867836SJohn.Forte@Sun.COM 		if (svp->sv_dev == udev)
6877836SJohn.Forte@Sun.COM 			break;
6887836SJohn.Forte@Sun.COM 
6897836SJohn.Forte@Sun.COM 		if (svp->sv_hash == NULL)
6907836SJohn.Forte@Sun.COM 			insert = &svp->sv_hash;
6917836SJohn.Forte@Sun.COM 	}
6927836SJohn.Forte@Sun.COM 
6937836SJohn.Forte@Sun.COM 	if (svp) {
6947836SJohn.Forte@Sun.COM 		DTRACE_PROBE1(
6957836SJohn.Forte@Sun.COM 		    sv_get_state_enabled,
6967836SJohn.Forte@Sun.COM 		    dev_t, udev);
6977836SJohn.Forte@Sun.COM 		return (SV_EENABLED);
6987836SJohn.Forte@Sun.COM 	}
6997836SJohn.Forte@Sun.COM 
7007836SJohn.Forte@Sun.COM 	/* look for spare sv_devs slot */
7017836SJohn.Forte@Sun.COM 
7027836SJohn.Forte@Sun.COM 	for (i = 0; i < sv_max_devices; i++) {
7037836SJohn.Forte@Sun.COM 		svp = &sv_devs[i];
7047836SJohn.Forte@Sun.COM 
7057836SJohn.Forte@Sun.COM 		if (svp->sv_state == SV_DISABLE)
7067836SJohn.Forte@Sun.COM 			break;
7077836SJohn.Forte@Sun.COM 	}
7087836SJohn.Forte@Sun.COM 
7097836SJohn.Forte@Sun.COM 	if (i >= sv_max_devices) {
7107836SJohn.Forte@Sun.COM 		DTRACE_PROBE1(
7117836SJohn.Forte@Sun.COM 		    sv_get_state_noslots,
7127836SJohn.Forte@Sun.COM 		    dev_t, udev);
7137836SJohn.Forte@Sun.COM 		return (SV_ENOSLOTS);
7147836SJohn.Forte@Sun.COM 	}
7157836SJohn.Forte@Sun.COM 
7167836SJohn.Forte@Sun.COM 	svp->sv_state = SV_PENDING;
7177836SJohn.Forte@Sun.COM 	svp->sv_pending = curthread;
7187836SJohn.Forte@Sun.COM 
7197836SJohn.Forte@Sun.COM 	*insert = svp;
7207836SJohn.Forte@Sun.COM 	svp->sv_hash = NULL;
7217836SJohn.Forte@Sun.COM 	maj->sm_seq++;		/* must be after the store to the hash chain */
7227836SJohn.Forte@Sun.COM 
7237836SJohn.Forte@Sun.COM 	*svpp = svp;
7247836SJohn.Forte@Sun.COM 
7257836SJohn.Forte@Sun.COM 	/*
7267836SJohn.Forte@Sun.COM 	 * We do not know the size of the underlying device at
7277836SJohn.Forte@Sun.COM 	 * this stage, so initialise "nblocks" property to
7287836SJohn.Forte@Sun.COM 	 * zero, and update it whenever we succeed in
7297836SJohn.Forte@Sun.COM 	 * nsc_reserve'ing the underlying nsc_fd_t.
7307836SJohn.Forte@Sun.COM 	 */
7317836SJohn.Forte@Sun.COM 
7327836SJohn.Forte@Sun.COM 	svp->sv_nblocks = 0;
7337836SJohn.Forte@Sun.COM 
7347836SJohn.Forte@Sun.COM 	return (0);
7357836SJohn.Forte@Sun.COM }
7367836SJohn.Forte@Sun.COM 
7377836SJohn.Forte@Sun.COM 
7387836SJohn.Forte@Sun.COM /*
7397836SJohn.Forte@Sun.COM  * Remove a device structure from it's hash chain.
7407836SJohn.Forte@Sun.COM  * Must be called with sv_mutex held.
7417836SJohn.Forte@Sun.COM  */
7427836SJohn.Forte@Sun.COM 
7437836SJohn.Forte@Sun.COM static void
sv_rm_hash(sv_dev_t * svp)7447836SJohn.Forte@Sun.COM sv_rm_hash(sv_dev_t *svp)
7457836SJohn.Forte@Sun.COM {
7467836SJohn.Forte@Sun.COM 	sv_dev_t **svpp;
7477836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
7487836SJohn.Forte@Sun.COM 
7497836SJohn.Forte@Sun.COM 	/* Get major hash table */
7507836SJohn.Forte@Sun.COM 	if ((maj = sv_getmajor(svp->sv_dev)) == NULL)
7517836SJohn.Forte@Sun.COM 		return;
7527836SJohn.Forte@Sun.COM 
7537836SJohn.Forte@Sun.COM 	/* remove svp from hash chain */
7547836SJohn.Forte@Sun.COM 
7557836SJohn.Forte@Sun.COM 	svpp = &(maj->sm_hash[SV_MINOR_HASH(getminor(svp->sv_dev))]);
7567836SJohn.Forte@Sun.COM 	while (*svpp) {
7577836SJohn.Forte@Sun.COM 		if (*svpp == svp) {
7587836SJohn.Forte@Sun.COM 			/*
7597836SJohn.Forte@Sun.COM 			 * increment of sm_seq must be before the
7607836SJohn.Forte@Sun.COM 			 * removal from the hash chain
7617836SJohn.Forte@Sun.COM 			 */
7627836SJohn.Forte@Sun.COM 			maj->sm_seq++;
7637836SJohn.Forte@Sun.COM 			*svpp = svp->sv_hash;
7647836SJohn.Forte@Sun.COM 			break;
7657836SJohn.Forte@Sun.COM 		}
7667836SJohn.Forte@Sun.COM 
7677836SJohn.Forte@Sun.COM 		svpp = &(*svpp)->sv_hash;
7687836SJohn.Forte@Sun.COM 	}
7697836SJohn.Forte@Sun.COM 
7707836SJohn.Forte@Sun.COM 	svp->sv_hash = NULL;
7717836SJohn.Forte@Sun.COM }
7727836SJohn.Forte@Sun.COM 
7737836SJohn.Forte@Sun.COM /*
7747836SJohn.Forte@Sun.COM  * Free (disable) a device structure.
7757836SJohn.Forte@Sun.COM  * Must be called with sv_lock(RW_WRITER) and sv_mutex held, and will
7767836SJohn.Forte@Sun.COM  * perform the exits during its processing.
7777836SJohn.Forte@Sun.COM  */
7787836SJohn.Forte@Sun.COM 
7797836SJohn.Forte@Sun.COM static int
sv_free(sv_dev_t * svp,const int error)7807836SJohn.Forte@Sun.COM sv_free(sv_dev_t *svp, const int error)
7817836SJohn.Forte@Sun.COM {
7827836SJohn.Forte@Sun.COM 	struct cb_ops *cb_ops;
7837836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
7847836SJohn.Forte@Sun.COM 
7857836SJohn.Forte@Sun.COM 	/* Get major hash table */
7867836SJohn.Forte@Sun.COM 	if ((maj = sv_getmajor(svp->sv_dev)) == NULL)
7877836SJohn.Forte@Sun.COM 		return (NULL);
7887836SJohn.Forte@Sun.COM 
7897836SJohn.Forte@Sun.COM 	svp->sv_state = SV_PENDING;
7907836SJohn.Forte@Sun.COM 	svp->sv_pending = curthread;
7917836SJohn.Forte@Sun.COM 
7927836SJohn.Forte@Sun.COM 	/*
7937836SJohn.Forte@Sun.COM 	 * Close the fd's before removing from the hash or swapping
7947836SJohn.Forte@Sun.COM 	 * back the cb_ops pointers so that the cache flushes before new
7957836SJohn.Forte@Sun.COM 	 * io can come in.
7967836SJohn.Forte@Sun.COM 	 */
7977836SJohn.Forte@Sun.COM 
7987836SJohn.Forte@Sun.COM 	if (svp->sv_fd) {
7997836SJohn.Forte@Sun.COM 		(void) nsc_close(svp->sv_fd);
8007836SJohn.Forte@Sun.COM 		svp->sv_fd = 0;
8017836SJohn.Forte@Sun.COM 	}
8027836SJohn.Forte@Sun.COM 
8037836SJohn.Forte@Sun.COM 	sv_rm_hash(svp);
8047836SJohn.Forte@Sun.COM 
8057836SJohn.Forte@Sun.COM 	if (error != SV_ESDOPEN &&
8067836SJohn.Forte@Sun.COM 	    error != SV_ELYROPEN && --maj->sm_inuse == 0) {
8077836SJohn.Forte@Sun.COM 
8087836SJohn.Forte@Sun.COM 		if (maj->sm_dev_ops)
8097836SJohn.Forte@Sun.COM 			cb_ops = maj->sm_dev_ops->devo_cb_ops;
8107836SJohn.Forte@Sun.COM 		else
8117836SJohn.Forte@Sun.COM 			cb_ops = NULL;
8127836SJohn.Forte@Sun.COM 
8137836SJohn.Forte@Sun.COM 		if (cb_ops && maj->sm_strategy != NULL) {
8147836SJohn.Forte@Sun.COM 			cb_ops->cb_strategy = maj->sm_strategy;
8157836SJohn.Forte@Sun.COM 			cb_ops->cb_close = maj->sm_close;
8167836SJohn.Forte@Sun.COM 			cb_ops->cb_ioctl = maj->sm_ioctl;
8177836SJohn.Forte@Sun.COM 			cb_ops->cb_write = maj->sm_write;
8187836SJohn.Forte@Sun.COM 			cb_ops->cb_open = maj->sm_open;
8197836SJohn.Forte@Sun.COM 			cb_ops->cb_read = maj->sm_read;
8207836SJohn.Forte@Sun.COM 			cb_ops->cb_flag = maj->sm_flag;
8217836SJohn.Forte@Sun.COM 
8227836SJohn.Forte@Sun.COM 			if (maj->sm_awrite)
8237836SJohn.Forte@Sun.COM 				cb_ops->cb_awrite = maj->sm_awrite;
8247836SJohn.Forte@Sun.COM 
8257836SJohn.Forte@Sun.COM 			if (maj->sm_aread)
8267836SJohn.Forte@Sun.COM 				cb_ops->cb_aread = maj->sm_aread;
8277836SJohn.Forte@Sun.COM 
8287836SJohn.Forte@Sun.COM 			/*
8297836SJohn.Forte@Sun.COM 			 * corbin XXX
8307836SJohn.Forte@Sun.COM 			 * Leave backing device ops in maj->sm_*
8317836SJohn.Forte@Sun.COM 			 * to handle any requests that might come
8327836SJohn.Forte@Sun.COM 			 * in during the disable.  This could be
8337836SJohn.Forte@Sun.COM 			 * a problem however if the backing device
8347836SJohn.Forte@Sun.COM 			 * driver is changed while we process these
8357836SJohn.Forte@Sun.COM 			 * requests.
8367836SJohn.Forte@Sun.COM 			 *
8377836SJohn.Forte@Sun.COM 			 * maj->sm_strategy = 0;
8387836SJohn.Forte@Sun.COM 			 * maj->sm_awrite = 0;
8397836SJohn.Forte@Sun.COM 			 * maj->sm_write = 0;
8407836SJohn.Forte@Sun.COM 			 * maj->sm_ioctl = 0;
8417836SJohn.Forte@Sun.COM 			 * maj->sm_close = 0;
8427836SJohn.Forte@Sun.COM 			 * maj->sm_aread = 0;
8437836SJohn.Forte@Sun.COM 			 * maj->sm_read = 0;
8447836SJohn.Forte@Sun.COM 			 * maj->sm_open = 0;
8457836SJohn.Forte@Sun.COM 			 * maj->sm_flag = 0;
8467836SJohn.Forte@Sun.COM 			 *
8477836SJohn.Forte@Sun.COM 			 */
8487836SJohn.Forte@Sun.COM 		}
8497836SJohn.Forte@Sun.COM 
8507836SJohn.Forte@Sun.COM 		if (maj->sm_dev_ops) {
8517836SJohn.Forte@Sun.COM 			maj->sm_dev_ops = 0;
8527836SJohn.Forte@Sun.COM 		}
8537836SJohn.Forte@Sun.COM 	}
8547836SJohn.Forte@Sun.COM 
8557836SJohn.Forte@Sun.COM 	if (svp->sv_lh) {
8567836SJohn.Forte@Sun.COM 		cred_t *crp = ddi_get_cred();
8577836SJohn.Forte@Sun.COM 
8587836SJohn.Forte@Sun.COM 		/*
8597836SJohn.Forte@Sun.COM 		 * Close the protective layered driver open using the
8607836SJohn.Forte@Sun.COM 		 * Sun Private layered driver i/f.
8617836SJohn.Forte@Sun.COM 		 */
8627836SJohn.Forte@Sun.COM 
8637836SJohn.Forte@Sun.COM 		(void) ldi_close(svp->sv_lh, FREAD|FWRITE, crp);
8647836SJohn.Forte@Sun.COM 		svp->sv_lh = NULL;
8657836SJohn.Forte@Sun.COM 	}
8667836SJohn.Forte@Sun.COM 
8677836SJohn.Forte@Sun.COM 	svp->sv_timestamp = nsc_lbolt();
8687836SJohn.Forte@Sun.COM 	svp->sv_state = SV_DISABLE;
8697836SJohn.Forte@Sun.COM 	svp->sv_pending = NULL;
8707836SJohn.Forte@Sun.COM 	rw_exit(&svp->sv_lock);
8717836SJohn.Forte@Sun.COM 	mutex_exit(&sv_mutex);
8727836SJohn.Forte@Sun.COM 
8737836SJohn.Forte@Sun.COM 	return (error);
8747836SJohn.Forte@Sun.COM }
8757836SJohn.Forte@Sun.COM 
8767836SJohn.Forte@Sun.COM /*
8777836SJohn.Forte@Sun.COM  * Reserve the device, taking into account the possibility that
8787836SJohn.Forte@Sun.COM  * the reserve might have to be retried.
8797836SJohn.Forte@Sun.COM  */
8807836SJohn.Forte@Sun.COM static int
sv_reserve(nsc_fd_t * fd,int flags)8817836SJohn.Forte@Sun.COM sv_reserve(nsc_fd_t *fd, int flags)
8827836SJohn.Forte@Sun.COM {
8837836SJohn.Forte@Sun.COM 	int eintr_count;
8847836SJohn.Forte@Sun.COM 	int rc;
8857836SJohn.Forte@Sun.COM 
8867836SJohn.Forte@Sun.COM 	eintr_count = 0;
8877836SJohn.Forte@Sun.COM 	do {
8887836SJohn.Forte@Sun.COM 		rc = nsc_reserve(fd, flags);
8897836SJohn.Forte@Sun.COM 		if (rc == EINTR) {
8907836SJohn.Forte@Sun.COM 			++eintr_count;
8917836SJohn.Forte@Sun.COM 			delay(2);
8927836SJohn.Forte@Sun.COM 		}
8937836SJohn.Forte@Sun.COM 	} while ((rc == EINTR) && (eintr_count < MAX_EINTR_COUNT));
8947836SJohn.Forte@Sun.COM 
8957836SJohn.Forte@Sun.COM 	return (rc);
8967836SJohn.Forte@Sun.COM }
8977836SJohn.Forte@Sun.COM 
8987836SJohn.Forte@Sun.COM static int
sv_enable(const caddr_t path,const int flag,const dev_t udev,spcs_s_info_t kstatus)8997836SJohn.Forte@Sun.COM sv_enable(const caddr_t path, const int flag,
9007836SJohn.Forte@Sun.COM     const dev_t udev, spcs_s_info_t kstatus)
9017836SJohn.Forte@Sun.COM {
9027836SJohn.Forte@Sun.COM 	struct dev_ops *dev_ops;
9037836SJohn.Forte@Sun.COM 	struct cb_ops *cb_ops;
9047836SJohn.Forte@Sun.COM 	sv_dev_t *svp;
9057836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
9067836SJohn.Forte@Sun.COM 	nsc_size_t nblocks;
9077836SJohn.Forte@Sun.COM 	int rc;
9087836SJohn.Forte@Sun.COM 	cred_t *crp;
9097836SJohn.Forte@Sun.COM 	ldi_ident_t	li;
9107836SJohn.Forte@Sun.COM 
9117836SJohn.Forte@Sun.COM 	if (udev == (dev_t)-1 || udev == 0) {
9127836SJohn.Forte@Sun.COM 		DTRACE_PROBE1(
9137836SJohn.Forte@Sun.COM 		    sv_enable_err_baddev,
9147836SJohn.Forte@Sun.COM 		    dev_t, udev);
9157836SJohn.Forte@Sun.COM 		return (SV_EBADDEV);
9167836SJohn.Forte@Sun.COM 	}
9177836SJohn.Forte@Sun.COM 
9187836SJohn.Forte@Sun.COM 	if ((flag & ~(NSC_CACHE|NSC_DEVICE)) != 0) {
9197836SJohn.Forte@Sun.COM 		DTRACE_PROBE1(sv_enable_err_amode, dev_t, udev);
9207836SJohn.Forte@Sun.COM 		return (SV_EAMODE);
9217836SJohn.Forte@Sun.COM 	}
9227836SJohn.Forte@Sun.COM 
9237836SJohn.Forte@Sun.COM 	/* Get major hash table */
9247836SJohn.Forte@Sun.COM 	if ((maj = sv_getmajor(udev)) == NULL)
9257836SJohn.Forte@Sun.COM 		return (SV_EBADDEV);
9267836SJohn.Forte@Sun.COM 
9277836SJohn.Forte@Sun.COM 	mutex_enter(&sv_mutex);
9287836SJohn.Forte@Sun.COM 
9297836SJohn.Forte@Sun.COM 	rc = sv_get_state(udev, &svp);
9307836SJohn.Forte@Sun.COM 	if (rc) {
9317836SJohn.Forte@Sun.COM 		mutex_exit(&sv_mutex);
932*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_enable_err_state, dev_t, udev);
9337836SJohn.Forte@Sun.COM 		return (rc);
9347836SJohn.Forte@Sun.COM 	}
9357836SJohn.Forte@Sun.COM 
9367836SJohn.Forte@Sun.COM 	rw_enter(&svp->sv_lock, RW_WRITER);
9377836SJohn.Forte@Sun.COM 
9387836SJohn.Forte@Sun.COM 	/*
9397836SJohn.Forte@Sun.COM 	 * Get real fd used for io
9407836SJohn.Forte@Sun.COM 	 */
9417836SJohn.Forte@Sun.COM 
9427836SJohn.Forte@Sun.COM 	svp->sv_dev = udev;
9437836SJohn.Forte@Sun.COM 	svp->sv_flag = flag;
9447836SJohn.Forte@Sun.COM 
9457836SJohn.Forte@Sun.COM 	/*
9467836SJohn.Forte@Sun.COM 	 * OR in NSC_DEVICE to ensure that nskern grabs the real strategy
9477836SJohn.Forte@Sun.COM 	 * function pointer before sv swaps them out.
9487836SJohn.Forte@Sun.COM 	 */
9497836SJohn.Forte@Sun.COM 
9507836SJohn.Forte@Sun.COM 	svp->sv_fd = nsc_open(path, (svp->sv_flag | NSC_DEVICE),
951*9093SRamana.Srikanth@Sun.COM 	    sv_fd_def, (blind_t)udev, &rc);
9527836SJohn.Forte@Sun.COM 
9537836SJohn.Forte@Sun.COM 	if (svp->sv_fd == NULL) {
9547836SJohn.Forte@Sun.COM 		if (kstatus)
9557836SJohn.Forte@Sun.COM 			spcs_s_add(kstatus, rc);
956*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_enable_err_fd, dev_t, udev);
9577836SJohn.Forte@Sun.COM 		return (sv_free(svp, SV_ESDOPEN));
9587836SJohn.Forte@Sun.COM 	}
9597836SJohn.Forte@Sun.COM 
9607836SJohn.Forte@Sun.COM 	/*
9617836SJohn.Forte@Sun.COM 	 * Perform a layered driver open using the Sun Private layered
9627836SJohn.Forte@Sun.COM 	 * driver i/f to ensure that the cb_ops structure for the driver
9637836SJohn.Forte@Sun.COM 	 * is not detached out from under us whilst sv is enabled.
9647836SJohn.Forte@Sun.COM 	 *
9657836SJohn.Forte@Sun.COM 	 */
9667836SJohn.Forte@Sun.COM 
9677836SJohn.Forte@Sun.COM 	crp = ddi_get_cred();
9687836SJohn.Forte@Sun.COM 	svp->sv_lh = NULL;
9697836SJohn.Forte@Sun.COM 
9707836SJohn.Forte@Sun.COM 	if ((rc = ldi_ident_from_dev(svp->sv_dev, &li)) == 0) {
9717836SJohn.Forte@Sun.COM 		rc = ldi_open_by_dev(&svp->sv_dev,
9727836SJohn.Forte@Sun.COM 		    OTYP_BLK, FREAD|FWRITE, crp, &svp->sv_lh, li);
9737836SJohn.Forte@Sun.COM 	}
9747836SJohn.Forte@Sun.COM 
9757836SJohn.Forte@Sun.COM 	if (rc != 0) {
9767836SJohn.Forte@Sun.COM 		if (kstatus)
9777836SJohn.Forte@Sun.COM 			spcs_s_add(kstatus, rc);
978*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_enable_err_lyr_open, dev_t, udev);
9797836SJohn.Forte@Sun.COM 		return (sv_free(svp, SV_ELYROPEN));
9807836SJohn.Forte@Sun.COM 	}
9817836SJohn.Forte@Sun.COM 
9827836SJohn.Forte@Sun.COM 	/*
9837836SJohn.Forte@Sun.COM 	 * Do layering if required - must happen after nsc_open().
9847836SJohn.Forte@Sun.COM 	 */
9857836SJohn.Forte@Sun.COM 
9867836SJohn.Forte@Sun.COM 	if (maj->sm_inuse++ == 0) {
9877836SJohn.Forte@Sun.COM 		maj->sm_dev_ops = nsc_get_devops(getmajor(udev));
9887836SJohn.Forte@Sun.COM 
9897836SJohn.Forte@Sun.COM 		if (maj->sm_dev_ops == NULL ||
990*9093SRamana.Srikanth@Sun.COM 		    maj->sm_dev_ops->devo_cb_ops == NULL) {
991*9093SRamana.Srikanth@Sun.COM 			DTRACE_PROBE1(sv_enable_err_load, dev_t, udev);
9927836SJohn.Forte@Sun.COM 			return (sv_free(svp, SV_ELOAD));
9937836SJohn.Forte@Sun.COM 		}
9947836SJohn.Forte@Sun.COM 
9957836SJohn.Forte@Sun.COM 		dev_ops = maj->sm_dev_ops;
9967836SJohn.Forte@Sun.COM 		cb_ops = dev_ops->devo_cb_ops;
9977836SJohn.Forte@Sun.COM 
9987836SJohn.Forte@Sun.COM 		if (cb_ops->cb_strategy == NULL ||
9997836SJohn.Forte@Sun.COM 		    cb_ops->cb_strategy == nodev ||
10007836SJohn.Forte@Sun.COM 		    cb_ops->cb_strategy == nulldev) {
1001*9093SRamana.Srikanth@Sun.COM 			DTRACE_PROBE1(sv_enable_err_nostrategy, dev_t, udev);
10027836SJohn.Forte@Sun.COM 			return (sv_free(svp, SV_ELOAD));
10037836SJohn.Forte@Sun.COM 		}
10047836SJohn.Forte@Sun.COM 
10057836SJohn.Forte@Sun.COM 		if (cb_ops->cb_strategy == sv_lyr_strategy) {
1006*9093SRamana.Srikanth@Sun.COM 			DTRACE_PROBE1(sv_enable_err_svstrategy, dev_t, udev);
10077836SJohn.Forte@Sun.COM 			return (sv_free(svp, SV_ESTRATEGY));
10087836SJohn.Forte@Sun.COM 		}
10097836SJohn.Forte@Sun.COM 
10107836SJohn.Forte@Sun.COM 		maj->sm_strategy = cb_ops->cb_strategy;
10117836SJohn.Forte@Sun.COM 		maj->sm_close = cb_ops->cb_close;
10127836SJohn.Forte@Sun.COM 		maj->sm_ioctl = cb_ops->cb_ioctl;
10137836SJohn.Forte@Sun.COM 		maj->sm_write = cb_ops->cb_write;
10147836SJohn.Forte@Sun.COM 		maj->sm_open = cb_ops->cb_open;
10157836SJohn.Forte@Sun.COM 		maj->sm_read = cb_ops->cb_read;
10167836SJohn.Forte@Sun.COM 		maj->sm_flag = cb_ops->cb_flag;
10177836SJohn.Forte@Sun.COM 
10187836SJohn.Forte@Sun.COM 		cb_ops->cb_flag = cb_ops->cb_flag | D_MP;
10197836SJohn.Forte@Sun.COM 		cb_ops->cb_strategy = sv_lyr_strategy;
10207836SJohn.Forte@Sun.COM 		cb_ops->cb_close = sv_lyr_close;
10217836SJohn.Forte@Sun.COM 		cb_ops->cb_ioctl = sv_lyr_ioctl;
10227836SJohn.Forte@Sun.COM 		cb_ops->cb_write = sv_lyr_write;
10237836SJohn.Forte@Sun.COM 		cb_ops->cb_open = sv_lyr_open;
10247836SJohn.Forte@Sun.COM 		cb_ops->cb_read = sv_lyr_read;
10257836SJohn.Forte@Sun.COM 
10267836SJohn.Forte@Sun.COM 		/*
10277836SJohn.Forte@Sun.COM 		 * Check that the driver has async I/O entry points
10287836SJohn.Forte@Sun.COM 		 * before changing them.
10297836SJohn.Forte@Sun.COM 		 */
10307836SJohn.Forte@Sun.COM 
10317836SJohn.Forte@Sun.COM 		if (dev_ops->devo_rev < 3 || cb_ops->cb_rev < 1) {
10327836SJohn.Forte@Sun.COM 			maj->sm_awrite = 0;
10337836SJohn.Forte@Sun.COM 			maj->sm_aread = 0;
10347836SJohn.Forte@Sun.COM 		} else {
10357836SJohn.Forte@Sun.COM 			maj->sm_awrite = cb_ops->cb_awrite;
10367836SJohn.Forte@Sun.COM 			maj->sm_aread = cb_ops->cb_aread;
10377836SJohn.Forte@Sun.COM 
10387836SJohn.Forte@Sun.COM 			cb_ops->cb_awrite = sv_lyr_awrite;
10397836SJohn.Forte@Sun.COM 			cb_ops->cb_aread = sv_lyr_aread;
10407836SJohn.Forte@Sun.COM 		}
10417836SJohn.Forte@Sun.COM 
10427836SJohn.Forte@Sun.COM 		/*
10437836SJohn.Forte@Sun.COM 		 * Bug 4645743
10447836SJohn.Forte@Sun.COM 		 *
10457836SJohn.Forte@Sun.COM 		 * Prevent sv from ever unloading after it has interposed
10467836SJohn.Forte@Sun.COM 		 * on a major device because there is a race between
10477836SJohn.Forte@Sun.COM 		 * sv removing its layered entry points from the target
10487836SJohn.Forte@Sun.COM 		 * dev_ops, a client coming in and accessing the driver,
10497836SJohn.Forte@Sun.COM 		 * and the kernel modunloading the sv text.
10507836SJohn.Forte@Sun.COM 		 *
10517836SJohn.Forte@Sun.COM 		 * To allow unload, do svboot -u, which only happens in
10527836SJohn.Forte@Sun.COM 		 * pkgrm time.
10537836SJohn.Forte@Sun.COM 		 */
10547836SJohn.Forte@Sun.COM 		ASSERT(MUTEX_HELD(&sv_mutex));
10557836SJohn.Forte@Sun.COM 		sv_mod_status = SV_PREVENT_UNLOAD;
10567836SJohn.Forte@Sun.COM 	}
10577836SJohn.Forte@Sun.COM 
10587836SJohn.Forte@Sun.COM 
10597836SJohn.Forte@Sun.COM 	svp->sv_timestamp = nsc_lbolt();
10607836SJohn.Forte@Sun.COM 	svp->sv_state = SV_ENABLE;
10617836SJohn.Forte@Sun.COM 	svp->sv_pending = NULL;
10627836SJohn.Forte@Sun.COM 	rw_exit(&svp->sv_lock);
10637836SJohn.Forte@Sun.COM 
10647836SJohn.Forte@Sun.COM 	sv_ndevices++;
10657836SJohn.Forte@Sun.COM 	mutex_exit(&sv_mutex);
10667836SJohn.Forte@Sun.COM 
10677836SJohn.Forte@Sun.COM 	nblocks = 0;
10687836SJohn.Forte@Sun.COM 	if (sv_reserve(svp->sv_fd, NSC_READ|NSC_MULTI|NSC_PCATCH) == 0) {
10697836SJohn.Forte@Sun.COM 		nblocks = svp->sv_nblocks;
10707836SJohn.Forte@Sun.COM 		nsc_release(svp->sv_fd);
10717836SJohn.Forte@Sun.COM 	}
10727836SJohn.Forte@Sun.COM 
10737836SJohn.Forte@Sun.COM 	cmn_err(CE_CONT, "!sv: rdev 0x%lx, nblocks %" NSC_SZFMT "\n",
10747836SJohn.Forte@Sun.COM 	    svp->sv_dev, nblocks);
10757836SJohn.Forte@Sun.COM 
10767836SJohn.Forte@Sun.COM 	return (0);
10777836SJohn.Forte@Sun.COM }
10787836SJohn.Forte@Sun.COM 
10797836SJohn.Forte@Sun.COM 
10807836SJohn.Forte@Sun.COM static int
sv_prepare_unload()10817836SJohn.Forte@Sun.COM sv_prepare_unload()
10827836SJohn.Forte@Sun.COM {
10837836SJohn.Forte@Sun.COM 	int rc = 0;
10847836SJohn.Forte@Sun.COM 
10857836SJohn.Forte@Sun.COM 	mutex_enter(&sv_mutex);
10867836SJohn.Forte@Sun.COM 
10877836SJohn.Forte@Sun.COM 	if (sv_mod_status == SV_PREVENT_UNLOAD) {
10887836SJohn.Forte@Sun.COM 		if ((sv_ndevices != 0) || (sv_tset != NULL)) {
10897836SJohn.Forte@Sun.COM 			rc = EBUSY;
10907836SJohn.Forte@Sun.COM 		} else {
10917836SJohn.Forte@Sun.COM 			sv_mod_status = SV_ALLOW_UNLOAD;
10927836SJohn.Forte@Sun.COM 			delay(SV_WAIT_UNLOAD * drv_usectohz(1000000));
10937836SJohn.Forte@Sun.COM 		}
10947836SJohn.Forte@Sun.COM 	}
10957836SJohn.Forte@Sun.COM 
10967836SJohn.Forte@Sun.COM 	mutex_exit(&sv_mutex);
10977836SJohn.Forte@Sun.COM 	return (rc);
10987836SJohn.Forte@Sun.COM }
10997836SJohn.Forte@Sun.COM 
11007836SJohn.Forte@Sun.COM static int
svattach_fd(blind_t arg)11017836SJohn.Forte@Sun.COM svattach_fd(blind_t arg)
11027836SJohn.Forte@Sun.COM {
11037836SJohn.Forte@Sun.COM 	dev_t dev = (dev_t)arg;
11047836SJohn.Forte@Sun.COM 	sv_dev_t *svp = sv_dev_to_sv(dev, NULL);
11057836SJohn.Forte@Sun.COM 	int rc;
11067836SJohn.Forte@Sun.COM 
11077836SJohn.Forte@Sun.COM 	if (sv_debug > 0)
1108*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_CONT, "!svattach_fd(%p, %p)\n", arg, (void *)svp);
11097836SJohn.Forte@Sun.COM 
11107836SJohn.Forte@Sun.COM 	if (svp == NULL) {
11117836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN, "!svattach_fd: no state (arg %p)", arg);
11127836SJohn.Forte@Sun.COM 		return (0);
11137836SJohn.Forte@Sun.COM 	}
11147836SJohn.Forte@Sun.COM 
11157836SJohn.Forte@Sun.COM 	if ((rc = nsc_partsize(svp->sv_fd, &svp->sv_nblocks)) != 0) {
11167836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN,
11177836SJohn.Forte@Sun.COM 		    "!svattach_fd: nsc_partsize() failed, rc %d", rc);
11187836SJohn.Forte@Sun.COM 		svp->sv_nblocks = 0;
11197836SJohn.Forte@Sun.COM 	}
11207836SJohn.Forte@Sun.COM 
11217836SJohn.Forte@Sun.COM 	if ((rc = nsc_maxfbas(svp->sv_fd, 0, &svp->sv_maxfbas)) != 0) {
11227836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN,
11237836SJohn.Forte@Sun.COM 		    "!svattach_fd: nsc_maxfbas() failed, rc %d", rc);
11247836SJohn.Forte@Sun.COM 		svp->sv_maxfbas = 0;
11257836SJohn.Forte@Sun.COM 	}
11267836SJohn.Forte@Sun.COM 
11277836SJohn.Forte@Sun.COM 	if (sv_debug > 0) {
11287836SJohn.Forte@Sun.COM 		cmn_err(CE_CONT,
1129*9093SRamana.Srikanth@Sun.COM 		    "!svattach_fd(%p): size %" NSC_SZFMT ", "
11307836SJohn.Forte@Sun.COM 		    "maxfbas %" NSC_SZFMT "\n",
11317836SJohn.Forte@Sun.COM 		    arg, svp->sv_nblocks, svp->sv_maxfbas);
11327836SJohn.Forte@Sun.COM 	}
11337836SJohn.Forte@Sun.COM 
11347836SJohn.Forte@Sun.COM 	return (0);
11357836SJohn.Forte@Sun.COM }
11367836SJohn.Forte@Sun.COM 
11377836SJohn.Forte@Sun.COM 
11387836SJohn.Forte@Sun.COM static int
svdetach_fd(blind_t arg)11397836SJohn.Forte@Sun.COM svdetach_fd(blind_t arg)
11407836SJohn.Forte@Sun.COM {
11417836SJohn.Forte@Sun.COM 	dev_t dev = (dev_t)arg;
11427836SJohn.Forte@Sun.COM 	sv_dev_t *svp = sv_dev_to_sv(dev, NULL);
11437836SJohn.Forte@Sun.COM 
11447836SJohn.Forte@Sun.COM 	if (sv_debug > 0)
1145*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_CONT, "!svdetach_fd(%p, %p)\n", arg, (void *)svp);
11467836SJohn.Forte@Sun.COM 
11477836SJohn.Forte@Sun.COM 	/* svp can be NULL during disable of an sv */
11487836SJohn.Forte@Sun.COM 	if (svp == NULL)
11497836SJohn.Forte@Sun.COM 		return (0);
11507836SJohn.Forte@Sun.COM 
11517836SJohn.Forte@Sun.COM 	svp->sv_maxfbas = 0;
11527836SJohn.Forte@Sun.COM 	svp->sv_nblocks = 0;
11537836SJohn.Forte@Sun.COM 	return (0);
11547836SJohn.Forte@Sun.COM }
11557836SJohn.Forte@Sun.COM 
11567836SJohn.Forte@Sun.COM 
11577836SJohn.Forte@Sun.COM /*
11587836SJohn.Forte@Sun.COM  * Side effect: if called with (guard != 0), then expects both sv_mutex
11597836SJohn.Forte@Sun.COM  * and sv_lock(RW_WRITER) to be held, and will release them before returning.
11607836SJohn.Forte@Sun.COM  */
11617836SJohn.Forte@Sun.COM 
11627836SJohn.Forte@Sun.COM /* ARGSUSED */
11637836SJohn.Forte@Sun.COM static int
sv_disable(dev_t dev,spcs_s_info_t kstatus)11647836SJohn.Forte@Sun.COM sv_disable(dev_t dev, spcs_s_info_t kstatus)
11657836SJohn.Forte@Sun.COM {
11667836SJohn.Forte@Sun.COM 	sv_dev_t *svp = sv_dev_to_sv(dev, NULL);
11677836SJohn.Forte@Sun.COM 
11687836SJohn.Forte@Sun.COM 	if (svp == NULL) {
11697836SJohn.Forte@Sun.COM 
11707836SJohn.Forte@Sun.COM 		DTRACE_PROBE1(sv_disable_err_nodev, sv_dev_t *, svp);
11717836SJohn.Forte@Sun.COM 		return (SV_ENODEV);
11727836SJohn.Forte@Sun.COM 	}
11737836SJohn.Forte@Sun.COM 
11747836SJohn.Forte@Sun.COM 	mutex_enter(&sv_mutex);
11757836SJohn.Forte@Sun.COM 	rw_enter(&svp->sv_lock, RW_WRITER);
11767836SJohn.Forte@Sun.COM 
11777836SJohn.Forte@Sun.COM 	if (svp->sv_fd == NULL || svp->sv_state != SV_ENABLE) {
11787836SJohn.Forte@Sun.COM 		rw_exit(&svp->sv_lock);
11797836SJohn.Forte@Sun.COM 		mutex_exit(&sv_mutex);
11807836SJohn.Forte@Sun.COM 
11817836SJohn.Forte@Sun.COM 		DTRACE_PROBE1(sv_disable_err_disabled, sv_dev_t *, svp);
11827836SJohn.Forte@Sun.COM 		return (SV_EDISABLED);
11837836SJohn.Forte@Sun.COM 	}
11847836SJohn.Forte@Sun.COM 
11857836SJohn.Forte@Sun.COM 
11867836SJohn.Forte@Sun.COM 	sv_ndevices--;
11877836SJohn.Forte@Sun.COM 	return (sv_free(svp, 0));
11887836SJohn.Forte@Sun.COM }
11897836SJohn.Forte@Sun.COM 
11907836SJohn.Forte@Sun.COM 
11917836SJohn.Forte@Sun.COM 
11927836SJohn.Forte@Sun.COM static int
sv_lyr_open(dev_t * devp,int flag,int otyp,cred_t * crp)11937836SJohn.Forte@Sun.COM sv_lyr_open(dev_t *devp, int flag, int otyp, cred_t *crp)
11947836SJohn.Forte@Sun.COM {
11957836SJohn.Forte@Sun.COM 	nsc_buf_t *tmph;
11967836SJohn.Forte@Sun.COM 	sv_dev_t *svp;
11977836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
11987836SJohn.Forte@Sun.COM 	int (*fn)();
11997836SJohn.Forte@Sun.COM 	dev_t odev;
12007836SJohn.Forte@Sun.COM 	int ret;
12017836SJohn.Forte@Sun.COM 	int rc;
12027836SJohn.Forte@Sun.COM 
12037836SJohn.Forte@Sun.COM 	svp = sv_dev_to_sv(*devp, &maj);
12047836SJohn.Forte@Sun.COM 
12057836SJohn.Forte@Sun.COM 	if (svp) {
12067836SJohn.Forte@Sun.COM 		if (svp->sv_state == SV_PENDING &&
12077836SJohn.Forte@Sun.COM 		    svp->sv_pending == curthread) {
12087836SJohn.Forte@Sun.COM 			/*
12097836SJohn.Forte@Sun.COM 			 * This is a recursive open from a call to
12107836SJohn.Forte@Sun.COM 			 * ddi_lyr_open_by_devt and so we just want
12117836SJohn.Forte@Sun.COM 			 * to pass it straight through to the
12127836SJohn.Forte@Sun.COM 			 * underlying driver.
12137836SJohn.Forte@Sun.COM 			 */
12147836SJohn.Forte@Sun.COM 			DTRACE_PROBE2(sv_lyr_open_recursive,
12157836SJohn.Forte@Sun.COM 			    sv_dev_t *, svp,
12167836SJohn.Forte@Sun.COM 			    dev_t, *devp);
12177836SJohn.Forte@Sun.COM 			svp = NULL;
12187836SJohn.Forte@Sun.COM 		} else
12197836SJohn.Forte@Sun.COM 			rw_enter(&svp->sv_lock, RW_READER);
12207836SJohn.Forte@Sun.COM 	}
12217836SJohn.Forte@Sun.COM 
12227836SJohn.Forte@Sun.COM 	odev = *devp;
12237836SJohn.Forte@Sun.COM 
12247836SJohn.Forte@Sun.COM 	if (maj && (fn = maj->sm_open) != 0) {
12257836SJohn.Forte@Sun.COM 		if (!(maj->sm_flag & D_MP)) {
12267836SJohn.Forte@Sun.COM 			UNSAFE_ENTER();
12277836SJohn.Forte@Sun.COM 			ret = (*fn)(devp, flag, otyp, crp);
12287836SJohn.Forte@Sun.COM 			UNSAFE_EXIT();
12297836SJohn.Forte@Sun.COM 		} else {
12307836SJohn.Forte@Sun.COM 			ret = (*fn)(devp, flag, otyp, crp);
12317836SJohn.Forte@Sun.COM 		}
12327836SJohn.Forte@Sun.COM 
12337836SJohn.Forte@Sun.COM 		if (ret == 0) {
12347836SJohn.Forte@Sun.COM 			/*
12357836SJohn.Forte@Sun.COM 			 * Re-acquire svp if the driver changed *devp.
12367836SJohn.Forte@Sun.COM 			 */
12377836SJohn.Forte@Sun.COM 
12387836SJohn.Forte@Sun.COM 			if (*devp != odev) {
12397836SJohn.Forte@Sun.COM 				rw_exit(&svp->sv_lock);
12407836SJohn.Forte@Sun.COM 
12417836SJohn.Forte@Sun.COM 				svp = sv_dev_to_sv(*devp, NULL);
12427836SJohn.Forte@Sun.COM 
12437836SJohn.Forte@Sun.COM 				if (svp) {
12447836SJohn.Forte@Sun.COM 					rw_enter(&svp->sv_lock, RW_READER);
12457836SJohn.Forte@Sun.COM 				}
12467836SJohn.Forte@Sun.COM 			}
12477836SJohn.Forte@Sun.COM 		}
12487836SJohn.Forte@Sun.COM 	} else {
12497836SJohn.Forte@Sun.COM 		ret = ENODEV;
12507836SJohn.Forte@Sun.COM 	}
12517836SJohn.Forte@Sun.COM 
12527836SJohn.Forte@Sun.COM 	if (svp && ret != 0 && svp->sv_state == SV_ENABLE) {
12537836SJohn.Forte@Sun.COM 		/*
12547836SJohn.Forte@Sun.COM 		 * Underlying DDI open failed, but we have this
12557836SJohn.Forte@Sun.COM 		 * device SV enabled.  If we can read some data
12567836SJohn.Forte@Sun.COM 		 * from the device, fake a successful open (this
12577836SJohn.Forte@Sun.COM 		 * probably means that this device is RDC'd and we
12587836SJohn.Forte@Sun.COM 		 * are getting the data from the secondary node).
12597836SJohn.Forte@Sun.COM 		 *
12607836SJohn.Forte@Sun.COM 		 * The reserve must be done with NSC_TRY|NSC_NOWAIT to
12617836SJohn.Forte@Sun.COM 		 * ensure that it does not deadlock if this open is
12627836SJohn.Forte@Sun.COM 		 * coming from nskernd:get_bsize().
12637836SJohn.Forte@Sun.COM 		 */
12647836SJohn.Forte@Sun.COM 		rc = sv_reserve(svp->sv_fd,
1265*9093SRamana.Srikanth@Sun.COM 		    NSC_TRY | NSC_NOWAIT | NSC_MULTI | NSC_PCATCH);
12667836SJohn.Forte@Sun.COM 		if (rc == 0) {
12677836SJohn.Forte@Sun.COM 			tmph = NULL;
12687836SJohn.Forte@Sun.COM 
12697836SJohn.Forte@Sun.COM 			rc = nsc_alloc_buf(svp->sv_fd, 0, 1, NSC_READ, &tmph);
12707836SJohn.Forte@Sun.COM 			if (rc <= 0) {
12717836SJohn.Forte@Sun.COM 				/* success */
12727836SJohn.Forte@Sun.COM 				ret = 0;
12737836SJohn.Forte@Sun.COM 			}
12747836SJohn.Forte@Sun.COM 
12757836SJohn.Forte@Sun.COM 			if (tmph) {
12767836SJohn.Forte@Sun.COM 				(void) nsc_free_buf(tmph);
12777836SJohn.Forte@Sun.COM 				tmph = NULL;
12787836SJohn.Forte@Sun.COM 			}
12797836SJohn.Forte@Sun.COM 
12807836SJohn.Forte@Sun.COM 			nsc_release(svp->sv_fd);
12817836SJohn.Forte@Sun.COM 
12827836SJohn.Forte@Sun.COM 			/*
12837836SJohn.Forte@Sun.COM 			 * Count the number of layered opens that we
12847836SJohn.Forte@Sun.COM 			 * fake since we have to fake a matching number
12857836SJohn.Forte@Sun.COM 			 * of closes (OTYP_LYR open/close calls must be
12867836SJohn.Forte@Sun.COM 			 * paired).
12877836SJohn.Forte@Sun.COM 			 */
12887836SJohn.Forte@Sun.COM 
12897836SJohn.Forte@Sun.COM 			if (ret == 0 && otyp == OTYP_LYR) {
12907836SJohn.Forte@Sun.COM 				mutex_enter(&svp->sv_olock);
12917836SJohn.Forte@Sun.COM 				svp->sv_openlcnt++;
12927836SJohn.Forte@Sun.COM 				mutex_exit(&svp->sv_olock);
12937836SJohn.Forte@Sun.COM 			}
12947836SJohn.Forte@Sun.COM 		}
12957836SJohn.Forte@Sun.COM 	}
12967836SJohn.Forte@Sun.COM 
12977836SJohn.Forte@Sun.COM 	if (svp) {
12987836SJohn.Forte@Sun.COM 		rw_exit(&svp->sv_lock);
12997836SJohn.Forte@Sun.COM 	}
13007836SJohn.Forte@Sun.COM 
13017836SJohn.Forte@Sun.COM 	return (ret);
13027836SJohn.Forte@Sun.COM }
13037836SJohn.Forte@Sun.COM 
13047836SJohn.Forte@Sun.COM 
13057836SJohn.Forte@Sun.COM static int
sv_lyr_close(dev_t dev,int flag,int otyp,cred_t * crp)13067836SJohn.Forte@Sun.COM sv_lyr_close(dev_t dev, int flag, int otyp, cred_t *crp)
13077836SJohn.Forte@Sun.COM {
13087836SJohn.Forte@Sun.COM 	sv_dev_t *svp;
13097836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
13107836SJohn.Forte@Sun.COM 	int (*fn)();
13117836SJohn.Forte@Sun.COM 	int ret;
13127836SJohn.Forte@Sun.COM 
13137836SJohn.Forte@Sun.COM 	svp = sv_dev_to_sv(dev, &maj);
13147836SJohn.Forte@Sun.COM 
13157836SJohn.Forte@Sun.COM 	if (svp &&
13167836SJohn.Forte@Sun.COM 	    svp->sv_state == SV_PENDING &&
13177836SJohn.Forte@Sun.COM 	    svp->sv_pending == curthread) {
13187836SJohn.Forte@Sun.COM 		/*
13197836SJohn.Forte@Sun.COM 		 * This is a recursive open from a call to
13207836SJohn.Forte@Sun.COM 		 * ddi_lyr_close and so we just want
13217836SJohn.Forte@Sun.COM 		 * to pass it straight through to the
13227836SJohn.Forte@Sun.COM 		 * underlying driver.
13237836SJohn.Forte@Sun.COM 		 */
1324*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE2(sv_lyr_close_recursive, sv_dev_t *, svp,
1325*9093SRamana.Srikanth@Sun.COM 		    dev_t, dev);
13267836SJohn.Forte@Sun.COM 		svp = NULL;
13277836SJohn.Forte@Sun.COM 	}
13287836SJohn.Forte@Sun.COM 
13297836SJohn.Forte@Sun.COM 	if (svp) {
13307836SJohn.Forte@Sun.COM 		rw_enter(&svp->sv_lock, RW_READER);
13317836SJohn.Forte@Sun.COM 
13327836SJohn.Forte@Sun.COM 		if (otyp == OTYP_LYR) {
13337836SJohn.Forte@Sun.COM 			mutex_enter(&svp->sv_olock);
13347836SJohn.Forte@Sun.COM 
13357836SJohn.Forte@Sun.COM 			if (svp->sv_openlcnt) {
13367836SJohn.Forte@Sun.COM 				/*
13377836SJohn.Forte@Sun.COM 				 * Consume sufficient layered closes to
13387836SJohn.Forte@Sun.COM 				 * account for the opens that we faked
13397836SJohn.Forte@Sun.COM 				 * whilst the device was failed.
13407836SJohn.Forte@Sun.COM 				 */
13417836SJohn.Forte@Sun.COM 				svp->sv_openlcnt--;
13427836SJohn.Forte@Sun.COM 				mutex_exit(&svp->sv_olock);
13437836SJohn.Forte@Sun.COM 				rw_exit(&svp->sv_lock);
13447836SJohn.Forte@Sun.COM 
1345*9093SRamana.Srikanth@Sun.COM 				DTRACE_PROBE1(sv_lyr_close_end, dev_t, dev);
13467836SJohn.Forte@Sun.COM 
13477836SJohn.Forte@Sun.COM 				return (0);
13487836SJohn.Forte@Sun.COM 			}
13497836SJohn.Forte@Sun.COM 
13507836SJohn.Forte@Sun.COM 			mutex_exit(&svp->sv_olock);
13517836SJohn.Forte@Sun.COM 		}
13527836SJohn.Forte@Sun.COM 	}
13537836SJohn.Forte@Sun.COM 
13547836SJohn.Forte@Sun.COM 	if (maj && (fn = maj->sm_close) != 0) {
13557836SJohn.Forte@Sun.COM 		if (!(maj->sm_flag & D_MP)) {
13567836SJohn.Forte@Sun.COM 			UNSAFE_ENTER();
13577836SJohn.Forte@Sun.COM 			ret = (*fn)(dev, flag, otyp, crp);
13587836SJohn.Forte@Sun.COM 			UNSAFE_EXIT();
13597836SJohn.Forte@Sun.COM 		} else {
13607836SJohn.Forte@Sun.COM 			ret = (*fn)(dev, flag, otyp, crp);
13617836SJohn.Forte@Sun.COM 		}
13627836SJohn.Forte@Sun.COM 	} else {
13637836SJohn.Forte@Sun.COM 		ret = ENODEV;
13647836SJohn.Forte@Sun.COM 	}
13657836SJohn.Forte@Sun.COM 
13667836SJohn.Forte@Sun.COM 	if (svp) {
13677836SJohn.Forte@Sun.COM 		rw_exit(&svp->sv_lock);
13687836SJohn.Forte@Sun.COM 	}
13697836SJohn.Forte@Sun.COM 
13707836SJohn.Forte@Sun.COM 	return (ret);
13717836SJohn.Forte@Sun.COM }
13727836SJohn.Forte@Sun.COM 
13737836SJohn.Forte@Sun.COM 
13747836SJohn.Forte@Sun.COM /*
13757836SJohn.Forte@Sun.COM  * Convert the specified dev_t into a locked and enabled sv_dev_t, or
13767836SJohn.Forte@Sun.COM  * return NULL.
13777836SJohn.Forte@Sun.COM  */
13787836SJohn.Forte@Sun.COM static sv_dev_t *
sv_find_enabled(const dev_t dev,sv_maj_t ** majpp)13797836SJohn.Forte@Sun.COM sv_find_enabled(const dev_t dev, sv_maj_t **majpp)
13807836SJohn.Forte@Sun.COM {
13817836SJohn.Forte@Sun.COM 	sv_dev_t *svp;
13827836SJohn.Forte@Sun.COM 
13837836SJohn.Forte@Sun.COM 	while ((svp = sv_dev_to_sv(dev, majpp)) != NULL) {
13847836SJohn.Forte@Sun.COM 		rw_enter(&svp->sv_lock, RW_READER);
13857836SJohn.Forte@Sun.COM 
13867836SJohn.Forte@Sun.COM 		if (svp->sv_state == SV_ENABLE) {
13877836SJohn.Forte@Sun.COM 			/* locked and enabled */
13887836SJohn.Forte@Sun.COM 			break;
13897836SJohn.Forte@Sun.COM 		}
13907836SJohn.Forte@Sun.COM 
13917836SJohn.Forte@Sun.COM 		/*
13927836SJohn.Forte@Sun.COM 		 * State was changed while waiting on the lock.
13937836SJohn.Forte@Sun.COM 		 * Wait for a stable state.
13947836SJohn.Forte@Sun.COM 		 */
13957836SJohn.Forte@Sun.COM 		rw_exit(&svp->sv_lock);
13967836SJohn.Forte@Sun.COM 
1397*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_find_enabled_retry, dev_t, dev);
13987836SJohn.Forte@Sun.COM 
13997836SJohn.Forte@Sun.COM 		delay(2);
14007836SJohn.Forte@Sun.COM 	}
14017836SJohn.Forte@Sun.COM 
14027836SJohn.Forte@Sun.COM 	return (svp);
14037836SJohn.Forte@Sun.COM }
14047836SJohn.Forte@Sun.COM 
14057836SJohn.Forte@Sun.COM 
14067836SJohn.Forte@Sun.COM static int
sv_lyr_uio(dev_t dev,uio_t * uiop,cred_t * crp,int rw)14077836SJohn.Forte@Sun.COM sv_lyr_uio(dev_t dev, uio_t *uiop, cred_t *crp, int rw)
14087836SJohn.Forte@Sun.COM {
14097836SJohn.Forte@Sun.COM 	sv_dev_t *svp;
14107836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
14117836SJohn.Forte@Sun.COM 	int (*fn)();
14127836SJohn.Forte@Sun.COM 	int rc;
14137836SJohn.Forte@Sun.COM 
14147836SJohn.Forte@Sun.COM 	svp = sv_find_enabled(dev, &maj);
14157836SJohn.Forte@Sun.COM 	if (svp == NULL) {
14167836SJohn.Forte@Sun.COM 		if (maj) {
14177836SJohn.Forte@Sun.COM 			if (rw == NSC_READ)
14187836SJohn.Forte@Sun.COM 				fn = maj->sm_read;
14197836SJohn.Forte@Sun.COM 			else
14207836SJohn.Forte@Sun.COM 				fn = maj->sm_write;
14217836SJohn.Forte@Sun.COM 
14227836SJohn.Forte@Sun.COM 			if (fn != 0) {
14237836SJohn.Forte@Sun.COM 				if (!(maj->sm_flag & D_MP)) {
14247836SJohn.Forte@Sun.COM 					UNSAFE_ENTER();
14257836SJohn.Forte@Sun.COM 					rc = (*fn)(dev, uiop, crp);
14267836SJohn.Forte@Sun.COM 					UNSAFE_EXIT();
14277836SJohn.Forte@Sun.COM 				} else {
14287836SJohn.Forte@Sun.COM 					rc = (*fn)(dev, uiop, crp);
14297836SJohn.Forte@Sun.COM 				}
14307836SJohn.Forte@Sun.COM 			}
14317836SJohn.Forte@Sun.COM 
14327836SJohn.Forte@Sun.COM 			return (rc);
14337836SJohn.Forte@Sun.COM 		} else {
14347836SJohn.Forte@Sun.COM 			return (ENODEV);
14357836SJohn.Forte@Sun.COM 		}
14367836SJohn.Forte@Sun.COM 	}
14377836SJohn.Forte@Sun.COM 
14387836SJohn.Forte@Sun.COM 	ASSERT(RW_READ_HELD(&svp->sv_lock));
14397836SJohn.Forte@Sun.COM 
14407836SJohn.Forte@Sun.COM 	if (svp->sv_flag == 0) {
14417836SJohn.Forte@Sun.COM 		/*
14427836SJohn.Forte@Sun.COM 		 * guard access mode
14437836SJohn.Forte@Sun.COM 		 * - prevent user level access to the device
14447836SJohn.Forte@Sun.COM 		 */
1445*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_lyr_uio_err_guard, uio_t *, uiop);
14467836SJohn.Forte@Sun.COM 		rc = EPERM;
14477836SJohn.Forte@Sun.COM 		goto out;
14487836SJohn.Forte@Sun.COM 	}
14497836SJohn.Forte@Sun.COM 
14507836SJohn.Forte@Sun.COM 	if ((rc = sv_reserve(svp->sv_fd, NSC_MULTI|NSC_PCATCH)) != 0) {
1451*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_lyr_uio_err_rsrv, uio_t *, uiop);
14527836SJohn.Forte@Sun.COM 		goto out;
14537836SJohn.Forte@Sun.COM 	}
14547836SJohn.Forte@Sun.COM 
14557836SJohn.Forte@Sun.COM 	if (rw == NSC_READ)
14567836SJohn.Forte@Sun.COM 		rc = nsc_uread(svp->sv_fd, uiop, crp);
14577836SJohn.Forte@Sun.COM 	else
14587836SJohn.Forte@Sun.COM 		rc = nsc_uwrite(svp->sv_fd, uiop, crp);
14597836SJohn.Forte@Sun.COM 
14607836SJohn.Forte@Sun.COM 	nsc_release(svp->sv_fd);
14617836SJohn.Forte@Sun.COM 
14627836SJohn.Forte@Sun.COM out:
14637836SJohn.Forte@Sun.COM 	rw_exit(&svp->sv_lock);
14647836SJohn.Forte@Sun.COM 
14657836SJohn.Forte@Sun.COM 	return (rc);
14667836SJohn.Forte@Sun.COM }
14677836SJohn.Forte@Sun.COM 
14687836SJohn.Forte@Sun.COM 
14697836SJohn.Forte@Sun.COM static int
sv_lyr_read(dev_t dev,uio_t * uiop,cred_t * crp)14707836SJohn.Forte@Sun.COM sv_lyr_read(dev_t dev, uio_t *uiop, cred_t *crp)
14717836SJohn.Forte@Sun.COM {
14727836SJohn.Forte@Sun.COM 	return (sv_lyr_uio(dev, uiop, crp, NSC_READ));
14737836SJohn.Forte@Sun.COM }
14747836SJohn.Forte@Sun.COM 
14757836SJohn.Forte@Sun.COM 
14767836SJohn.Forte@Sun.COM static int
sv_lyr_write(dev_t dev,uio_t * uiop,cred_t * crp)14777836SJohn.Forte@Sun.COM sv_lyr_write(dev_t dev, uio_t *uiop, cred_t *crp)
14787836SJohn.Forte@Sun.COM {
14797836SJohn.Forte@Sun.COM 	return (sv_lyr_uio(dev, uiop, crp, NSC_WRITE));
14807836SJohn.Forte@Sun.COM }
14817836SJohn.Forte@Sun.COM 
14827836SJohn.Forte@Sun.COM 
14837836SJohn.Forte@Sun.COM /* ARGSUSED */
14847836SJohn.Forte@Sun.COM 
14857836SJohn.Forte@Sun.COM static int
sv_lyr_aread(dev_t dev,struct aio_req * aio,cred_t * crp)14867836SJohn.Forte@Sun.COM sv_lyr_aread(dev_t dev, struct aio_req *aio, cred_t *crp)
14877836SJohn.Forte@Sun.COM {
14887836SJohn.Forte@Sun.COM 	return (aphysio(sv_lyr_strategy,
14897836SJohn.Forte@Sun.COM 	    anocancel, dev, B_READ, minphys, aio));
14907836SJohn.Forte@Sun.COM }
14917836SJohn.Forte@Sun.COM 
14927836SJohn.Forte@Sun.COM 
14937836SJohn.Forte@Sun.COM /* ARGSUSED */
14947836SJohn.Forte@Sun.COM 
14957836SJohn.Forte@Sun.COM static int
sv_lyr_awrite(dev_t dev,struct aio_req * aio,cred_t * crp)14967836SJohn.Forte@Sun.COM sv_lyr_awrite(dev_t dev, struct aio_req *aio, cred_t *crp)
14977836SJohn.Forte@Sun.COM {
14987836SJohn.Forte@Sun.COM 	return (aphysio(sv_lyr_strategy,
14997836SJohn.Forte@Sun.COM 	    anocancel, dev, B_WRITE, minphys, aio));
15007836SJohn.Forte@Sun.COM }
15017836SJohn.Forte@Sun.COM 
15027836SJohn.Forte@Sun.COM 
15037836SJohn.Forte@Sun.COM /*
15047836SJohn.Forte@Sun.COM  * Set up an array containing the list of raw path names
15057836SJohn.Forte@Sun.COM  * The array for the paths is svl and the size of the array is
15067836SJohn.Forte@Sun.COM  * in size.
15077836SJohn.Forte@Sun.COM  *
15087836SJohn.Forte@Sun.COM  * If there are more layered devices than will fit in the array,
15097836SJohn.Forte@Sun.COM  * the number of extra layered devices is returned.  Otherwise
15107836SJohn.Forte@Sun.COM  * zero is return.
15117836SJohn.Forte@Sun.COM  *
15127836SJohn.Forte@Sun.COM  * Input:
15137836SJohn.Forte@Sun.COM  *	svn	: array for paths
15147836SJohn.Forte@Sun.COM  *	size	: size of the array
15157836SJohn.Forte@Sun.COM  *
15167836SJohn.Forte@Sun.COM  * Output (extra):
15177836SJohn.Forte@Sun.COM  *	zero	: All paths fit in array
15187836SJohn.Forte@Sun.COM  *	>0	: Number of defined layered devices don't fit in array
15197836SJohn.Forte@Sun.COM  */
15207836SJohn.Forte@Sun.COM 
15217836SJohn.Forte@Sun.COM static int
sv_list(void * ptr,const int size,int * extra,const int ilp32)15227836SJohn.Forte@Sun.COM sv_list(void *ptr, const int size, int *extra, const int ilp32)
15237836SJohn.Forte@Sun.COM {
15247836SJohn.Forte@Sun.COM 	sv_name32_t *svn32;
15257836SJohn.Forte@Sun.COM 	sv_name_t *svn;
15267836SJohn.Forte@Sun.COM 	sv_dev_t *svp;
15277836SJohn.Forte@Sun.COM 	int *mode, *nblocks;
15287836SJohn.Forte@Sun.COM 	int i, index;
15297836SJohn.Forte@Sun.COM 	char *path;
15307836SJohn.Forte@Sun.COM 
15317836SJohn.Forte@Sun.COM 	*extra = 0;
15327836SJohn.Forte@Sun.COM 	index = 0;
15337836SJohn.Forte@Sun.COM 
15347836SJohn.Forte@Sun.COM 	if (ilp32)
15357836SJohn.Forte@Sun.COM 		svn32 = ptr;
15367836SJohn.Forte@Sun.COM 	else
15377836SJohn.Forte@Sun.COM 		svn = ptr;
15387836SJohn.Forte@Sun.COM 
15397836SJohn.Forte@Sun.COM 	mutex_enter(&sv_mutex);
15407836SJohn.Forte@Sun.COM 	for (i = 0; i < sv_max_devices; i++) {
15417836SJohn.Forte@Sun.COM 		svp = &sv_devs[i];
15427836SJohn.Forte@Sun.COM 
15437836SJohn.Forte@Sun.COM 		rw_enter(&svp->sv_lock, RW_READER);
15447836SJohn.Forte@Sun.COM 
15457836SJohn.Forte@Sun.COM 		if (svp->sv_state != SV_ENABLE) {
15467836SJohn.Forte@Sun.COM 			rw_exit(&svp->sv_lock);
15477836SJohn.Forte@Sun.COM 			continue;
15487836SJohn.Forte@Sun.COM 		}
15497836SJohn.Forte@Sun.COM 
15507836SJohn.Forte@Sun.COM 		if ((*extra) != 0 || ptr == NULL) {
15517836SJohn.Forte@Sun.COM 			/* Another overflow entry */
15527836SJohn.Forte@Sun.COM 			rw_exit(&svp->sv_lock);
15537836SJohn.Forte@Sun.COM 			(*extra)++;
15547836SJohn.Forte@Sun.COM 			continue;
15557836SJohn.Forte@Sun.COM 		}
15567836SJohn.Forte@Sun.COM 
15577836SJohn.Forte@Sun.COM 		if (ilp32) {
15587836SJohn.Forte@Sun.COM 			nblocks = &svn32->svn_nblocks;
15597836SJohn.Forte@Sun.COM 			mode = &svn32->svn_mode;
15607836SJohn.Forte@Sun.COM 			path = svn32->svn_path;
15617836SJohn.Forte@Sun.COM 
15627836SJohn.Forte@Sun.COM 			svn32->svn_timestamp = (uint32_t)svp->sv_timestamp;
15637836SJohn.Forte@Sun.COM 			svn32++;
15647836SJohn.Forte@Sun.COM 		} else {
15657836SJohn.Forte@Sun.COM 			nblocks = &svn->svn_nblocks;
15667836SJohn.Forte@Sun.COM 			mode = &svn->svn_mode;
15677836SJohn.Forte@Sun.COM 			path = svn->svn_path;
15687836SJohn.Forte@Sun.COM 
15697836SJohn.Forte@Sun.COM 			svn->svn_timestamp = svp->sv_timestamp;
15707836SJohn.Forte@Sun.COM 			svn++;
15717836SJohn.Forte@Sun.COM 		}
15727836SJohn.Forte@Sun.COM 
15737836SJohn.Forte@Sun.COM 		(void) strcpy(path, nsc_pathname(svp->sv_fd));
15747836SJohn.Forte@Sun.COM 		*nblocks = svp->sv_nblocks;
15757836SJohn.Forte@Sun.COM 		*mode = svp->sv_flag;
15767836SJohn.Forte@Sun.COM 
15777836SJohn.Forte@Sun.COM 		if (*nblocks == 0) {
15787836SJohn.Forte@Sun.COM 			if (sv_debug > 3)
1579*9093SRamana.Srikanth@Sun.COM 				cmn_err(CE_CONT, "!sv_list: need to reserve\n");
1580*9093SRamana.Srikanth@Sun.COM 
1581*9093SRamana.Srikanth@Sun.COM 			if (sv_reserve(svp->sv_fd, NSC_MULTI|NSC_PCATCH) == 0) {
15827836SJohn.Forte@Sun.COM 				*nblocks = svp->sv_nblocks;
15837836SJohn.Forte@Sun.COM 				nsc_release(svp->sv_fd);
15847836SJohn.Forte@Sun.COM 			}
15857836SJohn.Forte@Sun.COM 		}
15867836SJohn.Forte@Sun.COM 
15877836SJohn.Forte@Sun.COM 		if (++index >= size) {
15887836SJohn.Forte@Sun.COM 			/* Out of space */
15897836SJohn.Forte@Sun.COM 			(*extra)++;
15907836SJohn.Forte@Sun.COM 		}
15917836SJohn.Forte@Sun.COM 
15927836SJohn.Forte@Sun.COM 		rw_exit(&svp->sv_lock);
15937836SJohn.Forte@Sun.COM 	}
15947836SJohn.Forte@Sun.COM 	mutex_exit(&sv_mutex);
15957836SJohn.Forte@Sun.COM 
15967836SJohn.Forte@Sun.COM 	if (index < size) {
15977836SJohn.Forte@Sun.COM 		/* NULL terminated list */
15987836SJohn.Forte@Sun.COM 		if (ilp32)
15997836SJohn.Forte@Sun.COM 			svn32->svn_path[0] = '\0';
16007836SJohn.Forte@Sun.COM 		else
16017836SJohn.Forte@Sun.COM 			svn->svn_path[0] = '\0';
16027836SJohn.Forte@Sun.COM 	}
16037836SJohn.Forte@Sun.COM 
16047836SJohn.Forte@Sun.COM 	return (0);
16057836SJohn.Forte@Sun.COM }
16067836SJohn.Forte@Sun.COM 
16077836SJohn.Forte@Sun.COM 
16087836SJohn.Forte@Sun.COM static void
sv_thread_tune(int threads)16097836SJohn.Forte@Sun.COM sv_thread_tune(int threads)
16107836SJohn.Forte@Sun.COM {
16117836SJohn.Forte@Sun.COM 	int incr = (threads > 0) ? 1 : -1;
16127836SJohn.Forte@Sun.COM 	int change = 0;
16137836SJohn.Forte@Sun.COM 	int nthreads;
16147836SJohn.Forte@Sun.COM 
16157836SJohn.Forte@Sun.COM 	ASSERT(MUTEX_HELD(&sv_mutex));
16167836SJohn.Forte@Sun.COM 
16177836SJohn.Forte@Sun.COM 	if (sv_threads_extra) {
16187836SJohn.Forte@Sun.COM 		/* keep track of any additional threads requested */
16197836SJohn.Forte@Sun.COM 		if (threads > 0) {
16207836SJohn.Forte@Sun.COM 			sv_threads_extra += threads;
16217836SJohn.Forte@Sun.COM 			return;
16227836SJohn.Forte@Sun.COM 		}
16237836SJohn.Forte@Sun.COM 		threads = -threads;
16247836SJohn.Forte@Sun.COM 		if (threads >= sv_threads_extra) {
16257836SJohn.Forte@Sun.COM 			threads -= sv_threads_extra;
16267836SJohn.Forte@Sun.COM 			sv_threads_extra = 0;
16277836SJohn.Forte@Sun.COM 			/* fall through to while loop */
16287836SJohn.Forte@Sun.COM 		} else {
16297836SJohn.Forte@Sun.COM 			sv_threads_extra -= threads;
16307836SJohn.Forte@Sun.COM 			return;
16317836SJohn.Forte@Sun.COM 		}
16327836SJohn.Forte@Sun.COM 	} else if (threads > 0) {
16337836SJohn.Forte@Sun.COM 		/*
16347836SJohn.Forte@Sun.COM 		 * do not increase the number of threads beyond
16357836SJohn.Forte@Sun.COM 		 * sv_threads_max when doing dynamic thread tuning
16367836SJohn.Forte@Sun.COM 		 */
16377836SJohn.Forte@Sun.COM 		nthreads = nst_nthread(sv_tset);
16387836SJohn.Forte@Sun.COM 		if ((nthreads + threads) > sv_threads_max) {
16397836SJohn.Forte@Sun.COM 			sv_threads_extra = nthreads + threads - sv_threads_max;
16407836SJohn.Forte@Sun.COM 			threads = sv_threads_max - nthreads;
16417836SJohn.Forte@Sun.COM 			if (threads <= 0)
16427836SJohn.Forte@Sun.COM 				return;
16437836SJohn.Forte@Sun.COM 		}
16447836SJohn.Forte@Sun.COM 	}
16457836SJohn.Forte@Sun.COM 
16467836SJohn.Forte@Sun.COM 	if (threads < 0)
16477836SJohn.Forte@Sun.COM 		threads = -threads;
16487836SJohn.Forte@Sun.COM 
16497836SJohn.Forte@Sun.COM 	while (threads--) {
16507836SJohn.Forte@Sun.COM 		nthreads = nst_nthread(sv_tset);
16517836SJohn.Forte@Sun.COM 		sv_threads_needed += incr;
16527836SJohn.Forte@Sun.COM 
16537836SJohn.Forte@Sun.COM 		if (sv_threads_needed >= nthreads)
16547836SJohn.Forte@Sun.COM 			change += nst_add_thread(sv_tset, sv_threads_inc);
16557836SJohn.Forte@Sun.COM 		else if ((sv_threads_needed <
16567836SJohn.Forte@Sun.COM 		    (nthreads - (sv_threads_inc + sv_threads_hysteresis))) &&
16577836SJohn.Forte@Sun.COM 		    ((nthreads - sv_threads_inc) >= sv_threads))
16587836SJohn.Forte@Sun.COM 			change -= nst_del_thread(sv_tset, sv_threads_inc);
16597836SJohn.Forte@Sun.COM 	}
16607836SJohn.Forte@Sun.COM 
16617836SJohn.Forte@Sun.COM #ifdef DEBUG
16627836SJohn.Forte@Sun.COM 	if (change) {
16637836SJohn.Forte@Sun.COM 		cmn_err(CE_NOTE,
1664*9093SRamana.Srikanth@Sun.COM 		    "!sv_thread_tune: threads needed %d, nthreads %d, "
16657836SJohn.Forte@Sun.COM 		    "nthreads change %d",
16667836SJohn.Forte@Sun.COM 		    sv_threads_needed, nst_nthread(sv_tset), change);
16677836SJohn.Forte@Sun.COM 	}
16687836SJohn.Forte@Sun.COM #endif
16697836SJohn.Forte@Sun.COM }
16707836SJohn.Forte@Sun.COM 
16717836SJohn.Forte@Sun.COM 
16727836SJohn.Forte@Sun.COM /* ARGSUSED */
16737836SJohn.Forte@Sun.COM static int
svopen(dev_t * devp,int flag,int otyp,cred_t * crp)16747836SJohn.Forte@Sun.COM svopen(dev_t *devp, int flag, int otyp, cred_t *crp)
16757836SJohn.Forte@Sun.COM {
16767836SJohn.Forte@Sun.COM 	int rc;
16777836SJohn.Forte@Sun.COM 
16787836SJohn.Forte@Sun.COM 	mutex_enter(&sv_mutex);
16797836SJohn.Forte@Sun.COM 	rc = sv_init_devs();
16807836SJohn.Forte@Sun.COM 	mutex_exit(&sv_mutex);
16817836SJohn.Forte@Sun.COM 
16827836SJohn.Forte@Sun.COM 	return (rc);
16837836SJohn.Forte@Sun.COM }
16847836SJohn.Forte@Sun.COM 
16857836SJohn.Forte@Sun.COM 
16867836SJohn.Forte@Sun.COM /* ARGSUSED */
16877836SJohn.Forte@Sun.COM static int
svclose(dev_t dev,int flag,int otyp,cred_t * crp)16887836SJohn.Forte@Sun.COM svclose(dev_t dev, int flag, int otyp, cred_t *crp)
16897836SJohn.Forte@Sun.COM {
16907836SJohn.Forte@Sun.COM 	const int secs = HZ * 5;
16917836SJohn.Forte@Sun.COM 	const int ticks = HZ / 10;
16927836SJohn.Forte@Sun.COM 	int loops = secs / ticks;
16937836SJohn.Forte@Sun.COM 
16947836SJohn.Forte@Sun.COM 	mutex_enter(&sv_mutex);
16957836SJohn.Forte@Sun.COM 	while (sv_ndevices <= 0 && sv_tset != NULL && loops > 0) {
16967836SJohn.Forte@Sun.COM 		if (nst_nlive(sv_tset) <= 0) {
16977836SJohn.Forte@Sun.COM 			nst_destroy(sv_tset);
16987836SJohn.Forte@Sun.COM 			sv_tset = NULL;
16997836SJohn.Forte@Sun.COM 			break;
17007836SJohn.Forte@Sun.COM 		}
17017836SJohn.Forte@Sun.COM 
17027836SJohn.Forte@Sun.COM 		/* threads still active - wait for them to exit */
17037836SJohn.Forte@Sun.COM 		mutex_exit(&sv_mutex);
17047836SJohn.Forte@Sun.COM 		delay(ticks);
17057836SJohn.Forte@Sun.COM 		loops--;
17067836SJohn.Forte@Sun.COM 		mutex_enter(&sv_mutex);
17077836SJohn.Forte@Sun.COM 	}
17087836SJohn.Forte@Sun.COM 	mutex_exit(&sv_mutex);
17097836SJohn.Forte@Sun.COM 
17107836SJohn.Forte@Sun.COM 	if (loops <= 0) {
17117836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN,
17127836SJohn.Forte@Sun.COM #ifndef DEBUG
17137836SJohn.Forte@Sun.COM 		    /* do not write to console when non-DEBUG */
17147836SJohn.Forte@Sun.COM 		    "!"
17157836SJohn.Forte@Sun.COM #endif
17167836SJohn.Forte@Sun.COM 		    "sv:svclose: threads still active "
17177836SJohn.Forte@Sun.COM 		    "after %d sec - leaking thread set", secs);
17187836SJohn.Forte@Sun.COM 	}
17197836SJohn.Forte@Sun.COM 
17207836SJohn.Forte@Sun.COM 	return (0);
17217836SJohn.Forte@Sun.COM }
17227836SJohn.Forte@Sun.COM 
17237836SJohn.Forte@Sun.COM 
17247836SJohn.Forte@Sun.COM static int
svioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * crp,int * rvalp)17257836SJohn.Forte@Sun.COM svioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *crp, int *rvalp)
17267836SJohn.Forte@Sun.COM {
17277836SJohn.Forte@Sun.COM 	char itmp1[12], itmp2[12]; /* temp char array for editing ints */
17287836SJohn.Forte@Sun.COM 	spcs_s_info_t kstatus;	/* Kernel version of spcs status */
17297836SJohn.Forte@Sun.COM 	spcs_s_info_t ustatus;	/* Address of user version of spcs status */
17307836SJohn.Forte@Sun.COM 	sv_list32_t svl32;	/* 32 bit Initial structure for SVIOC_LIST */
17317836SJohn.Forte@Sun.COM 	sv_version_t svv;	/* Version structure */
17327836SJohn.Forte@Sun.COM 	sv_conf_t svc;		/* User config structure */
17337836SJohn.Forte@Sun.COM 	sv_list_t svl;		/* Initial structure for SVIOC_LIST */
17347836SJohn.Forte@Sun.COM 	void *usvn;		/* Address of user sv_name_t */
17357836SJohn.Forte@Sun.COM 	void *svn = NULL;	/* Array for SVIOC_LIST */
17367836SJohn.Forte@Sun.COM 	uint64_t phash;		/* pathname hash */
17377836SJohn.Forte@Sun.COM 	int rc = 0;		/* Return code -- errno */
17387836SJohn.Forte@Sun.COM 	int size;		/* Number of items in array */
17397836SJohn.Forte@Sun.COM 	int bytes;		/* Byte size of array */
17407836SJohn.Forte@Sun.COM 	int ilp32;		/* Convert data structures for ilp32 userland */
17417836SJohn.Forte@Sun.COM 
17427836SJohn.Forte@Sun.COM 	*rvalp = 0;
17437836SJohn.Forte@Sun.COM 
17447836SJohn.Forte@Sun.COM 	/*
17457836SJohn.Forte@Sun.COM 	 * If sv_mod_status is 0 or SV_PREVENT_UNLOAD, then it will continue.
17467836SJohn.Forte@Sun.COM 	 * else it means it previously was SV_PREVENT_UNLOAD, and now it's
17477836SJohn.Forte@Sun.COM 	 * SV_ALLOW_UNLOAD, expecting the driver to eventually unload.
17487836SJohn.Forte@Sun.COM 	 *
17497836SJohn.Forte@Sun.COM 	 * SV_ALLOW_UNLOAD is final state, so no need to grab sv_mutex.
17507836SJohn.Forte@Sun.COM 	 */
17517836SJohn.Forte@Sun.COM 	if (sv_mod_status == SV_ALLOW_UNLOAD) {
17527836SJohn.Forte@Sun.COM 		return (EBUSY);
17537836SJohn.Forte@Sun.COM 	}
17547836SJohn.Forte@Sun.COM 
17557836SJohn.Forte@Sun.COM 	if ((cmd != SVIOC_LIST) && ((rc = drv_priv(crp)) != 0))
17567836SJohn.Forte@Sun.COM 		return (rc);
17577836SJohn.Forte@Sun.COM 
17587836SJohn.Forte@Sun.COM 	kstatus = spcs_s_kcreate();
17597836SJohn.Forte@Sun.COM 	if (!kstatus) {
1760*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_ioctl_err_kcreate, dev_t, dev);
17617836SJohn.Forte@Sun.COM 		return (ENOMEM);
17627836SJohn.Forte@Sun.COM 	}
17637836SJohn.Forte@Sun.COM 
17647836SJohn.Forte@Sun.COM 	ilp32 = (ddi_model_convert_from((mode & FMODELS)) == DDI_MODEL_ILP32);
17657836SJohn.Forte@Sun.COM 
17667836SJohn.Forte@Sun.COM 	switch (cmd) {
17677836SJohn.Forte@Sun.COM 
17687836SJohn.Forte@Sun.COM 	case SVIOC_ENABLE:
17697836SJohn.Forte@Sun.COM 
17707836SJohn.Forte@Sun.COM 		if (ilp32) {
17717836SJohn.Forte@Sun.COM 			sv_conf32_t svc32;
17727836SJohn.Forte@Sun.COM 
17737836SJohn.Forte@Sun.COM 			if (ddi_copyin((void *)arg, &svc32,
1774*9093SRamana.Srikanth@Sun.COM 			    sizeof (svc32), mode) < 0) {
17757836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
17767836SJohn.Forte@Sun.COM 				return (EFAULT);
17777836SJohn.Forte@Sun.COM 			}
17787836SJohn.Forte@Sun.COM 
17797836SJohn.Forte@Sun.COM 			svc.svc_error = (spcs_s_info_t)svc32.svc_error;
17807836SJohn.Forte@Sun.COM 			(void) strcpy(svc.svc_path, svc32.svc_path);
17817836SJohn.Forte@Sun.COM 			svc.svc_flag  = svc32.svc_flag;
17827836SJohn.Forte@Sun.COM 			svc.svc_major = svc32.svc_major;
17837836SJohn.Forte@Sun.COM 			svc.svc_minor = svc32.svc_minor;
17847836SJohn.Forte@Sun.COM 		} else {
17857836SJohn.Forte@Sun.COM 			if (ddi_copyin((void *)arg, &svc,
1786*9093SRamana.Srikanth@Sun.COM 			    sizeof (svc), mode) < 0) {
17877836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
17887836SJohn.Forte@Sun.COM 				return (EFAULT);
17897836SJohn.Forte@Sun.COM 			}
17907836SJohn.Forte@Sun.COM 		}
17917836SJohn.Forte@Sun.COM 
17927836SJohn.Forte@Sun.COM 		/* force to raw access */
17937836SJohn.Forte@Sun.COM 		svc.svc_flag = NSC_DEVICE;
17947836SJohn.Forte@Sun.COM 
17957836SJohn.Forte@Sun.COM 		if (sv_tset == NULL) {
17967836SJohn.Forte@Sun.COM 			mutex_enter(&sv_mutex);
17977836SJohn.Forte@Sun.COM 
17987836SJohn.Forte@Sun.COM 			if (sv_tset == NULL) {
17997836SJohn.Forte@Sun.COM 				sv_tset = nst_init("sv_thr", sv_threads);
18007836SJohn.Forte@Sun.COM 			}
18017836SJohn.Forte@Sun.COM 
18027836SJohn.Forte@Sun.COM 			mutex_exit(&sv_mutex);
18037836SJohn.Forte@Sun.COM 
18047836SJohn.Forte@Sun.COM 			if (sv_tset == NULL) {
18057836SJohn.Forte@Sun.COM 				cmn_err(CE_WARN,
1806*9093SRamana.Srikanth@Sun.COM 				    "!sv: could not allocate %d threads",
18077836SJohn.Forte@Sun.COM 				    sv_threads);
18087836SJohn.Forte@Sun.COM 			}
18097836SJohn.Forte@Sun.COM 		}
18107836SJohn.Forte@Sun.COM 
18117836SJohn.Forte@Sun.COM 		rc = sv_enable(svc.svc_path, svc.svc_flag,
1812*9093SRamana.Srikanth@Sun.COM 		    makedevice(svc.svc_major, svc.svc_minor), kstatus);
18137836SJohn.Forte@Sun.COM 
18147836SJohn.Forte@Sun.COM 		if (rc == 0) {
18157836SJohn.Forte@Sun.COM 			sv_config_time = nsc_lbolt();
18167836SJohn.Forte@Sun.COM 
18177836SJohn.Forte@Sun.COM 			mutex_enter(&sv_mutex);
18187836SJohn.Forte@Sun.COM 			sv_thread_tune(sv_threads_dev);
18197836SJohn.Forte@Sun.COM 			mutex_exit(&sv_mutex);
18207836SJohn.Forte@Sun.COM 		}
18217836SJohn.Forte@Sun.COM 
1822*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE3(sv_ioctl_end, dev_t, dev, int, *rvalp, int, rc);
18237836SJohn.Forte@Sun.COM 
18247836SJohn.Forte@Sun.COM 		return (spcs_s_ocopyoutf(&kstatus, svc.svc_error, rc));
18257836SJohn.Forte@Sun.COM 		/* NOTREACHED */
18267836SJohn.Forte@Sun.COM 
18277836SJohn.Forte@Sun.COM 	case SVIOC_DISABLE:
18287836SJohn.Forte@Sun.COM 
18297836SJohn.Forte@Sun.COM 		if (ilp32) {
18307836SJohn.Forte@Sun.COM 			sv_conf32_t svc32;
18317836SJohn.Forte@Sun.COM 
18327836SJohn.Forte@Sun.COM 			if (ddi_copyin((void *)arg, &svc32,
1833*9093SRamana.Srikanth@Sun.COM 			    sizeof (svc32), mode) < 0) {
18347836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
18357836SJohn.Forte@Sun.COM 				return (EFAULT);
18367836SJohn.Forte@Sun.COM 			}
18377836SJohn.Forte@Sun.COM 
18387836SJohn.Forte@Sun.COM 			svc.svc_error = (spcs_s_info_t)svc32.svc_error;
18397836SJohn.Forte@Sun.COM 			svc.svc_major = svc32.svc_major;
18407836SJohn.Forte@Sun.COM 			svc.svc_minor = svc32.svc_minor;
18417836SJohn.Forte@Sun.COM 			(void) strcpy(svc.svc_path, svc32.svc_path);
18427836SJohn.Forte@Sun.COM 			svc.svc_flag  = svc32.svc_flag;
18437836SJohn.Forte@Sun.COM 		} else {
18447836SJohn.Forte@Sun.COM 			if (ddi_copyin((void *)arg, &svc,
1845*9093SRamana.Srikanth@Sun.COM 			    sizeof (svc), mode) < 0) {
18467836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
18477836SJohn.Forte@Sun.COM 				return (EFAULT);
18487836SJohn.Forte@Sun.COM 			}
18497836SJohn.Forte@Sun.COM 		}
18507836SJohn.Forte@Sun.COM 
18517836SJohn.Forte@Sun.COM 		if (svc.svc_major == (major_t)-1 &&
18527836SJohn.Forte@Sun.COM 		    svc.svc_minor == (minor_t)-1) {
18537836SJohn.Forte@Sun.COM 			sv_dev_t *svp;
18547836SJohn.Forte@Sun.COM 			int i;
18557836SJohn.Forte@Sun.COM 
18567836SJohn.Forte@Sun.COM 			/*
18577836SJohn.Forte@Sun.COM 			 * User level could not find the minor device
18587836SJohn.Forte@Sun.COM 			 * node, so do this the slow way by searching
18597836SJohn.Forte@Sun.COM 			 * the entire sv config for a matching pathname.
18607836SJohn.Forte@Sun.COM 			 */
18617836SJohn.Forte@Sun.COM 
18627836SJohn.Forte@Sun.COM 			phash = nsc_strhash(svc.svc_path);
18637836SJohn.Forte@Sun.COM 
18647836SJohn.Forte@Sun.COM 			mutex_enter(&sv_mutex);
18657836SJohn.Forte@Sun.COM 
18667836SJohn.Forte@Sun.COM 			for (i = 0; i < sv_max_devices; i++) {
18677836SJohn.Forte@Sun.COM 				svp = &sv_devs[i];
18687836SJohn.Forte@Sun.COM 
18697836SJohn.Forte@Sun.COM 				if (svp->sv_state == SV_DISABLE ||
18707836SJohn.Forte@Sun.COM 				    svp->sv_fd == NULL)
18717836SJohn.Forte@Sun.COM 					continue;
18727836SJohn.Forte@Sun.COM 
18737836SJohn.Forte@Sun.COM 				if (nsc_fdpathcmp(svp->sv_fd, phash,
18747836SJohn.Forte@Sun.COM 				    svc.svc_path) == 0) {
18757836SJohn.Forte@Sun.COM 					svc.svc_major = getmajor(svp->sv_dev);
18767836SJohn.Forte@Sun.COM 					svc.svc_minor = getminor(svp->sv_dev);
18777836SJohn.Forte@Sun.COM 					break;
18787836SJohn.Forte@Sun.COM 				}
18797836SJohn.Forte@Sun.COM 			}
18807836SJohn.Forte@Sun.COM 
18817836SJohn.Forte@Sun.COM 			mutex_exit(&sv_mutex);
18827836SJohn.Forte@Sun.COM 
18837836SJohn.Forte@Sun.COM 			if (svc.svc_major == (major_t)-1 &&
18847836SJohn.Forte@Sun.COM 			    svc.svc_minor == (minor_t)-1)
18857836SJohn.Forte@Sun.COM 				return (spcs_s_ocopyoutf(&kstatus,
1886*9093SRamana.Srikanth@Sun.COM 				    svc.svc_error, SV_ENODEV));
18877836SJohn.Forte@Sun.COM 		}
18887836SJohn.Forte@Sun.COM 
18897836SJohn.Forte@Sun.COM 		rc = sv_disable(makedevice(svc.svc_major, svc.svc_minor),
1890*9093SRamana.Srikanth@Sun.COM 		    kstatus);
18917836SJohn.Forte@Sun.COM 
18927836SJohn.Forte@Sun.COM 		if (rc == 0) {
18937836SJohn.Forte@Sun.COM 			sv_config_time = nsc_lbolt();
18947836SJohn.Forte@Sun.COM 
18957836SJohn.Forte@Sun.COM 			mutex_enter(&sv_mutex);
18967836SJohn.Forte@Sun.COM 			sv_thread_tune(-sv_threads_dev);
18977836SJohn.Forte@Sun.COM 			mutex_exit(&sv_mutex);
18987836SJohn.Forte@Sun.COM 		}
18997836SJohn.Forte@Sun.COM 
1900*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE3(sv_ioctl_2, dev_t, dev, int, *rvalp, int, rc);
19017836SJohn.Forte@Sun.COM 
19027836SJohn.Forte@Sun.COM 		return (spcs_s_ocopyoutf(&kstatus, svc.svc_error, rc));
19037836SJohn.Forte@Sun.COM 		/* NOTREACHED */
19047836SJohn.Forte@Sun.COM 
19057836SJohn.Forte@Sun.COM 	case SVIOC_LIST:
19067836SJohn.Forte@Sun.COM 
19077836SJohn.Forte@Sun.COM 		if (ilp32) {
19087836SJohn.Forte@Sun.COM 			if (ddi_copyin((void *)arg, &svl32,
1909*9093SRamana.Srikanth@Sun.COM 			    sizeof (svl32), mode) < 0) {
19107836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
19117836SJohn.Forte@Sun.COM 				return (EFAULT);
19127836SJohn.Forte@Sun.COM 			}
19137836SJohn.Forte@Sun.COM 
19147836SJohn.Forte@Sun.COM 			ustatus = (spcs_s_info_t)svl32.svl_error;
19157836SJohn.Forte@Sun.COM 			size = svl32.svl_count;
19167836SJohn.Forte@Sun.COM 			usvn = (void *)(unsigned long)svl32.svl_names;
19177836SJohn.Forte@Sun.COM 		} else {
19187836SJohn.Forte@Sun.COM 			if (ddi_copyin((void *)arg, &svl,
1919*9093SRamana.Srikanth@Sun.COM 			    sizeof (svl), mode) < 0) {
19207836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
19217836SJohn.Forte@Sun.COM 				return (EFAULT);
19227836SJohn.Forte@Sun.COM 			}
19237836SJohn.Forte@Sun.COM 
19247836SJohn.Forte@Sun.COM 			ustatus = svl.svl_error;
19257836SJohn.Forte@Sun.COM 			size = svl.svl_count;
19267836SJohn.Forte@Sun.COM 			usvn = svl.svl_names;
19277836SJohn.Forte@Sun.COM 		}
19287836SJohn.Forte@Sun.COM 
19297836SJohn.Forte@Sun.COM 		/* Do some boundary checking */
19307836SJohn.Forte@Sun.COM 		if ((size < 0) || (size > sv_max_devices)) {
19317836SJohn.Forte@Sun.COM 			/* Array size is out of range */
19327836SJohn.Forte@Sun.COM 			return (spcs_s_ocopyoutf(&kstatus, ustatus,
1933*9093SRamana.Srikanth@Sun.COM 			    SV_EARRBOUNDS, "0",
1934*9093SRamana.Srikanth@Sun.COM 			    spcs_s_inttostring(sv_max_devices, itmp1,
1935*9093SRamana.Srikanth@Sun.COM 			    sizeof (itmp1), 0),
1936*9093SRamana.Srikanth@Sun.COM 			    spcs_s_inttostring(size, itmp2,
1937*9093SRamana.Srikanth@Sun.COM 			    sizeof (itmp2), 0)));
19387836SJohn.Forte@Sun.COM 		}
19397836SJohn.Forte@Sun.COM 
19407836SJohn.Forte@Sun.COM 		if (ilp32)
19417836SJohn.Forte@Sun.COM 			bytes = size * sizeof (sv_name32_t);
19427836SJohn.Forte@Sun.COM 		else
19437836SJohn.Forte@Sun.COM 			bytes = size * sizeof (sv_name_t);
19447836SJohn.Forte@Sun.COM 
19457836SJohn.Forte@Sun.COM 		/* Allocate memory for the array of structures */
19467836SJohn.Forte@Sun.COM 		if (bytes != 0) {
19477836SJohn.Forte@Sun.COM 			svn = kmem_zalloc(bytes, KM_SLEEP);
19487836SJohn.Forte@Sun.COM 			if (!svn) {
19497836SJohn.Forte@Sun.COM 				return (spcs_s_ocopyoutf(&kstatus,
19507836SJohn.Forte@Sun.COM 				    ustatus, ENOMEM));
19517836SJohn.Forte@Sun.COM 			}
19527836SJohn.Forte@Sun.COM 		}
19537836SJohn.Forte@Sun.COM 
19547836SJohn.Forte@Sun.COM 		rc = sv_list(svn, size, rvalp, ilp32);
19557836SJohn.Forte@Sun.COM 		if (rc) {
19567836SJohn.Forte@Sun.COM 			if (svn != NULL)
19577836SJohn.Forte@Sun.COM 				kmem_free(svn, bytes);
19587836SJohn.Forte@Sun.COM 			return (spcs_s_ocopyoutf(&kstatus, ustatus, rc));
19597836SJohn.Forte@Sun.COM 		}
19607836SJohn.Forte@Sun.COM 
19617836SJohn.Forte@Sun.COM 		if (ilp32) {
19627836SJohn.Forte@Sun.COM 			svl32.svl_timestamp = (uint32_t)sv_config_time;
19637836SJohn.Forte@Sun.COM 			svl32.svl_maxdevs = (int32_t)sv_max_devices;
19647836SJohn.Forte@Sun.COM 
19657836SJohn.Forte@Sun.COM 			/* Return the list structure */
19667836SJohn.Forte@Sun.COM 			if (ddi_copyout(&svl32, (void *)arg,
1967*9093SRamana.Srikanth@Sun.COM 			    sizeof (svl32), mode) < 0) {
19687836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
19697836SJohn.Forte@Sun.COM 				if (svn != NULL)
19707836SJohn.Forte@Sun.COM 					kmem_free(svn, bytes);
19717836SJohn.Forte@Sun.COM 				return (EFAULT);
19727836SJohn.Forte@Sun.COM 			}
19737836SJohn.Forte@Sun.COM 		} else {
19747836SJohn.Forte@Sun.COM 			svl.svl_timestamp = sv_config_time;
19757836SJohn.Forte@Sun.COM 			svl.svl_maxdevs = sv_max_devices;
19767836SJohn.Forte@Sun.COM 
19777836SJohn.Forte@Sun.COM 			/* Return the list structure */
19787836SJohn.Forte@Sun.COM 			if (ddi_copyout(&svl, (void *)arg,
1979*9093SRamana.Srikanth@Sun.COM 			    sizeof (svl), mode) < 0) {
19807836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
19817836SJohn.Forte@Sun.COM 				if (svn != NULL)
19827836SJohn.Forte@Sun.COM 					kmem_free(svn, bytes);
19837836SJohn.Forte@Sun.COM 				return (EFAULT);
19847836SJohn.Forte@Sun.COM 			}
19857836SJohn.Forte@Sun.COM 		}
19867836SJohn.Forte@Sun.COM 
19877836SJohn.Forte@Sun.COM 		/* Return the array */
19887836SJohn.Forte@Sun.COM 		if (svn != NULL) {
19897836SJohn.Forte@Sun.COM 			if (ddi_copyout(svn, usvn, bytes, mode) < 0) {
19907836SJohn.Forte@Sun.COM 				kmem_free(svn, bytes);
19917836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
19927836SJohn.Forte@Sun.COM 				return (EFAULT);
19937836SJohn.Forte@Sun.COM 			}
19947836SJohn.Forte@Sun.COM 			kmem_free(svn, bytes);
19957836SJohn.Forte@Sun.COM 		}
19967836SJohn.Forte@Sun.COM 
1997*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE3(sv_ioctl_3, dev_t, dev, int, *rvalp, int, 0);
19987836SJohn.Forte@Sun.COM 
19997836SJohn.Forte@Sun.COM 		return (spcs_s_ocopyoutf(&kstatus, ustatus, 0));
20007836SJohn.Forte@Sun.COM 		/* NOTREACHED */
20017836SJohn.Forte@Sun.COM 
20027836SJohn.Forte@Sun.COM 	case SVIOC_VERSION:
20037836SJohn.Forte@Sun.COM 
20047836SJohn.Forte@Sun.COM 		if (ilp32) {
20057836SJohn.Forte@Sun.COM 			sv_version32_t svv32;
20067836SJohn.Forte@Sun.COM 
20077836SJohn.Forte@Sun.COM 			if (ddi_copyin((void *)arg, &svv32,
2008*9093SRamana.Srikanth@Sun.COM 			    sizeof (svv32), mode) < 0) {
20097836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
20107836SJohn.Forte@Sun.COM 				return (EFAULT);
20117836SJohn.Forte@Sun.COM 			}
20127836SJohn.Forte@Sun.COM 
20137836SJohn.Forte@Sun.COM 			svv32.svv_major_rev = sv_major_rev;
20147836SJohn.Forte@Sun.COM 			svv32.svv_minor_rev = sv_minor_rev;
20157836SJohn.Forte@Sun.COM 			svv32.svv_micro_rev = sv_micro_rev;
20167836SJohn.Forte@Sun.COM 			svv32.svv_baseline_rev = sv_baseline_rev;
20177836SJohn.Forte@Sun.COM 
20187836SJohn.Forte@Sun.COM 			if (ddi_copyout(&svv32, (void *)arg,
2019*9093SRamana.Srikanth@Sun.COM 			    sizeof (svv32), mode) < 0) {
20207836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
20217836SJohn.Forte@Sun.COM 				return (EFAULT);
20227836SJohn.Forte@Sun.COM 			}
20237836SJohn.Forte@Sun.COM 
20247836SJohn.Forte@Sun.COM 			ustatus = (spcs_s_info_t)svv32.svv_error;
20257836SJohn.Forte@Sun.COM 		} else {
20267836SJohn.Forte@Sun.COM 			if (ddi_copyin((void *)arg, &svv,
2027*9093SRamana.Srikanth@Sun.COM 			    sizeof (svv), mode) < 0) {
20287836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
20297836SJohn.Forte@Sun.COM 				return (EFAULT);
20307836SJohn.Forte@Sun.COM 			}
20317836SJohn.Forte@Sun.COM 
20327836SJohn.Forte@Sun.COM 			svv.svv_major_rev = sv_major_rev;
20337836SJohn.Forte@Sun.COM 			svv.svv_minor_rev = sv_minor_rev;
20347836SJohn.Forte@Sun.COM 			svv.svv_micro_rev = sv_micro_rev;
20357836SJohn.Forte@Sun.COM 			svv.svv_baseline_rev = sv_baseline_rev;
20367836SJohn.Forte@Sun.COM 
20377836SJohn.Forte@Sun.COM 			if (ddi_copyout(&svv, (void *)arg,
2038*9093SRamana.Srikanth@Sun.COM 			    sizeof (svv), mode) < 0) {
20397836SJohn.Forte@Sun.COM 				spcs_s_kfree(kstatus);
20407836SJohn.Forte@Sun.COM 				return (EFAULT);
20417836SJohn.Forte@Sun.COM 			}
20427836SJohn.Forte@Sun.COM 
20437836SJohn.Forte@Sun.COM 			ustatus = svv.svv_error;
20447836SJohn.Forte@Sun.COM 		}
20457836SJohn.Forte@Sun.COM 
2046*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE3(sv_ioctl_4, dev_t, dev, int, *rvalp, int, 0);
20477836SJohn.Forte@Sun.COM 
20487836SJohn.Forte@Sun.COM 		return (spcs_s_ocopyoutf(&kstatus, ustatus, 0));
20497836SJohn.Forte@Sun.COM 		/* NOTREACHED */
20507836SJohn.Forte@Sun.COM 
20517836SJohn.Forte@Sun.COM 	case SVIOC_UNLOAD:
20527836SJohn.Forte@Sun.COM 		rc = sv_prepare_unload();
20537836SJohn.Forte@Sun.COM 
2054*9093SRamana.Srikanth@Sun.COM 		if (ddi_copyout(&rc, (void *)arg, sizeof (rc), mode) < 0) {
20557836SJohn.Forte@Sun.COM 			rc = EFAULT;
20567836SJohn.Forte@Sun.COM 		}
20577836SJohn.Forte@Sun.COM 
20587836SJohn.Forte@Sun.COM 		spcs_s_kfree(kstatus);
20597836SJohn.Forte@Sun.COM 		return (rc);
20607836SJohn.Forte@Sun.COM 
20617836SJohn.Forte@Sun.COM 	default:
20627836SJohn.Forte@Sun.COM 		spcs_s_kfree(kstatus);
20637836SJohn.Forte@Sun.COM 
2064*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE3(sv_ioctl_4, dev_t, dev, int, *rvalp, int, EINVAL);
20657836SJohn.Forte@Sun.COM 
20667836SJohn.Forte@Sun.COM 		return (EINVAL);
20677836SJohn.Forte@Sun.COM 		/* NOTREACHED */
20687836SJohn.Forte@Sun.COM 	}
20697836SJohn.Forte@Sun.COM 
20707836SJohn.Forte@Sun.COM 	/* NOTREACHED */
20717836SJohn.Forte@Sun.COM }
20727836SJohn.Forte@Sun.COM 
20737836SJohn.Forte@Sun.COM 
20747836SJohn.Forte@Sun.COM /* ARGSUSED */
20757836SJohn.Forte@Sun.COM static int
svprint(dev_t dev,char * str)20767836SJohn.Forte@Sun.COM svprint(dev_t dev, char *str)
20777836SJohn.Forte@Sun.COM {
20787836SJohn.Forte@Sun.COM 	int instance = ddi_get_instance(sv_dip);
2079*9093SRamana.Srikanth@Sun.COM 	cmn_err(CE_WARN, "!%s%d: %s", ddi_get_name(sv_dip), instance, str);
20807836SJohn.Forte@Sun.COM 	return (0);
20817836SJohn.Forte@Sun.COM }
20827836SJohn.Forte@Sun.COM 
20837836SJohn.Forte@Sun.COM 
20847836SJohn.Forte@Sun.COM static void
_sv_lyr_strategy(struct buf * bp)20857836SJohn.Forte@Sun.COM _sv_lyr_strategy(struct buf *bp)
20867836SJohn.Forte@Sun.COM {
20877836SJohn.Forte@Sun.COM 	caddr_t buf_addr;		/* pointer to linear buffer in bp */
20887836SJohn.Forte@Sun.COM 	nsc_buf_t *bufh = NULL;
20897836SJohn.Forte@Sun.COM 	nsc_buf_t *hndl = NULL;
20907836SJohn.Forte@Sun.COM 	sv_dev_t *svp;
20917836SJohn.Forte@Sun.COM 	nsc_vec_t *v;
20927836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
20937836SJohn.Forte@Sun.COM 	nsc_size_t fba_req, fba_len;	/* FBA lengths */
20947836SJohn.Forte@Sun.COM 	nsc_off_t fba_off;		/* FBA offset */
20957836SJohn.Forte@Sun.COM 	size_t tocopy, nbytes;		/* byte lengths */
20967836SJohn.Forte@Sun.COM 	int rw, rc;			/* flags and return codes */
20977836SJohn.Forte@Sun.COM 	int (*fn)();
20987836SJohn.Forte@Sun.COM 
20997836SJohn.Forte@Sun.COM 	rc = 0;
21007836SJohn.Forte@Sun.COM 
21017836SJohn.Forte@Sun.COM 	if (sv_debug > 5)
2102*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_CONT, "!_sv_lyr_strategy(%p)\n", (void *)bp);
21037836SJohn.Forte@Sun.COM 
21047836SJohn.Forte@Sun.COM 	svp = sv_find_enabled(bp->b_edev, &maj);
21057836SJohn.Forte@Sun.COM 	if (svp == NULL) {
21067836SJohn.Forte@Sun.COM 		if (maj && (fn = maj->sm_strategy) != 0) {
21077836SJohn.Forte@Sun.COM 			if (!(maj->sm_flag & D_MP)) {
21087836SJohn.Forte@Sun.COM 				UNSAFE_ENTER();
21097836SJohn.Forte@Sun.COM 				rc = (*fn)(bp);
21107836SJohn.Forte@Sun.COM 				UNSAFE_EXIT();
21117836SJohn.Forte@Sun.COM 			} else {
21127836SJohn.Forte@Sun.COM 				rc = (*fn)(bp);
21137836SJohn.Forte@Sun.COM 			}
21147836SJohn.Forte@Sun.COM 			return;
21157836SJohn.Forte@Sun.COM 		} else {
21167836SJohn.Forte@Sun.COM 			bioerror(bp, ENODEV);
21177836SJohn.Forte@Sun.COM 			biodone(bp);
21187836SJohn.Forte@Sun.COM 			return;
21197836SJohn.Forte@Sun.COM 		}
21207836SJohn.Forte@Sun.COM 	}
21217836SJohn.Forte@Sun.COM 
21227836SJohn.Forte@Sun.COM 	ASSERT(RW_READ_HELD(&svp->sv_lock));
21237836SJohn.Forte@Sun.COM 
21247836SJohn.Forte@Sun.COM 	if (svp->sv_flag == 0) {
21257836SJohn.Forte@Sun.COM 		/*
21267836SJohn.Forte@Sun.COM 		 * guard access mode
21277836SJohn.Forte@Sun.COM 		 * - prevent user level access to the device
21287836SJohn.Forte@Sun.COM 		 */
2129*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_lyr_strategy_err_guard, struct buf *, bp);
21307836SJohn.Forte@Sun.COM 		bioerror(bp, EPERM);
21317836SJohn.Forte@Sun.COM 		goto out;
21327836SJohn.Forte@Sun.COM 	}
21337836SJohn.Forte@Sun.COM 
21347836SJohn.Forte@Sun.COM 	if ((rc = sv_reserve(svp->sv_fd, NSC_MULTI|NSC_PCATCH)) != 0) {
2135*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_lyr_strategy_err_rsrv, struct buf *, bp);
21367836SJohn.Forte@Sun.COM 
21377836SJohn.Forte@Sun.COM 		if (rc == EINTR)
2138*9093SRamana.Srikanth@Sun.COM 			cmn_err(CE_WARN, "!nsc_reserve() returned EINTR");
21397836SJohn.Forte@Sun.COM 		bioerror(bp, rc);
21407836SJohn.Forte@Sun.COM 		goto out;
21417836SJohn.Forte@Sun.COM 	}
21427836SJohn.Forte@Sun.COM 
21437836SJohn.Forte@Sun.COM 	if (bp->b_lblkno >= (diskaddr_t)svp->sv_nblocks) {
2144*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_lyr_strategy_eof, struct buf *, bp);
21457836SJohn.Forte@Sun.COM 
21467836SJohn.Forte@Sun.COM 		if (bp->b_flags & B_READ) {
21477836SJohn.Forte@Sun.COM 			/* return EOF, not an error */
21487836SJohn.Forte@Sun.COM 			bp->b_resid = bp->b_bcount;
21497836SJohn.Forte@Sun.COM 			bioerror(bp, 0);
21507836SJohn.Forte@Sun.COM 		} else
21517836SJohn.Forte@Sun.COM 			bioerror(bp, EINVAL);
21527836SJohn.Forte@Sun.COM 
21537836SJohn.Forte@Sun.COM 		goto done;
21547836SJohn.Forte@Sun.COM 	}
21557836SJohn.Forte@Sun.COM 
21567836SJohn.Forte@Sun.COM 	/*
21577836SJohn.Forte@Sun.COM 	 * Preallocate a handle once per call to strategy.
21587836SJohn.Forte@Sun.COM 	 * If this fails, then the nsc_alloc_buf() will allocate
21597836SJohn.Forte@Sun.COM 	 * a temporary handle per allocation/free pair.
21607836SJohn.Forte@Sun.COM 	 */
21617836SJohn.Forte@Sun.COM 
2162*9093SRamana.Srikanth@Sun.COM 	DTRACE_PROBE1(sv_dbg_alloch_start, sv_dev_t *, svp);
21637836SJohn.Forte@Sun.COM 
21647836SJohn.Forte@Sun.COM 	bufh = nsc_alloc_handle(svp->sv_fd, NULL, NULL, NULL);
21657836SJohn.Forte@Sun.COM 
2166*9093SRamana.Srikanth@Sun.COM 	DTRACE_PROBE1(sv_dbg_alloch_end, sv_dev_t *, svp);
21677836SJohn.Forte@Sun.COM 
21687836SJohn.Forte@Sun.COM 	if (bufh && (bufh->sb_flag & NSC_HACTIVE) != 0) {
2169*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_lyr_strategy_err_hactive, struct buf *, bp);
21707836SJohn.Forte@Sun.COM 
21717836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN,
2172*9093SRamana.Srikanth@Sun.COM 		    "!sv: allocated active handle (bufh %p, flags %x)",
2173*9093SRamana.Srikanth@Sun.COM 		    (void *)bufh, bufh->sb_flag);
21747836SJohn.Forte@Sun.COM 
21757836SJohn.Forte@Sun.COM 		bioerror(bp, ENXIO);
21767836SJohn.Forte@Sun.COM 		goto done;
21777836SJohn.Forte@Sun.COM 	}
21787836SJohn.Forte@Sun.COM 
21797836SJohn.Forte@Sun.COM 	fba_req = FBA_LEN(bp->b_bcount);
21807836SJohn.Forte@Sun.COM 	if (fba_req + bp->b_lblkno > (diskaddr_t)svp->sv_nblocks)
21817836SJohn.Forte@Sun.COM 		fba_req = (nsc_size_t)(svp->sv_nblocks - bp->b_lblkno);
21827836SJohn.Forte@Sun.COM 
21837836SJohn.Forte@Sun.COM 	rw = (bp->b_flags & B_READ) ? NSC_READ : NSC_WRITE;
21847836SJohn.Forte@Sun.COM 
21857836SJohn.Forte@Sun.COM 	bp_mapin(bp);
21867836SJohn.Forte@Sun.COM 
21877836SJohn.Forte@Sun.COM 	bp->b_resid = bp->b_bcount;
21887836SJohn.Forte@Sun.COM 	buf_addr = bp->b_un.b_addr;
21897836SJohn.Forte@Sun.COM 	fba_off = 0;
21907836SJohn.Forte@Sun.COM 
21917836SJohn.Forte@Sun.COM 	/*
21927836SJohn.Forte@Sun.COM 	 * fba_req  - requested size of transfer in FBAs after
21937836SJohn.Forte@Sun.COM 	 *		truncation to device extent, and allowing for
21947836SJohn.Forte@Sun.COM 	 *		possible non-FBA bounded final chunk.
21957836SJohn.Forte@Sun.COM 	 * fba_off  - offset of start of chunk from start of bp in FBAs.
21967836SJohn.Forte@Sun.COM 	 * fba_len  - size of this chunk in FBAs.
21977836SJohn.Forte@Sun.COM 	 */
21987836SJohn.Forte@Sun.COM 
21997836SJohn.Forte@Sun.COM loop:
22007836SJohn.Forte@Sun.COM 	fba_len = min(fba_req, svp->sv_maxfbas);
22017836SJohn.Forte@Sun.COM 	hndl = bufh;
22027836SJohn.Forte@Sun.COM 
22037836SJohn.Forte@Sun.COM 	DTRACE_PROBE4(sv_dbg_allocb_start,
22047836SJohn.Forte@Sun.COM 	    sv_dev_t *, svp,
22057836SJohn.Forte@Sun.COM 	    uint64_t, (uint64_t)(bp->b_lblkno + fba_off),
22067836SJohn.Forte@Sun.COM 	    uint64_t, (uint64_t)fba_len,
22077836SJohn.Forte@Sun.COM 	    int, rw);
22087836SJohn.Forte@Sun.COM 
22097836SJohn.Forte@Sun.COM 	rc = nsc_alloc_buf(svp->sv_fd, (nsc_off_t)(bp->b_lblkno + fba_off),
2210*9093SRamana.Srikanth@Sun.COM 	    fba_len, rw, &hndl);
2211*9093SRamana.Srikanth@Sun.COM 
2212*9093SRamana.Srikanth@Sun.COM 	DTRACE_PROBE1(sv_dbg_allocb_end, sv_dev_t *, svp);
22137836SJohn.Forte@Sun.COM 
22147836SJohn.Forte@Sun.COM 	if (rc > 0) {
2215*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_lyr_strategy_err_alloc, struct buf *, bp);
22167836SJohn.Forte@Sun.COM 		bioerror(bp, rc);
22177836SJohn.Forte@Sun.COM 		if (hndl != bufh)
22187836SJohn.Forte@Sun.COM 			(void) nsc_free_buf(hndl);
22197836SJohn.Forte@Sun.COM 		hndl = NULL;
22207836SJohn.Forte@Sun.COM 		goto done;
22217836SJohn.Forte@Sun.COM 	}
22227836SJohn.Forte@Sun.COM 
22237836SJohn.Forte@Sun.COM 	tocopy = min(FBA_SIZE(fba_len), bp->b_resid);
22247836SJohn.Forte@Sun.COM 	v = hndl->sb_vec;
22257836SJohn.Forte@Sun.COM 
22267836SJohn.Forte@Sun.COM 	if (rw == NSC_WRITE && FBA_OFF(tocopy) != 0) {
22277836SJohn.Forte@Sun.COM 		/*
22287836SJohn.Forte@Sun.COM 		 * Not overwriting all of the last FBA, so read in the
22297836SJohn.Forte@Sun.COM 		 * old contents now before we overwrite it with the new
22307836SJohn.Forte@Sun.COM 		 * data.
22317836SJohn.Forte@Sun.COM 		 */
22327836SJohn.Forte@Sun.COM 
2233*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE2(sv_dbg_read_start, sv_dev_t *, svp,
2234*9093SRamana.Srikanth@Sun.COM 		    uint64_t, (uint64_t)(hndl->sb_pos + hndl->sb_len - 1));
22357836SJohn.Forte@Sun.COM 
22367836SJohn.Forte@Sun.COM 		rc = nsc_read(hndl, (hndl->sb_pos + hndl->sb_len - 1), 1, 0);
22377836SJohn.Forte@Sun.COM 		if (rc > 0) {
22387836SJohn.Forte@Sun.COM 			bioerror(bp, rc);
22397836SJohn.Forte@Sun.COM 			goto done;
22407836SJohn.Forte@Sun.COM 		}
22417836SJohn.Forte@Sun.COM 
2242*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_dbg_read_end, sv_dev_t *, svp);
22437836SJohn.Forte@Sun.COM 	}
22447836SJohn.Forte@Sun.COM 
2245*9093SRamana.Srikanth@Sun.COM 	DTRACE_PROBE1(sv_dbg_bcopy_start, sv_dev_t *, svp);
22467836SJohn.Forte@Sun.COM 
22477836SJohn.Forte@Sun.COM 	while (tocopy > 0) {
22487836SJohn.Forte@Sun.COM 		nbytes = min(tocopy, (nsc_size_t)v->sv_len);
22497836SJohn.Forte@Sun.COM 
22507836SJohn.Forte@Sun.COM 		if (bp->b_flags & B_READ)
22517836SJohn.Forte@Sun.COM 			(void) bcopy(v->sv_addr, buf_addr, nbytes);
22527836SJohn.Forte@Sun.COM 		else
22537836SJohn.Forte@Sun.COM 			(void) bcopy(buf_addr, v->sv_addr, nbytes);
22547836SJohn.Forte@Sun.COM 
22557836SJohn.Forte@Sun.COM 		bp->b_resid -= nbytes;
22567836SJohn.Forte@Sun.COM 		buf_addr += nbytes;
22577836SJohn.Forte@Sun.COM 		tocopy -= nbytes;
22587836SJohn.Forte@Sun.COM 		v++;
22597836SJohn.Forte@Sun.COM 	}
22607836SJohn.Forte@Sun.COM 
2261*9093SRamana.Srikanth@Sun.COM 	DTRACE_PROBE1(sv_dbg_bcopy_end, sv_dev_t *, svp);
22627836SJohn.Forte@Sun.COM 
22637836SJohn.Forte@Sun.COM 	if ((bp->b_flags & B_READ) == 0) {
2264*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE3(sv_dbg_write_start, sv_dev_t *, svp,
22657836SJohn.Forte@Sun.COM 		    uint64_t, (uint64_t)hndl->sb_pos,
22667836SJohn.Forte@Sun.COM 		    uint64_t, (uint64_t)hndl->sb_len);
22677836SJohn.Forte@Sun.COM 
22687836SJohn.Forte@Sun.COM 		rc = nsc_write(hndl, hndl->sb_pos, hndl->sb_len, 0);
22697836SJohn.Forte@Sun.COM 
2270*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_dbg_write_end, sv_dev_t *, svp);
22717836SJohn.Forte@Sun.COM 
22727836SJohn.Forte@Sun.COM 		if (rc > 0) {
22737836SJohn.Forte@Sun.COM 			bioerror(bp, rc);
22747836SJohn.Forte@Sun.COM 			goto done;
22757836SJohn.Forte@Sun.COM 		}
22767836SJohn.Forte@Sun.COM 	}
22777836SJohn.Forte@Sun.COM 
22787836SJohn.Forte@Sun.COM 	/*
22797836SJohn.Forte@Sun.COM 	 * Adjust FBA offset and requested (ie. remaining) length,
22807836SJohn.Forte@Sun.COM 	 * loop if more data to transfer.
22817836SJohn.Forte@Sun.COM 	 */
22827836SJohn.Forte@Sun.COM 
22837836SJohn.Forte@Sun.COM 	fba_off += fba_len;
22847836SJohn.Forte@Sun.COM 	fba_req -= fba_len;
22857836SJohn.Forte@Sun.COM 
22867836SJohn.Forte@Sun.COM 	if (fba_req > 0) {
2287*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_dbg_freeb_start, sv_dev_t *, svp);
22887836SJohn.Forte@Sun.COM 
22897836SJohn.Forte@Sun.COM 		rc = nsc_free_buf(hndl);
22907836SJohn.Forte@Sun.COM 
2291*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_dbg_freeb_end, sv_dev_t *, svp);
22927836SJohn.Forte@Sun.COM 
22937836SJohn.Forte@Sun.COM 		if (rc > 0) {
22947836SJohn.Forte@Sun.COM 			DTRACE_PROBE1(sv_lyr_strategy_err_free,
2295*9093SRamana.Srikanth@Sun.COM 			    struct buf *, bp);
22967836SJohn.Forte@Sun.COM 			bioerror(bp, rc);
22977836SJohn.Forte@Sun.COM 		}
22987836SJohn.Forte@Sun.COM 
22997836SJohn.Forte@Sun.COM 		hndl = NULL;
23007836SJohn.Forte@Sun.COM 
23017836SJohn.Forte@Sun.COM 		if (rc <= 0)
23027836SJohn.Forte@Sun.COM 			goto loop;
23037836SJohn.Forte@Sun.COM 	}
23047836SJohn.Forte@Sun.COM 
23057836SJohn.Forte@Sun.COM done:
23067836SJohn.Forte@Sun.COM 	if (hndl != NULL) {
2307*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_dbg_freeb_start, sv_dev_t *, svp);
23087836SJohn.Forte@Sun.COM 
23097836SJohn.Forte@Sun.COM 		rc = nsc_free_buf(hndl);
23107836SJohn.Forte@Sun.COM 
2311*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_dbg_freeb_end, sv_dev_t *, svp);
23127836SJohn.Forte@Sun.COM 
23137836SJohn.Forte@Sun.COM 		if (rc > 0) {
23147836SJohn.Forte@Sun.COM 			DTRACE_PROBE1(sv_lyr_strategy_err_free,
2315*9093SRamana.Srikanth@Sun.COM 			    struct buf *, bp);
23167836SJohn.Forte@Sun.COM 			bioerror(bp, rc);
23177836SJohn.Forte@Sun.COM 		}
23187836SJohn.Forte@Sun.COM 
23197836SJohn.Forte@Sun.COM 		hndl = NULL;
23207836SJohn.Forte@Sun.COM 	}
23217836SJohn.Forte@Sun.COM 
23227836SJohn.Forte@Sun.COM 	if (bufh)
23237836SJohn.Forte@Sun.COM 		(void) nsc_free_handle(bufh);
23247836SJohn.Forte@Sun.COM 
2325*9093SRamana.Srikanth@Sun.COM 	DTRACE_PROBE1(sv_dbg_rlse_start, sv_dev_t *, svp);
23267836SJohn.Forte@Sun.COM 
23277836SJohn.Forte@Sun.COM 	nsc_release(svp->sv_fd);
23287836SJohn.Forte@Sun.COM 
2329*9093SRamana.Srikanth@Sun.COM 	DTRACE_PROBE1(sv_dbg_rlse_end, sv_dev_t *, svp);
23307836SJohn.Forte@Sun.COM 
23317836SJohn.Forte@Sun.COM out:
23327836SJohn.Forte@Sun.COM 	if (sv_debug > 5) {
23337836SJohn.Forte@Sun.COM 		cmn_err(CE_CONT,
2334*9093SRamana.Srikanth@Sun.COM 		    "!_sv_lyr_strategy: bp %p, bufh %p, bp->b_error %d\n",
23357836SJohn.Forte@Sun.COM 		    (void *)bp, (void *)bufh, bp->b_error);
23367836SJohn.Forte@Sun.COM 	}
23377836SJohn.Forte@Sun.COM 
2338*9093SRamana.Srikanth@Sun.COM 	DTRACE_PROBE2(sv_lyr_strategy_end, struct buf *, bp, int, bp->b_error);
23397836SJohn.Forte@Sun.COM 
23407836SJohn.Forte@Sun.COM 	rw_exit(&svp->sv_lock);
23417836SJohn.Forte@Sun.COM 	biodone(bp);
23427836SJohn.Forte@Sun.COM }
23437836SJohn.Forte@Sun.COM 
23447836SJohn.Forte@Sun.COM 
23457836SJohn.Forte@Sun.COM static void
sv_async_strategy(blind_t arg)23467836SJohn.Forte@Sun.COM sv_async_strategy(blind_t arg)
23477836SJohn.Forte@Sun.COM {
23487836SJohn.Forte@Sun.COM 	struct buf *bp = (struct buf *)arg;
23497836SJohn.Forte@Sun.COM 	_sv_lyr_strategy(bp);
23507836SJohn.Forte@Sun.COM }
23517836SJohn.Forte@Sun.COM 
23527836SJohn.Forte@Sun.COM 
23537836SJohn.Forte@Sun.COM static int
sv_lyr_strategy(struct buf * bp)23547836SJohn.Forte@Sun.COM sv_lyr_strategy(struct buf *bp)
23557836SJohn.Forte@Sun.COM {
23567836SJohn.Forte@Sun.COM 	nsthread_t *tp;
23577836SJohn.Forte@Sun.COM 	int nlive;
23587836SJohn.Forte@Sun.COM 
23597836SJohn.Forte@Sun.COM 	/*
23607836SJohn.Forte@Sun.COM 	 * If B_ASYNC was part of the DDI we could use it as a hint to
23617836SJohn.Forte@Sun.COM 	 * not create a thread for synchronous i/o.
23627836SJohn.Forte@Sun.COM 	 */
23637836SJohn.Forte@Sun.COM 	if (sv_dev_to_sv(bp->b_edev, NULL) == NULL) {
23647836SJohn.Forte@Sun.COM 		/* not sv enabled - just pass through */
2365*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE1(sv_lyr_strategy_notsv, struct buf *, bp);
23667836SJohn.Forte@Sun.COM 		_sv_lyr_strategy(bp);
23677836SJohn.Forte@Sun.COM 		return (0);
23687836SJohn.Forte@Sun.COM 	}
23697836SJohn.Forte@Sun.COM 
23707836SJohn.Forte@Sun.COM 	if (sv_debug > 4) {
2371*9093SRamana.Srikanth@Sun.COM 		cmn_err(CE_CONT, "!sv_lyr_strategy: nthread %d nlive %d\n",
23727836SJohn.Forte@Sun.COM 		    nst_nthread(sv_tset), nst_nlive(sv_tset));
23737836SJohn.Forte@Sun.COM 	}
23747836SJohn.Forte@Sun.COM 
23757836SJohn.Forte@Sun.COM 	/*
23767836SJohn.Forte@Sun.COM 	 * If there are only guard devices enabled there
23777836SJohn.Forte@Sun.COM 	 * won't be a threadset, so don't try and use it.
23787836SJohn.Forte@Sun.COM 	 */
23797836SJohn.Forte@Sun.COM 	tp = NULL;
23807836SJohn.Forte@Sun.COM 	if (sv_tset != NULL) {
23817836SJohn.Forte@Sun.COM 		tp = nst_create(sv_tset, sv_async_strategy, (blind_t)bp, 0);
23827836SJohn.Forte@Sun.COM 	}
23837836SJohn.Forte@Sun.COM 
23847836SJohn.Forte@Sun.COM 	if (tp == NULL) {
23857836SJohn.Forte@Sun.COM 		/*
23867836SJohn.Forte@Sun.COM 		 * out of threads, so fall back to synchronous io.
23877836SJohn.Forte@Sun.COM 		 */
23887836SJohn.Forte@Sun.COM 		if (sv_debug > 0) {
23897836SJohn.Forte@Sun.COM 			cmn_err(CE_CONT,
2390*9093SRamana.Srikanth@Sun.COM 			    "!sv_lyr_strategy: thread alloc failed\n");
23917836SJohn.Forte@Sun.COM 		}
23927836SJohn.Forte@Sun.COM 
23937836SJohn.Forte@Sun.COM 		DTRACE_PROBE1(sv_lyr_strategy_no_thread,
2394*9093SRamana.Srikanth@Sun.COM 		    struct buf *, bp);
23957836SJohn.Forte@Sun.COM 
23967836SJohn.Forte@Sun.COM 		_sv_lyr_strategy(bp);
23977836SJohn.Forte@Sun.COM 		sv_no_threads++;
23987836SJohn.Forte@Sun.COM 	} else {
23997836SJohn.Forte@Sun.COM 		nlive = nst_nlive(sv_tset);
24007836SJohn.Forte@Sun.COM 		if (nlive > sv_max_nlive) {
24017836SJohn.Forte@Sun.COM 			if (sv_debug > 0) {
24027836SJohn.Forte@Sun.COM 				cmn_err(CE_CONT,
2403*9093SRamana.Srikanth@Sun.COM 				    "!sv_lyr_strategy: "
24047836SJohn.Forte@Sun.COM 				    "new max nlive %d (nthread %d)\n",
24057836SJohn.Forte@Sun.COM 				    nlive, nst_nthread(sv_tset));
24067836SJohn.Forte@Sun.COM 			}
24077836SJohn.Forte@Sun.COM 
24087836SJohn.Forte@Sun.COM 			sv_max_nlive = nlive;
24097836SJohn.Forte@Sun.COM 		}
24107836SJohn.Forte@Sun.COM 	}
24117836SJohn.Forte@Sun.COM 
24127836SJohn.Forte@Sun.COM 	return (0);
24137836SJohn.Forte@Sun.COM }
24147836SJohn.Forte@Sun.COM 
24157836SJohn.Forte@Sun.COM 
24167836SJohn.Forte@Sun.COM #ifndef offsetof
24177836SJohn.Forte@Sun.COM #define	offsetof(s, m)	((size_t)(&((s *)0)->m))
24187836SJohn.Forte@Sun.COM #endif
24197836SJohn.Forte@Sun.COM 
24207836SJohn.Forte@Sun.COM /*
24217836SJohn.Forte@Sun.COM  * re-write the size of the current partition
24227836SJohn.Forte@Sun.COM  */
24237836SJohn.Forte@Sun.COM static int
sv_fix_dkiocgvtoc(const intptr_t arg,const int mode,sv_dev_t * svp)24247836SJohn.Forte@Sun.COM sv_fix_dkiocgvtoc(const intptr_t arg, const int mode, sv_dev_t *svp)
24257836SJohn.Forte@Sun.COM {
24267836SJohn.Forte@Sun.COM 	size_t offset;
24277836SJohn.Forte@Sun.COM 	int ilp32;
24287836SJohn.Forte@Sun.COM 	int pnum;
24297836SJohn.Forte@Sun.COM 	int rc;
24307836SJohn.Forte@Sun.COM 
24317836SJohn.Forte@Sun.COM 	ilp32 = (ddi_model_convert_from((mode & FMODELS)) == DDI_MODEL_ILP32);
24327836SJohn.Forte@Sun.COM 
24337836SJohn.Forte@Sun.COM 	rc = nskern_partition(svp->sv_dev, &pnum);
24347836SJohn.Forte@Sun.COM 	if (rc != 0) {
24357836SJohn.Forte@Sun.COM 		return (rc);
24367836SJohn.Forte@Sun.COM 	}
24377836SJohn.Forte@Sun.COM 
24387836SJohn.Forte@Sun.COM 	if (pnum < 0 || pnum >= V_NUMPAR) {
24397836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN,
2440*9093SRamana.Srikanth@Sun.COM 		    "!sv_gvtoc: unable to determine partition number "
24417836SJohn.Forte@Sun.COM 		    "for dev %lx", svp->sv_dev);
24427836SJohn.Forte@Sun.COM 		return (EINVAL);
24437836SJohn.Forte@Sun.COM 	}
24447836SJohn.Forte@Sun.COM 
24457836SJohn.Forte@Sun.COM 	if (ilp32) {
24467836SJohn.Forte@Sun.COM 		int32_t p_size;
24477836SJohn.Forte@Sun.COM 
24487836SJohn.Forte@Sun.COM #ifdef _SunOS_5_6
24497836SJohn.Forte@Sun.COM 		offset = offsetof(struct vtoc, v_part);
24507836SJohn.Forte@Sun.COM 		offset += sizeof (struct partition) * pnum;
24517836SJohn.Forte@Sun.COM 		offset += offsetof(struct partition, p_size);
24527836SJohn.Forte@Sun.COM #else
24537836SJohn.Forte@Sun.COM 		offset = offsetof(struct vtoc32, v_part);
24547836SJohn.Forte@Sun.COM 		offset += sizeof (struct partition32) * pnum;
24557836SJohn.Forte@Sun.COM 		offset += offsetof(struct partition32, p_size);
24567836SJohn.Forte@Sun.COM #endif
24577836SJohn.Forte@Sun.COM 
24587836SJohn.Forte@Sun.COM 		p_size = (int32_t)svp->sv_nblocks;
24597836SJohn.Forte@Sun.COM 		if (p_size == 0) {
24607836SJohn.Forte@Sun.COM 			if (sv_reserve(svp->sv_fd,
24617836SJohn.Forte@Sun.COM 			    NSC_MULTI|NSC_PCATCH) == 0) {
24627836SJohn.Forte@Sun.COM 				p_size = (int32_t)svp->sv_nblocks;
24637836SJohn.Forte@Sun.COM 				nsc_release(svp->sv_fd);
24647836SJohn.Forte@Sun.COM 			} else {
24657836SJohn.Forte@Sun.COM 				rc = EINTR;
24667836SJohn.Forte@Sun.COM 			}
24677836SJohn.Forte@Sun.COM 		}
24687836SJohn.Forte@Sun.COM 
24697836SJohn.Forte@Sun.COM 		if ((rc == 0) && ddi_copyout(&p_size, (void *)(arg + offset),
24707836SJohn.Forte@Sun.COM 		    sizeof (p_size), mode) != 0) {
24717836SJohn.Forte@Sun.COM 			rc = EFAULT;
24727836SJohn.Forte@Sun.COM 		}
24737836SJohn.Forte@Sun.COM 	} else {
24747836SJohn.Forte@Sun.COM 		long p_size;
24757836SJohn.Forte@Sun.COM 
24767836SJohn.Forte@Sun.COM 		offset = offsetof(struct vtoc, v_part);
24777836SJohn.Forte@Sun.COM 		offset += sizeof (struct partition) * pnum;
24787836SJohn.Forte@Sun.COM 		offset += offsetof(struct partition, p_size);
24797836SJohn.Forte@Sun.COM 
24807836SJohn.Forte@Sun.COM 		p_size = (long)svp->sv_nblocks;
24817836SJohn.Forte@Sun.COM 		if (p_size == 0) {
24827836SJohn.Forte@Sun.COM 			if (sv_reserve(svp->sv_fd,
24837836SJohn.Forte@Sun.COM 			    NSC_MULTI|NSC_PCATCH) == 0) {
24847836SJohn.Forte@Sun.COM 				p_size = (long)svp->sv_nblocks;
24857836SJohn.Forte@Sun.COM 				nsc_release(svp->sv_fd);
24867836SJohn.Forte@Sun.COM 			} else {
24877836SJohn.Forte@Sun.COM 				rc = EINTR;
24887836SJohn.Forte@Sun.COM 			}
24897836SJohn.Forte@Sun.COM 		}
24907836SJohn.Forte@Sun.COM 
24917836SJohn.Forte@Sun.COM 		if ((rc == 0) && ddi_copyout(&p_size, (void *)(arg + offset),
24927836SJohn.Forte@Sun.COM 		    sizeof (p_size), mode) != 0) {
24937836SJohn.Forte@Sun.COM 			rc = EFAULT;
24947836SJohn.Forte@Sun.COM 		}
24957836SJohn.Forte@Sun.COM 	}
24967836SJohn.Forte@Sun.COM 
24977836SJohn.Forte@Sun.COM 	return (rc);
24987836SJohn.Forte@Sun.COM }
24997836SJohn.Forte@Sun.COM 
25007836SJohn.Forte@Sun.COM 
25017836SJohn.Forte@Sun.COM #ifdef DKIOCPARTITION
25027836SJohn.Forte@Sun.COM /*
25037836SJohn.Forte@Sun.COM  * re-write the size of the current partition
25047836SJohn.Forte@Sun.COM  *
25057836SJohn.Forte@Sun.COM  * arg is dk_efi_t.
25067836SJohn.Forte@Sun.COM  *
25077836SJohn.Forte@Sun.COM  * dk_efi_t->dki_data = (void *)(uintptr_t)efi.dki_data_64;
25087836SJohn.Forte@Sun.COM  *
25097836SJohn.Forte@Sun.COM  * dk_efi_t->dki_data --> efi_gpt_t (label header)
25107836SJohn.Forte@Sun.COM  * dk_efi_t->dki_data + 1 --> efi_gpe_t[] (array of partitions)
25117836SJohn.Forte@Sun.COM  *
25127836SJohn.Forte@Sun.COM  * efi_gpt_t->efi_gpt_PartitionEntryArrayCRC32 --> CRC32 of array of parts
25137836SJohn.Forte@Sun.COM  * efi_gpt_t->efi_gpt_HeaderCRC32 --> CRC32 of header itself
25147836SJohn.Forte@Sun.COM  *
25157836SJohn.Forte@Sun.COM  * This assumes that sizeof (efi_gpt_t) is the same as the size of a
25167836SJohn.Forte@Sun.COM  * logical block on the disk.
25177836SJohn.Forte@Sun.COM  *
25187836SJohn.Forte@Sun.COM  * Everything is little endian (i.e. disk format).
25197836SJohn.Forte@Sun.COM  */
25207836SJohn.Forte@Sun.COM static int
sv_fix_dkiocgetefi(const intptr_t arg,const int mode,sv_dev_t * svp)25217836SJohn.Forte@Sun.COM sv_fix_dkiocgetefi(const intptr_t arg, const int mode, sv_dev_t *svp)
25227836SJohn.Forte@Sun.COM {
25237836SJohn.Forte@Sun.COM 	dk_efi_t efi;
25247836SJohn.Forte@Sun.COM 	efi_gpt_t gpt;
25257836SJohn.Forte@Sun.COM 	efi_gpe_t *gpe = NULL;
25267836SJohn.Forte@Sun.COM 	size_t sgpe;
25277836SJohn.Forte@Sun.COM 	uint64_t p_size;	/* virtual partition size from nsctl */
25287836SJohn.Forte@Sun.COM 	uint32_t crc;
25297836SJohn.Forte@Sun.COM 	int unparts;		/* number of parts in user's array */
25307836SJohn.Forte@Sun.COM 	int pnum;
25317836SJohn.Forte@Sun.COM 	int rc;
25327836SJohn.Forte@Sun.COM 
25337836SJohn.Forte@Sun.COM 	rc = nskern_partition(svp->sv_dev, &pnum);
25347836SJohn.Forte@Sun.COM 	if (rc != 0) {
25357836SJohn.Forte@Sun.COM 		return (rc);
25367836SJohn.Forte@Sun.COM 	}
25377836SJohn.Forte@Sun.COM 
25387836SJohn.Forte@Sun.COM 	if (pnum < 0) {
25397836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN,
2540*9093SRamana.Srikanth@Sun.COM 		    "!sv_efi: unable to determine partition number for dev %lx",
25417836SJohn.Forte@Sun.COM 		    svp->sv_dev);
25427836SJohn.Forte@Sun.COM 		return (EINVAL);
25437836SJohn.Forte@Sun.COM 	}
25447836SJohn.Forte@Sun.COM 
25457836SJohn.Forte@Sun.COM 	if (ddi_copyin((void *)arg, &efi, sizeof (efi), mode)) {
25467836SJohn.Forte@Sun.COM 		return (EFAULT);
25477836SJohn.Forte@Sun.COM 	}
25487836SJohn.Forte@Sun.COM 
25497836SJohn.Forte@Sun.COM 	efi.dki_data = (void *)(uintptr_t)efi.dki_data_64;
25507836SJohn.Forte@Sun.COM 
25517836SJohn.Forte@Sun.COM 	if (efi.dki_length < sizeof (gpt) + sizeof (gpe)) {
25527836SJohn.Forte@Sun.COM 		return (EINVAL);
25537836SJohn.Forte@Sun.COM 	}
25547836SJohn.Forte@Sun.COM 
25557836SJohn.Forte@Sun.COM 	if (ddi_copyin((void *)efi.dki_data, &gpt, sizeof (gpt), mode)) {
25567836SJohn.Forte@Sun.COM 		rc = EFAULT;
25577836SJohn.Forte@Sun.COM 		goto out;
25587836SJohn.Forte@Sun.COM 	}
25597836SJohn.Forte@Sun.COM 
25607836SJohn.Forte@Sun.COM 	if ((unparts = LE_32(gpt.efi_gpt_NumberOfPartitionEntries)) == 0)
25617836SJohn.Forte@Sun.COM 		unparts = 1;
25627836SJohn.Forte@Sun.COM 	else if (pnum >= unparts) {
25637836SJohn.Forte@Sun.COM 		cmn_err(CE_WARN,
2564*9093SRamana.Srikanth@Sun.COM 		    "!sv_efi: partition# beyond end of user array (%d >= %d)",
25657836SJohn.Forte@Sun.COM 		    pnum, unparts);
25667836SJohn.Forte@Sun.COM 		return (EINVAL);
25677836SJohn.Forte@Sun.COM 	}
25687836SJohn.Forte@Sun.COM 
25697836SJohn.Forte@Sun.COM 	sgpe = sizeof (*gpe) * unparts;
25707836SJohn.Forte@Sun.COM 	gpe = kmem_alloc(sgpe, KM_SLEEP);
25717836SJohn.Forte@Sun.COM 
25727836SJohn.Forte@Sun.COM 	if (ddi_copyin((void *)(efi.dki_data + 1), gpe, sgpe, mode)) {
25737836SJohn.Forte@Sun.COM 		rc = EFAULT;
25747836SJohn.Forte@Sun.COM 		goto out;
25757836SJohn.Forte@Sun.COM 	}
25767836SJohn.Forte@Sun.COM 
25777836SJohn.Forte@Sun.COM 	p_size = svp->sv_nblocks;
25787836SJohn.Forte@Sun.COM 	if (p_size == 0) {
25797836SJohn.Forte@Sun.COM 		if (sv_reserve(svp->sv_fd, NSC_MULTI|NSC_PCATCH) == 0) {
25807836SJohn.Forte@Sun.COM 			p_size = (diskaddr_t)svp->sv_nblocks;
25817836SJohn.Forte@Sun.COM 			nsc_release(svp->sv_fd);
25827836SJohn.Forte@Sun.COM 		} else {
25837836SJohn.Forte@Sun.COM 			rc = EINTR;
25847836SJohn.Forte@Sun.COM 		}
25857836SJohn.Forte@Sun.COM 	}
25867836SJohn.Forte@Sun.COM 
25877836SJohn.Forte@Sun.COM 	gpe[pnum].efi_gpe_EndingLBA = LE_64(
25887836SJohn.Forte@Sun.COM 	    LE_64(gpe[pnum].efi_gpe_StartingLBA) + p_size - 1);
25897836SJohn.Forte@Sun.COM 
25907836SJohn.Forte@Sun.COM 	gpt.efi_gpt_PartitionEntryArrayCRC32 = 0;
25917836SJohn.Forte@Sun.COM 	CRC32(crc, gpe, sgpe, -1U, sv_crc32_table);
25927836SJohn.Forte@Sun.COM 	gpt.efi_gpt_PartitionEntryArrayCRC32 = LE_32(~crc);
25937836SJohn.Forte@Sun.COM 
25947836SJohn.Forte@Sun.COM 	gpt.efi_gpt_HeaderCRC32 = 0;
25957836SJohn.Forte@Sun.COM 	CRC32(crc, &gpt, sizeof (gpt), -1U, sv_crc32_table);
25967836SJohn.Forte@Sun.COM 	gpt.efi_gpt_HeaderCRC32 = LE_32(~crc);
25977836SJohn.Forte@Sun.COM 
25987836SJohn.Forte@Sun.COM 	if ((rc == 0) && ddi_copyout(&gpt, efi.dki_data, sizeof (gpt), mode)) {
25997836SJohn.Forte@Sun.COM 		rc = EFAULT;
26007836SJohn.Forte@Sun.COM 		goto out;
26017836SJohn.Forte@Sun.COM 	}
26027836SJohn.Forte@Sun.COM 
26037836SJohn.Forte@Sun.COM 	if ((rc == 0) && ddi_copyout(gpe, efi.dki_data + 1, sgpe, mode)) {
26047836SJohn.Forte@Sun.COM 		rc = EFAULT;
26057836SJohn.Forte@Sun.COM 		goto out;
26067836SJohn.Forte@Sun.COM 	}
26077836SJohn.Forte@Sun.COM 
26087836SJohn.Forte@Sun.COM out:
26097836SJohn.Forte@Sun.COM 	if (gpe) {
26107836SJohn.Forte@Sun.COM 		kmem_free(gpe, sgpe);
26117836SJohn.Forte@Sun.COM 	}
26127836SJohn.Forte@Sun.COM 
26137836SJohn.Forte@Sun.COM 	return (rc);
26147836SJohn.Forte@Sun.COM }
26157836SJohn.Forte@Sun.COM 
26167836SJohn.Forte@Sun.COM 
26177836SJohn.Forte@Sun.COM /*
26187836SJohn.Forte@Sun.COM  * Re-write the size of the partition specified by p_partno
26197836SJohn.Forte@Sun.COM  *
26207836SJohn.Forte@Sun.COM  * Note that if a DKIOCPARTITION is issued to an fd opened against a
26217836SJohn.Forte@Sun.COM  * non-sv'd device, but p_partno requests the size for a different
26227836SJohn.Forte@Sun.COM  * device that is sv'd, this function will *not* be called as sv is
26237836SJohn.Forte@Sun.COM  * not interposed on the original device (the fd).
26247836SJohn.Forte@Sun.COM  *
26257836SJohn.Forte@Sun.COM  * It would not be easy to change this as we cannot get the partition
26267836SJohn.Forte@Sun.COM  * number for the non-sv'd device, so cannot compute the dev_t of the
26277836SJohn.Forte@Sun.COM  * (sv'd) p_partno device, and so cannot find out if it is sv'd or get
26287836SJohn.Forte@Sun.COM  * its size from nsctl.
26297836SJohn.Forte@Sun.COM  *
26307836SJohn.Forte@Sun.COM  * See also the "Bug 4755783" comment in sv_lyr_ioctl().
26317836SJohn.Forte@Sun.COM  */
26327836SJohn.Forte@Sun.COM static int
sv_fix_dkiocpartition(const intptr_t arg,const int mode,sv_dev_t * svp)26337836SJohn.Forte@Sun.COM sv_fix_dkiocpartition(const intptr_t arg, const int mode, sv_dev_t *svp)
26347836SJohn.Forte@Sun.COM {
26357836SJohn.Forte@Sun.COM 	struct partition64 p64;
26367836SJohn.Forte@Sun.COM 	sv_dev_t *nsvp = NULL;
26377836SJohn.Forte@Sun.COM 	diskaddr_t p_size;
26387836SJohn.Forte@Sun.COM 	minor_t nminor;
26397836SJohn.Forte@Sun.COM 	int pnum, rc;
26407836SJohn.Forte@Sun.COM 	dev_t ndev;
26417836SJohn.Forte@Sun.COM 
26427836SJohn.Forte@Sun.COM 	rc = nskern_partition(svp->sv_dev, &pnum);
26437836SJohn.Forte@Sun.COM 	if (rc != 0) {
26447836SJohn.Forte@Sun.COM 		return (rc);
26457836SJohn.Forte@Sun.COM 	}
26467836SJohn.Forte@Sun.COM 
26477836SJohn.Forte@Sun.COM 	if (ddi_copyin((void *)arg, &p64, sizeof (p64), mode)) {
26487836SJohn.Forte@Sun.COM 		return (EFAULT);
26497836SJohn.Forte@Sun.COM 	}
26507836SJohn.Forte@Sun.COM 
26517836SJohn.Forte@Sun.COM 	if (p64.p_partno != pnum) {
26527836SJohn.Forte@Sun.COM 		/* switch to requested partition, not the current one */
26537836SJohn.Forte@Sun.COM 		nminor = getminor(svp->sv_dev) + (p64.p_partno - pnum);
26547836SJohn.Forte@Sun.COM 		ndev = makedevice(getmajor(svp->sv_dev), nminor);
26557836SJohn.Forte@Sun.COM 		nsvp = sv_find_enabled(ndev, NULL);
26567836SJohn.Forte@Sun.COM 		if (nsvp == NULL) {
26577836SJohn.Forte@Sun.COM 			/* not sv device - just return */
26587836SJohn.Forte@Sun.COM 			return (0);
26597836SJohn.Forte@Sun.COM 		}
26607836SJohn.Forte@Sun.COM 
26617836SJohn.Forte@Sun.COM 		svp = nsvp;
26627836SJohn.Forte@Sun.COM 	}
26637836SJohn.Forte@Sun.COM 
26647836SJohn.Forte@Sun.COM 	p_size = svp->sv_nblocks;
26657836SJohn.Forte@Sun.COM 	if (p_size == 0) {
26667836SJohn.Forte@Sun.COM 		if (sv_reserve(svp->sv_fd, NSC_MULTI|NSC_PCATCH) == 0) {
26677836SJohn.Forte@Sun.COM 			p_size = (diskaddr_t)svp->sv_nblocks;
26687836SJohn.Forte@Sun.COM 			nsc_release(svp->sv_fd);
26697836SJohn.Forte@Sun.COM 		} else {
26707836SJohn.Forte@Sun.COM 			rc = EINTR;
26717836SJohn.Forte@Sun.COM 		}
26727836SJohn.Forte@Sun.COM 	}
26737836SJohn.Forte@Sun.COM 
26747836SJohn.Forte@Sun.COM 	if (nsvp != NULL) {
26757836SJohn.Forte@Sun.COM 		rw_exit(&nsvp->sv_lock);
26767836SJohn.Forte@Sun.COM 	}
26777836SJohn.Forte@Sun.COM 
26787836SJohn.Forte@Sun.COM 	if ((rc == 0) && ddi_copyout(&p_size,
26797836SJohn.Forte@Sun.COM 	    (void *)(arg + offsetof(struct partition64, p_size)),
26807836SJohn.Forte@Sun.COM 	    sizeof (p_size), mode) != 0) {
26817836SJohn.Forte@Sun.COM 		return (EFAULT);
26827836SJohn.Forte@Sun.COM 	}
26837836SJohn.Forte@Sun.COM 
26847836SJohn.Forte@Sun.COM 	return (rc);
26857836SJohn.Forte@Sun.COM }
26867836SJohn.Forte@Sun.COM #endif /* DKIOCPARTITION */
26877836SJohn.Forte@Sun.COM 
26887836SJohn.Forte@Sun.COM 
26897836SJohn.Forte@Sun.COM static int
sv_lyr_ioctl(const dev_t dev,const int cmd,const intptr_t arg,const int mode,cred_t * crp,int * rvalp)26907836SJohn.Forte@Sun.COM sv_lyr_ioctl(const dev_t dev, const int cmd, const intptr_t arg,
26917836SJohn.Forte@Sun.COM     const int mode, cred_t *crp, int *rvalp)
26927836SJohn.Forte@Sun.COM {
26937836SJohn.Forte@Sun.COM 	sv_dev_t *svp;
26947836SJohn.Forte@Sun.COM 	sv_maj_t *maj;
26957836SJohn.Forte@Sun.COM 	int (*fn)();
26967836SJohn.Forte@Sun.COM 	int rc = 0;
26977836SJohn.Forte@Sun.COM 
26987836SJohn.Forte@Sun.COM 	maj = 0;
26997836SJohn.Forte@Sun.COM 	fn = 0;
27007836SJohn.Forte@Sun.COM 
27017836SJohn.Forte@Sun.COM 	/*
27027836SJohn.Forte@Sun.COM 	 * If sv_mod_status is 0 or SV_PREVENT_UNLOAD, then it will continue.
27037836SJohn.Forte@Sun.COM 	 * else it means it previously was SV_PREVENT_UNLOAD, and now it's
27047836SJohn.Forte@Sun.COM 	 * SV_ALLOW_UNLOAD, expecting the driver to eventually unload.
27057836SJohn.Forte@Sun.COM 	 *
27067836SJohn.Forte@Sun.COM 	 * SV_ALLOW_UNLOAD is final state, so no need to grab sv_mutex.
27077836SJohn.Forte@Sun.COM 	 */
27087836SJohn.Forte@Sun.COM 	if (sv_mod_status == SV_ALLOW_UNLOAD) {
27097836SJohn.Forte@Sun.COM 		return (EBUSY);
27107836SJohn.Forte@Sun.COM 	}
27117836SJohn.Forte@Sun.COM 
27127836SJohn.Forte@Sun.COM 	svp = sv_find_enabled(dev, &maj);
27137836SJohn.Forte@Sun.COM 	if (svp != NULL) {
27147836SJohn.Forte@Sun.COM 		if (nskernd_isdaemon()) {
27157836SJohn.Forte@Sun.COM 			/*
27167836SJohn.Forte@Sun.COM 			 * This is nskernd which always needs to see
27177836SJohn.Forte@Sun.COM 			 * the underlying disk device accurately.
27187836SJohn.Forte@Sun.COM 			 *
27197836SJohn.Forte@Sun.COM 			 * So just pass the ioctl straight through
27207836SJohn.Forte@Sun.COM 			 * to the underlying driver as though the device
27217836SJohn.Forte@Sun.COM 			 * was not sv enabled.
27227836SJohn.Forte@Sun.COM 			 */
2723*9093SRamana.Srikanth@Sun.COM 			DTRACE_PROBE2(sv_lyr_ioctl_nskernd, sv_dev_t *, svp,
2724*9093SRamana.Srikanth@Sun.COM 			    dev_t, dev);
27257836SJohn.Forte@Sun.COM 
27267836SJohn.Forte@Sun.COM 			rw_exit(&svp->sv_lock);
27277836SJohn.Forte@Sun.COM 			svp = NULL;
27287836SJohn.Forte@Sun.COM 		} else {
27297836SJohn.Forte@Sun.COM 			ASSERT(RW_READ_HELD(&svp->sv_lock));
27307836SJohn.Forte@Sun.COM 		}
27317836SJohn.Forte@Sun.COM 	}
27327836SJohn.Forte@Sun.COM 
27337836SJohn.Forte@Sun.COM 	/*
27347836SJohn.Forte@Sun.COM 	 * We now have a locked and enabled SV device, or a non-SV device.
27357836SJohn.Forte@Sun.COM 	 */
27367836SJohn.Forte@Sun.COM 
27377836SJohn.Forte@Sun.COM 	switch (cmd) {
27387836SJohn.Forte@Sun.COM 		/*
27397836SJohn.Forte@Sun.COM 		 * DKIOCGVTOC, DKIOCSVTOC, DKIOCPARTITION, DKIOCGETEFI
27407836SJohn.Forte@Sun.COM 		 * and DKIOCSETEFI are intercepted and faked up as some
27417836SJohn.Forte@Sun.COM 		 * i/o providers emulate volumes of a different size to
27427836SJohn.Forte@Sun.COM 		 * the underlying volume.
27437836SJohn.Forte@Sun.COM 		 *
27447836SJohn.Forte@Sun.COM 		 * Setting the size by rewriting the vtoc is not permitted.
27457836SJohn.Forte@Sun.COM 		 */
27467836SJohn.Forte@Sun.COM 
27477836SJohn.Forte@Sun.COM 	case DKIOCSVTOC:
27487836SJohn.Forte@Sun.COM #ifdef DKIOCPARTITION
27497836SJohn.Forte@Sun.COM 	case DKIOCSETEFI:
27507836SJohn.Forte@Sun.COM #endif
27517836SJohn.Forte@Sun.COM 		if (svp == NULL) {
27527836SJohn.Forte@Sun.COM 			/* not intercepted -- allow ioctl through */
27537836SJohn.Forte@Sun.COM 			break;
27547836SJohn.Forte@Sun.COM 		}
27557836SJohn.Forte@Sun.COM 
27567836SJohn.Forte@Sun.COM 		rw_exit(&svp->sv_lock);
27577836SJohn.Forte@Sun.COM 
2758*9093SRamana.Srikanth@Sun.COM 		DTRACE_PROBE2(sv_lyr_ioctl_svtoc, dev_t, dev, int, EPERM);
27597836SJohn.Forte@Sun.COM 
27607836SJohn.Forte@Sun.COM 		return (EPERM);
27617836SJohn.Forte@Sun.COM 
27627836SJohn.Forte@Sun.COM 	default:
27637836SJohn.Forte@Sun.COM 		break;
27647836SJohn.Forte@Sun.COM 	}
27657836SJohn.Forte@Sun.COM 
27667836SJohn.Forte@Sun.COM 	/*
27677836SJohn.Forte@Sun.COM 	 * Pass through the real ioctl command.
27687836SJohn.Forte@Sun.COM 	 */
27697836SJohn.Forte@Sun.COM 
27707836SJohn.Forte@Sun.COM 	if (maj && (fn = maj->sm_ioctl) != 0) {
27717836SJohn.Forte@Sun.COM 		if (!(maj->sm_flag & D_MP)) {
27727836SJohn.Forte@Sun.COM 			UNSAFE_ENTER();
27737836SJohn.Forte@Sun.COM 			rc = (*fn)(dev, cmd, arg, mode, crp, rvalp);
27747836SJohn.Forte@Sun.COM 			UNSAFE_EXIT();
27757836SJohn.Forte@Sun.COM 		} else {
27767836SJohn.Forte@Sun.COM 			rc = (*fn)(dev, cmd, arg, mode, crp, rvalp);
27777836SJohn.Forte@Sun.COM 		}
27787836SJohn.Forte@Sun.COM 	} else {
27797836SJohn.Forte@Sun.COM 		rc = ENODEV;
27807836SJohn.Forte@Sun.COM 	}
27817836SJohn.Forte@Sun.COM 
27827836SJohn.Forte@Sun.COM 	/*
27837836SJohn.Forte@Sun.COM 	 * Bug 4755783
27847836SJohn.Forte@Sun.COM 	 * Fix up the size of the current partition to allow
27857836SJohn.Forte@Sun.COM 	 * for the virtual volume to be a different size to the
27867836SJohn.Forte@Sun.COM 	 * physical volume (e.g. for II compact dependent shadows).
27877836SJohn.Forte@Sun.COM 	 *
27887836SJohn.Forte@Sun.COM 	 * Note that this only attempts to fix up the current partition
27897836SJohn.Forte@Sun.COM 	 * - the one that the ioctl was issued against.  There could be
27907836SJohn.Forte@Sun.COM 	 * other sv'd partitions in the same vtoc, but we cannot tell
27917836SJohn.Forte@Sun.COM 	 * so we don't attempt to fix them up.
27927836SJohn.Forte@Sun.COM 	 */
27937836SJohn.Forte@Sun.COM 
27947836SJohn.Forte@Sun.COM 	if (svp != NULL && rc == 0) {
27957836SJohn.Forte@Sun.COM 		switch (cmd) {
27967836SJohn.Forte@Sun.COM 		case DKIOCGVTOC:
27977836SJohn.Forte@Sun.COM 			rc = sv_fix_dkiocgvtoc(arg, mode, svp);
27987836SJohn.Forte@Sun.COM 			break;
27997836SJohn.Forte@Sun.COM 
28007836SJohn.Forte@Sun.COM #ifdef DKIOCPARTITION
28017836SJohn.Forte@Sun.COM 		case DKIOCGETEFI:
28027836SJohn.Forte@Sun.COM 			rc = sv_fix_dkiocgetefi(arg, mode, svp);
28037836SJohn.Forte@Sun.COM 			break;
28047836SJohn.Forte@Sun.COM 
28057836SJohn.Forte@Sun.COM 		case DKIOCPARTITION:
28067836SJohn.Forte@Sun.COM 			rc = sv_fix_dkiocpartition(arg, mode, svp);
28077836SJohn.Forte@Sun.COM 			break;
28087836SJohn.Forte@Sun.COM #endif /* DKIOCPARTITION */
28097836SJohn.Forte@Sun.COM 		}
28107836SJohn.Forte@Sun.COM 	}
28117836SJohn.Forte@Sun.COM 
28127836SJohn.Forte@Sun.COM 	if (svp != NULL) {
28137836SJohn.Forte@Sun.COM 		rw_exit(&svp->sv_lock);
28147836SJohn.Forte@Sun.COM 	}
28157836SJohn.Forte@Sun.COM 
28167836SJohn.Forte@Sun.COM 	return (rc);
28177836SJohn.Forte@Sun.COM }
2818