xref: /onnv-gate/usr/src/uts/common/io/emul64_bsd.c (revision 11066:cebb50cbe4f9)
185Scth /*
285Scth  * CDDL HEADER START
385Scth  *
485Scth  * The contents of this file are subject to the terms of the
52314Smcneal  * Common Development and Distribution License (the "License").
62314Smcneal  * You may not use this file except in compliance with the License.
785Scth  *
885Scth  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
985Scth  * or http://www.opensolaris.org/os/licensing.
1085Scth  * See the License for the specific language governing permissions
1185Scth  * and limitations under the License.
1285Scth  *
1385Scth  * When distributing Covered Code, include this CDDL HEADER in each
1485Scth  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1585Scth  * If applicable, add the following below this CDDL HEADER, with the
1685Scth  * fields enclosed by brackets "[]" replaced with your own identifying
1785Scth  * information: Portions Copyright [yyyy] [name of copyright owner]
1885Scth  *
1985Scth  * CDDL HEADER END
2085Scth  */
2185Scth /*
22*11066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2385Scth  * Use is subject to license terms.
2485Scth  */
2585Scth 
2685Scth /*
2785Scth  * pseudo scsi disk driver
2885Scth  */
2985Scth 
3085Scth #include <sys/scsi/scsi.h>
3185Scth #include <sys/ddi.h>
3285Scth #include <sys/sunddi.h>
3385Scth #include <sys/kmem.h>
3485Scth #include <sys/taskq.h>
3585Scth #include <sys/disp.h>
3685Scth #include <sys/types.h>
3785Scth #include <sys/buf.h>
3885Scth 
3985Scth #include <sys/emul64.h>
4085Scth #include <sys/emul64cmd.h>
4185Scth #include <sys/emul64var.h>
4285Scth 
4385Scth /*
4485Scth  * Mode sense/select page control
4585Scth  */
4685Scth #define	MODE_SENSE_PC_CURRENT		0
4785Scth #define	MODE_SENSE_PC_CHANGEABLE	1
4885Scth #define	MODE_SENSE_PC_DEFAULT		2
4985Scth #define	MODE_SENSE_PC_SAVED		3
5085Scth 
5185Scth /*
5285Scth  * Byte conversion macros
5385Scth  */
5485Scth #if	defined(_BIG_ENDIAN)
5585Scth #define	ushort_to_scsi_ushort(n)	(n)
5685Scth #define	uint32_to_scsi_uint32(n)	(n)
5785Scth #define	uint64_to_scsi_uint64(n)	(n)
5885Scth #elif	defined(_LITTLE_ENDIAN)
5985Scth 
6085Scth #define	ushort_to_scsi_ushort(n)			\
6185Scth 		((((n) & 0x00ff) << 8) |		\
6285Scth 		(((n)  & 0xff00) >> 8))
6385Scth 
6485Scth #define	uint32_to_scsi_uint32(n)			\
6585Scth 		((((n) & 0x000000ff) << 24) |		\
6685Scth 		(((n)  & 0x0000ff00) << 8) |		\
6785Scth 		(((n)  & 0x00ff0000) >> 8) |		\
6885Scth 		(((n)  & 0xff000000) >> 24))
6985Scth #define	uint64_to_scsi_uint64(n)				\
7085Scth 		((((n) & 0x00000000000000ff) << 56) |           \
7185Scth 		(((n)  & 0x000000000000ff00) << 40) |           \
7285Scth 		(((n)  & 0x0000000000ff0000) << 24) |           \
7385Scth 		(((n)  & 0x00000000ff000000) << 8) |            \
7485Scth 		(((n)  & 0x000000ff00000000) >> 8) |            \
7585Scth 		(((n)  & 0x0000ff0000000000) >> 24) |           \
7685Scth 		(((n)  & 0x00ff000000000000) >> 40) |           \
7785Scth 		(((n)  & 0xff00000000000000) >> 56))
7885Scth #else
7985Scth error no _BIG_ENDIAN or _LITTLE_ENDIAN
8085Scth #endif
8185Scth #define	uint_to_byte0(n)		((n) & 0xff)
8285Scth #define	uint_to_byte1(n)		(((n)>>8) & 0xff)
8385Scth #define	uint_to_byte2(n)		(((n)>>16) & 0xff)
8485Scth #define	uint_to_byte3(n)		(((n)>>24) & 0xff)
8585Scth 
8685Scth /*
8785Scth  * struct prop_map
8885Scth  *
8985Scth  * This structure maps a property name to the place to store its value.
9085Scth  */
9185Scth struct prop_map {
9285Scth 	char 		*pm_name;	/* Name of the property. */
9385Scth 	int		*pm_value;	/* Place to store the value. */
9485Scth };
9585Scth 
9685Scth static int emul64_debug_blklist = 0;
9785Scth 
9885Scth /*
9985Scth  * Some interesting statistics.  These are protected by the
10085Scth  * emul64_stats_mutex.  It would be nice to have an ioctl to print them out,
10185Scth  * but we don't have the development time for that now.  You can at least
10285Scth  * look at them with adb.
10385Scth  */
10485Scth 
10585Scth int		emul64_collect_stats = 1; /* Collect stats if non-zero */
10685Scth kmutex_t	emul64_stats_mutex;	/* Protect these variables */
10785Scth long		emul64_nowrite_count = 0; /* # active nowrite ranges */
10885Scth static uint64_t	emul64_skipped_io = 0;	/* Skipped I/O operations, because of */
10985Scth 					/* EMUL64_WRITE_OFF. */
11085Scth static uint64_t	emul64_skipped_blk = 0;	/* Skipped blocks because of */
11185Scth 					/* EMUL64_WRITE_OFF. */
11285Scth static uint64_t	emul64_io_ops = 0;	/* Total number of I/O operations */
11385Scth 					/* including skipped and actual. */
11485Scth static uint64_t	emul64_io_blocks = 0;	/* Total number of blocks involved */
11585Scth 					/* in I/O operations. */
11685Scth static uint64_t	emul64_nonzero = 0;	/* Number of non-zero data blocks */
11785Scth 					/* currently held in memory */
11885Scth static uint64_t	emul64_max_list_length = 0; /* Maximum size of a linked */
11985Scth 					    /* list of non-zero blocks. */
12085Scth uint64_t emul64_taskq_max = 0;		/* emul64_scsi_start uses the taskq */
12185Scth 					/* mechanism to dispatch work. */
12285Scth 					/* If the number of entries in the */
12385Scth 					/* exceeds the maximum for the queue */
12485Scth 					/* the queue a 1 second delay is */
12585Scth 					/* encountered in taskq_ent_alloc. */
12685Scth 					/* This counter counts the number */
12785Scth 					/* times that this happens. */
12885Scth 
12985Scth /*
13085Scth  * Since emul64 does no physical I/O, operations that would normally be I/O
13185Scth  * intensive become CPU bound.  An example of this is RAID 5
13285Scth  * initialization.  When the kernel becomes CPU bound, it looks as if the
13385Scth  * machine is hung.
13485Scth  *
13585Scth  * To avoid this problem, we provide a function, emul64_yield_check, that does a
13685Scth  * delay from time to time to yield up the CPU.  The following variables
13785Scth  * are tunables for this algorithm.
13885Scth  *
13985Scth  *	emul64_num_delay_called	Number of times we called delay.  This is
14085Scth  *				not really a tunable.  Rather it is a
14185Scth  *				counter that provides useful information
14285Scth  *				for adjusting the tunables.
14385Scth  *	emul64_yield_length	Number of microseconds to yield the CPU.
14485Scth  *	emul64_yield_period	Number of I/O operations between yields.
14585Scth  *	emul64_yield_enable	emul64 will yield the CPU, only if this
14685Scth  *				variable contains a non-zero value.  This
14785Scth  *				allows the yield functionality to be turned
14885Scth  *				off for experimentation purposes.
14985Scth  *
15085Scth  * The value of 1000 for emul64_yield_period has been determined by
15185Scth  * experience with running the tests.
15285Scth  */
15385Scth static uint64_t		emul64_num_delay_called = 0;
15485Scth static int		emul64_yield_length = 1000;
15585Scth static int		emul64_yield_period = 1000;
15685Scth static int		emul64_yield_enable = 1;
15785Scth static kmutex_t		emul64_yield_mutex;
15885Scth static kcondvar_t 	emul64_yield_cv;
15985Scth 
16085Scth /*
16185Scth  * This array establishes a set of tunable variables that can be set by
16285Scth  * defining properties in the emul64.conf file.
16385Scth  */
16485Scth struct prop_map emul64_properties[] = {
16585Scth 	"emul64_collect_stats",		&emul64_collect_stats,
16685Scth 	"emul64_yield_length",		&emul64_yield_length,
16785Scth 	"emul64_yield_period",		&emul64_yield_period,
16885Scth 	"emul64_yield_enable",		&emul64_yield_enable,
16985Scth 	"emul64_max_task",		&emul64_max_task,
17085Scth 	"emul64_task_nthreads",		&emul64_task_nthreads
17185Scth };
17285Scth 
17385Scth static unsigned char *emul64_zeros = NULL; /* Block of 0s for comparison */
17485Scth 
17585Scth extern void emul64_check_cond(struct scsi_pkt *pkt, uchar_t key,
17685Scth 				uchar_t asc, uchar_t ascq);
17785Scth /* ncyl=250000 acyl=2 nhead=24 nsect=357 */
17885Scth uint_t dkg_rpm = 3600;
17985Scth 
18085Scth static int bsd_mode_sense_dad_mode_geometry(struct scsi_pkt *);
18185Scth static int bsd_mode_sense_dad_mode_err_recov(struct scsi_pkt *);
18285Scth static int bsd_mode_sense_modepage_disco_reco(struct scsi_pkt *);
18385Scth static int bsd_mode_sense_dad_mode_format(struct scsi_pkt *);
18485Scth static int bsd_mode_sense_dad_mode_cache(struct scsi_pkt *);
18585Scth static int bsd_readblks(struct emul64 *, ushort_t, ushort_t, diskaddr_t,
18685Scth 				int, unsigned char *);
18785Scth static int bsd_writeblks(struct emul64 *, ushort_t, ushort_t, diskaddr_t,
18885Scth 				int, unsigned char *);
18985Scth emul64_tgt_t *find_tgt(struct emul64 *, ushort_t, ushort_t);
19085Scth static blklist_t *bsd_findblk(emul64_tgt_t *, diskaddr_t, avl_index_t *);
19185Scth static void bsd_allocblk(emul64_tgt_t *, diskaddr_t, caddr_t, avl_index_t);
19285Scth static void bsd_freeblk(emul64_tgt_t *, blklist_t *);
19385Scth static void emul64_yield_check();
19485Scth static emul64_rng_overlap_t bsd_tgt_overlap(emul64_tgt_t *, diskaddr_t, int);
19585Scth 
19685Scth char *emul64_name = "emul64";
19785Scth 
19885Scth 
19985Scth /*
20085Scth  * Initialize globals in this file.
20185Scth  */
20285Scth void
emul64_bsd_init()20385Scth emul64_bsd_init()
20485Scth {
20585Scth 	emul64_zeros = (unsigned char *) kmem_zalloc(DEV_BSIZE, KM_SLEEP);
20685Scth 	mutex_init(&emul64_stats_mutex, NULL, MUTEX_DRIVER, NULL);
20785Scth 	mutex_init(&emul64_yield_mutex, NULL, MUTEX_DRIVER, NULL);
20885Scth 	cv_init(&emul64_yield_cv, NULL, CV_DRIVER, NULL);
20985Scth }
21085Scth 
21185Scth /*
21285Scth  * Clean up globals in this file.
21385Scth  */
21485Scth void
emul64_bsd_fini()21585Scth emul64_bsd_fini()
21685Scth {
21785Scth 	cv_destroy(&emul64_yield_cv);
21885Scth 	mutex_destroy(&emul64_yield_mutex);
21985Scth 	mutex_destroy(&emul64_stats_mutex);
22085Scth 	if (emul64_zeros != NULL) {
22185Scth 		kmem_free(emul64_zeros, DEV_BSIZE);
22285Scth 		emul64_zeros = NULL;
22385Scth 	}
22485Scth }
22585Scth 
22685Scth /*
22785Scth  * Attempt to get the values of the properties that are specified in the
22885Scth  * emul64_properties array.  If the property exists, copy its value to the
22985Scth  * specified location.  All the properties have been assigned default
23085Scth  * values in this driver, so if we cannot get the property that is not a
23185Scth  * problem.
23285Scth  */
23385Scth void
emul64_bsd_get_props(dev_info_t * dip)23485Scth emul64_bsd_get_props(dev_info_t *dip)
23585Scth {
23685Scth 	uint_t		count;
23785Scth 	uint_t		i;
23885Scth 	struct prop_map	*pmp;
23985Scth 	int		*properties;
24085Scth 
24185Scth 	for (pmp = emul64_properties, i = 0;
2427875SChris.Horne@Sun.COM 	    i < sizeof (emul64_properties) / sizeof (struct prop_map);
2437875SChris.Horne@Sun.COM 	    i++, pmp++) {
24485Scth 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
2457875SChris.Horne@Sun.COM 		    DDI_PROP_DONTPASS, pmp->pm_name, &properties,
2467875SChris.Horne@Sun.COM 		    &count) == DDI_PROP_SUCCESS) {
24785Scth 			if (count >= 1) {
24885Scth 				*pmp->pm_value = *properties;
24985Scth 			}
25085Scth 			ddi_prop_free((void *) properties);
25185Scth 		}
25285Scth 	}
25385Scth }
25485Scth 
25585Scth int
emul64_bsd_blkcompare(const void * a1,const void * b1)25685Scth emul64_bsd_blkcompare(const void *a1, const void *b1)
25785Scth {
25885Scth 	blklist_t	*a = (blklist_t *)a1;
25985Scth 	blklist_t	*b = (blklist_t *)b1;
26085Scth 
26185Scth 	if (a->bl_blkno < b->bl_blkno)
26285Scth 		return (-1);
26385Scth 	if (a->bl_blkno == b->bl_blkno)
26485Scth 		return (0);
26585Scth 	return (1);
26685Scth }
26785Scth 
26885Scth /* ARGSUSED 0 */
26985Scth int
bsd_scsi_start_stop_unit(struct scsi_pkt * pkt)27085Scth bsd_scsi_start_stop_unit(struct scsi_pkt *pkt)
27185Scth {
27285Scth 	return (0);
27385Scth }
27485Scth 
27585Scth /* ARGSUSED 0 */
27685Scth int
bsd_scsi_test_unit_ready(struct scsi_pkt * pkt)27785Scth bsd_scsi_test_unit_ready(struct scsi_pkt *pkt)
27885Scth {
27985Scth 	return (0);
28085Scth }
28185Scth 
28285Scth /* ARGSUSED 0 */
28385Scth int
bsd_scsi_request_sense(struct scsi_pkt * pkt)28485Scth bsd_scsi_request_sense(struct scsi_pkt *pkt)
28585Scth {
28685Scth 	return (0);
28785Scth }
28885Scth 
28985Scth int
bsd_scsi_inq_page0(struct scsi_pkt * pkt,uchar_t pqdtype)29085Scth bsd_scsi_inq_page0(struct scsi_pkt *pkt, uchar_t pqdtype)
29185Scth {
29285Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
29385Scth 
29485Scth 	if (sp->cmd_count < 6) {
29585Scth 		cmn_err(CE_CONT, "%s: bsd_scsi_inq_page0: size %d required\n",
29685Scth 		    emul64_name, 6);
29785Scth 		return (EIO);
29885Scth 	}
29985Scth 
30085Scth 	sp->cmd_addr[0] = pqdtype;	/* periph qual., dtype */
30185Scth 	sp->cmd_addr[1] = 0;		/* page code */
30285Scth 	sp->cmd_addr[2] = 0;		/* reserved */
30385Scth 	sp->cmd_addr[3] = 6 - 3;	/* length */
30485Scth 	sp->cmd_addr[4] = 0;		/* 1st page */
30585Scth 	sp->cmd_addr[5] = 0x83;		/* 2nd page */
30685Scth 
30785Scth 	pkt->pkt_resid = sp->cmd_count - 6;
30885Scth 	return (0);
30985Scth }
31085Scth 
31185Scth int
bsd_scsi_inq_page83(struct scsi_pkt * pkt,uchar_t pqdtype)31285Scth bsd_scsi_inq_page83(struct scsi_pkt *pkt, uchar_t pqdtype)
31385Scth {
31485Scth 	struct emul64		*emul64 = PKT2EMUL64(pkt);
31585Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
31685Scth 	int			instance = ddi_get_instance(emul64->emul64_dip);
31785Scth 
31885Scth 	if (sp->cmd_count < 22) {
31985Scth 		cmn_err(CE_CONT, "%s: bsd_scsi_inq_page83: size %d required\n",
32085Scth 		    emul64_name, 22);
32185Scth 		return (EIO);
32285Scth 	}
32385Scth 
32485Scth 	sp->cmd_addr[0] = pqdtype;	/* periph qual., dtype */
32585Scth 	sp->cmd_addr[1] = 0x83;		/* page code */
32685Scth 	sp->cmd_addr[2] = 0;		/* reserved */
32785Scth 	sp->cmd_addr[3] = (22 - 8) + 4;	/* length */
32885Scth 
32985Scth 	sp->cmd_addr[4] = 1;		/* code set - binary */
33085Scth 	sp->cmd_addr[5] = 3;		/* association and device ID type 3 */
33185Scth 	sp->cmd_addr[6] = 0;		/* reserved */
33285Scth 	sp->cmd_addr[7] = 22 - 8;	/* ID length */
33385Scth 
33485Scth 	sp->cmd_addr[8] = 0xde;		/* @8: identifier, byte 0 */
33585Scth 	sp->cmd_addr[9] = 0xca;
33685Scth 	sp->cmd_addr[10] = 0xde;
33785Scth 	sp->cmd_addr[11] = 0x80;
33885Scth 
33985Scth 	sp->cmd_addr[12] = 0xba;
34085Scth 	sp->cmd_addr[13] = 0xbe;
34185Scth 	sp->cmd_addr[14] = 0xab;
34285Scth 	sp->cmd_addr[15] = 0xba;
34385Scth 					/* @22: */
34485Scth 
34585Scth 	/*
34685Scth 	 * Instances seem to be assigned sequentially, so it unlikely that we
34785Scth 	 * will have more than 65535 of them.
34885Scth 	 */
34985Scth 	sp->cmd_addr[16] = uint_to_byte1(instance);
35085Scth 	sp->cmd_addr[17] = uint_to_byte0(instance);
35185Scth 	sp->cmd_addr[18] = uint_to_byte1(TGT(sp));
35285Scth 	sp->cmd_addr[19] = uint_to_byte0(TGT(sp));
35385Scth 	sp->cmd_addr[20] = uint_to_byte1(LUN(sp));
35485Scth 	sp->cmd_addr[21] = uint_to_byte0(LUN(sp));
35585Scth 
35685Scth 	pkt->pkt_resid = sp->cmd_count - 22;
35785Scth 	return (0);
35885Scth }
35985Scth 
36085Scth int
bsd_scsi_inquiry(struct scsi_pkt * pkt)36185Scth bsd_scsi_inquiry(struct scsi_pkt *pkt)
36285Scth {
36385Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
36485Scth 	union scsi_cdb		*cdb = (union scsi_cdb *)pkt->pkt_cdbp;
36585Scth 	emul64_tgt_t		*tgt;
36685Scth 	uchar_t			pqdtype;
36785Scth 	struct scsi_inquiry	inq;
36885Scth 
36985Scth 	EMUL64_MUTEX_ENTER(sp->cmd_emul64);
37085Scth 	tgt = find_tgt(sp->cmd_emul64,
37185Scth 	    pkt->pkt_address.a_target, pkt->pkt_address.a_lun);
37285Scth 	EMUL64_MUTEX_EXIT(sp->cmd_emul64);
37385Scth 
37485Scth 	if (sp->cmd_count < sizeof (inq)) {
37585Scth 		cmn_err(CE_CONT, "%s: bsd_scsi_inquiry: size %d required\n",
37685Scth 		    emul64_name, (int)sizeof (inq));
37785Scth 		return (EIO);
37885Scth 	}
37985Scth 
38085Scth 	if (cdb->cdb_opaque[1] & 0xfc) {
38185Scth 		cmn_err(CE_WARN, "%s: bsd_scsi_inquiry: 0x%x",
38285Scth 		    emul64_name, cdb->cdb_opaque[1]);
38385Scth 		emul64_check_cond(pkt, 0x5, 0x24, 0x0);	/* inv. fld in cdb */
38485Scth 		return (0);
38585Scth 	}
38685Scth 
38785Scth 	pqdtype = tgt->emul64_tgt_dtype;
38885Scth 	if (cdb->cdb_opaque[1] & 0x1) {
38985Scth 		switch (cdb->cdb_opaque[2]) {
3907875SChris.Horne@Sun.COM 		case 0x00:
3917875SChris.Horne@Sun.COM 			return (bsd_scsi_inq_page0(pkt, pqdtype));
3927875SChris.Horne@Sun.COM 		case 0x83:
3937875SChris.Horne@Sun.COM 			return (bsd_scsi_inq_page83(pkt, pqdtype));
3947875SChris.Horne@Sun.COM 		default:
3957875SChris.Horne@Sun.COM 			cmn_err(CE_WARN, "%s: bsd_scsi_inquiry: "
3967875SChris.Horne@Sun.COM 			    "unsupported 0x%x",
3977875SChris.Horne@Sun.COM 			    emul64_name, cdb->cdb_opaque[2]);
3987875SChris.Horne@Sun.COM 			return (0);
39985Scth 		}
40085Scth 	}
40185Scth 
40285Scth 	/* set up the inquiry data we return */
40385Scth 	(void) bzero((void *)&inq, sizeof (inq));
40485Scth 
40585Scth 	inq.inq_dtype = pqdtype;
40685Scth 	inq.inq_ansi = 2;
40785Scth 	inq.inq_rdf = 2;
40885Scth 	inq.inq_len = sizeof (inq) - 4;
40985Scth 	inq.inq_wbus16 = 1;
41085Scth 	inq.inq_cmdque = 1;
41185Scth 
41285Scth 	(void) bcopy(tgt->emul64_tgt_inq, inq.inq_vid,
41385Scth 	    sizeof (tgt->emul64_tgt_inq));
41485Scth 	(void) bcopy("1", inq.inq_revision, 2);
41585Scth 	(void) bcopy((void *)&inq, sp->cmd_addr, sizeof (inq));
41685Scth 
41785Scth 	pkt->pkt_resid = sp->cmd_count - sizeof (inq);
41885Scth 	return (0);
41985Scth }
42085Scth 
42185Scth /* ARGSUSED 0 */
42285Scth int
bsd_scsi_format(struct scsi_pkt * pkt)42385Scth bsd_scsi_format(struct scsi_pkt *pkt)
42485Scth {
42585Scth 	return (0);
42685Scth }
42785Scth 
42885Scth int
bsd_scsi_io(struct scsi_pkt * pkt)42985Scth bsd_scsi_io(struct scsi_pkt *pkt)
43085Scth {
43185Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
43285Scth 	union scsi_cdb		*cdb = (union scsi_cdb *)pkt->pkt_cdbp;
43385Scth 	diskaddr_t		lblkno;
43485Scth 	int			nblks;
43585Scth 
43685Scth 	switch (cdb->scc_cmd) {
43785Scth 	case SCMD_READ:
43885Scth 			lblkno = (uint32_t)GETG0ADDR(cdb);
43985Scth 			nblks = GETG0COUNT(cdb);
44085Scth 			pkt->pkt_resid = bsd_readblks(sp->cmd_emul64,
4417875SChris.Horne@Sun.COM 			    pkt->pkt_address.a_target, pkt->pkt_address.a_lun,
4427875SChris.Horne@Sun.COM 			    lblkno, nblks, sp->cmd_addr);
44385Scth 			if (emul64debug) {
44485Scth 				cmn_err(CE_CONT, "%s: bsd_scsi_io: "
44585Scth 				    "read g0 blk=%lld (0x%llx) nblks=%d\n",
44685Scth 				    emul64_name, lblkno, lblkno, nblks);
44785Scth 			}
44885Scth 		break;
44985Scth 	case SCMD_WRITE:
45085Scth 			lblkno = (uint32_t)GETG0ADDR(cdb);
45185Scth 			nblks = GETG0COUNT(cdb);
45285Scth 			pkt->pkt_resid = bsd_writeblks(sp->cmd_emul64,
4537875SChris.Horne@Sun.COM 			    pkt->pkt_address.a_target, pkt->pkt_address.a_lun,
4547875SChris.Horne@Sun.COM 			    lblkno, nblks, sp->cmd_addr);
45585Scth 			if (emul64debug) {
45685Scth 				cmn_err(CE_CONT, "%s: bsd_scsi_io: "
45785Scth 				    "write g0 blk=%lld (0x%llx) nblks=%d\n",
45885Scth 				    emul64_name, lblkno, lblkno, nblks);
45985Scth 			}
46085Scth 		break;
46185Scth 	case SCMD_READ_G1:
46285Scth 			lblkno = (uint32_t)GETG1ADDR(cdb);
46385Scth 			nblks = GETG1COUNT(cdb);
46485Scth 			pkt->pkt_resid = bsd_readblks(sp->cmd_emul64,
4657875SChris.Horne@Sun.COM 			    pkt->pkt_address.a_target, pkt->pkt_address.a_lun,
4667875SChris.Horne@Sun.COM 			    lblkno, nblks, sp->cmd_addr);
46785Scth 			if (emul64debug) {
46885Scth 				cmn_err(CE_CONT, "%s: bsd_scsi_io: "
46985Scth 				    "read g1 blk=%lld (0x%llx) nblks=%d\n",
47085Scth 				    emul64_name, lblkno, lblkno, nblks);
47185Scth 			}
47285Scth 		break;
47385Scth 	case SCMD_WRITE_G1:
47485Scth 			lblkno = (uint32_t)GETG1ADDR(cdb);
47585Scth 			nblks = GETG1COUNT(cdb);
47685Scth 			pkt->pkt_resid = bsd_writeblks(sp->cmd_emul64,
4777875SChris.Horne@Sun.COM 			    pkt->pkt_address.a_target, pkt->pkt_address.a_lun,
4787875SChris.Horne@Sun.COM 			    lblkno, nblks, sp->cmd_addr);
47985Scth 			if (emul64debug) {
48085Scth 				cmn_err(CE_CONT, "%s: bsd_scsi_io: "
48185Scth 				    "write g1 blk=%lld (0x%llx) nblks=%d\n",
48285Scth 				    emul64_name, lblkno, lblkno, nblks);
48385Scth 			}
48485Scth 		break;
48585Scth 	case SCMD_READ_G4:
48685Scth 			lblkno = GETG4ADDR(cdb);
48785Scth 			lblkno <<= 32;
48885Scth 			lblkno |= (uint32_t)GETG4ADDRTL(cdb);
48985Scth 			nblks = GETG4COUNT(cdb);
49085Scth 			pkt->pkt_resid = bsd_readblks(sp->cmd_emul64,
4917875SChris.Horne@Sun.COM 			    pkt->pkt_address.a_target, pkt->pkt_address.a_lun,
4927875SChris.Horne@Sun.COM 			    lblkno, nblks, sp->cmd_addr);
49385Scth 			if (emul64debug) {
49485Scth 				cmn_err(CE_CONT, "%s: bsd_scsi_io: "
49585Scth 				    "read g4 blk=%lld (0x%llx) nblks=%d\n",
49685Scth 				    emul64_name, lblkno, lblkno, nblks);
49785Scth 			}
49885Scth 		break;
49985Scth 	case SCMD_WRITE_G4:
50085Scth 			lblkno = GETG4ADDR(cdb);
50185Scth 			lblkno <<= 32;
50285Scth 			lblkno |= (uint32_t)GETG4ADDRTL(cdb);
50385Scth 			nblks = GETG4COUNT(cdb);
50485Scth 			pkt->pkt_resid = bsd_writeblks(sp->cmd_emul64,
5057875SChris.Horne@Sun.COM 			    pkt->pkt_address.a_target, pkt->pkt_address.a_lun,
5067875SChris.Horne@Sun.COM 			    lblkno, nblks, sp->cmd_addr);
50785Scth 			if (emul64debug) {
50885Scth 				cmn_err(CE_CONT, "%s: bsd_scsi_io: "
50985Scth 				    "write g4 blk=%lld (0x%llx) nblks=%d\n",
51085Scth 				    emul64_name, lblkno, lblkno, nblks);
51185Scth 			}
51285Scth 		break;
51385Scth 	default:
51485Scth 		cmn_err(CE_WARN, "%s: bsd_scsi_io: unhandled I/O: 0x%x",
51585Scth 		    emul64_name, cdb->scc_cmd);
51685Scth 		break;
51785Scth 	}
51885Scth 
51985Scth 	if (pkt->pkt_resid != 0)
52085Scth 		cmn_err(CE_WARN, "%s: bsd_scsi_io: "
52185Scth 		    "pkt_resid: 0x%lx, lblkno %lld, nblks %d",
52285Scth 		    emul64_name, pkt->pkt_resid, lblkno, nblks);
52385Scth 
52485Scth 	return (0);
52585Scth }
52685Scth 
52785Scth int
bsd_scsi_log_sense(struct scsi_pkt * pkt)52885Scth bsd_scsi_log_sense(struct scsi_pkt *pkt)
52985Scth {
53085Scth 	union scsi_cdb		*cdb = (union scsi_cdb *)pkt->pkt_cdbp;
53185Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
53285Scth 	int			page_code;
53385Scth 
53485Scth 	if (sp->cmd_count < 9) {
53585Scth 		cmn_err(CE_CONT, "%s: bsd_scsi_log_sense size %d required\n",
53685Scth 		    emul64_name, 9);
53785Scth 		return (EIO);
53885Scth 	}
53985Scth 
54085Scth 	page_code = cdb->cdb_opaque[2] & 0x3f;
54185Scth 	if (page_code) {
54285Scth 		cmn_err(CE_CONT, "%s: bsd_scsi_log_sense: "
54385Scth 		    "page 0x%x not supported\n", emul64_name, page_code);
54485Scth 		emul64_check_cond(pkt, 0x5, 0x24, 0x0); /* inv. fld in cdb */
54585Scth 		return (0);
54685Scth 	}
54785Scth 
54885Scth 	sp->cmd_addr[0] = 0;		/* page code */
54985Scth 	sp->cmd_addr[1] = 0;		/* reserved */
55085Scth 	sp->cmd_addr[2] = 0;		/* MSB of page length */
55185Scth 	sp->cmd_addr[3] = 8 - 3;	/* LSB of page length */
55285Scth 
55385Scth 	sp->cmd_addr[4] = 0;		/* MSB of parameter code */
55485Scth 	sp->cmd_addr[5] = 0;		/* LSB of parameter code */
55585Scth 	sp->cmd_addr[6] = 0;		/* parameter control byte */
55685Scth 	sp->cmd_addr[7] = 4 - 3;	/* parameter length */
55785Scth 	sp->cmd_addr[8] = 0x0;		/* parameter value */
55885Scth 
55985Scth 	pkt->pkt_resid = sp->cmd_count - 9;
56085Scth 	return (0);
56185Scth }
56285Scth 
56385Scth int
bsd_scsi_mode_sense(struct scsi_pkt * pkt)56485Scth bsd_scsi_mode_sense(struct scsi_pkt *pkt)
56585Scth {
56685Scth 	union scsi_cdb	*cdb = (union scsi_cdb *)pkt->pkt_cdbp;
56785Scth 	int		page_control;
56885Scth 	int		page_code;
56985Scth 	int		rval = 0;
57085Scth 
57185Scth 	switch (cdb->scc_cmd) {
57285Scth 	case SCMD_MODE_SENSE:
57385Scth 			page_code = cdb->cdb_opaque[2] & 0x3f;
57485Scth 			page_control = (cdb->cdb_opaque[2] >> 6) & 0x03;
57585Scth 			if (emul64debug) {
57685Scth 				cmn_err(CE_CONT, "%s: bsd_scsi_mode_sense: "
57785Scth 				    "page=0x%x control=0x%x nbytes=%d\n",
57885Scth 				    emul64_name, page_code, page_control,
57985Scth 				    GETG0COUNT(cdb));
58085Scth 			}
58185Scth 		break;
58285Scth 	case SCMD_MODE_SENSE_G1:
58385Scth 			page_code = cdb->cdb_opaque[2] & 0x3f;
58485Scth 			page_control = (cdb->cdb_opaque[2] >> 6) & 0x03;
58585Scth 			if (emul64debug) {
58685Scth 				cmn_err(CE_CONT, "%s: bsd_scsi_mode_sense: "
58785Scth 				    "page=0x%x control=0x%x nbytes=%d\n",
58885Scth 				    emul64_name, page_code, page_control,
58985Scth 				    GETG1COUNT(cdb));
59085Scth 			}
59185Scth 		break;
59285Scth 	default:
59385Scth 		cmn_err(CE_CONT, "%s: bsd_scsi_mode_sense: "
59485Scth 		    "cmd 0x%x not supported\n", emul64_name, cdb->scc_cmd);
59585Scth 		return (EIO);
59685Scth 	}
59785Scth 
59885Scth 	switch (page_code) {
59985Scth 	case DAD_MODE_GEOMETRY:
60085Scth 		rval = bsd_mode_sense_dad_mode_geometry(pkt);
60185Scth 		break;
60285Scth 	case DAD_MODE_ERR_RECOV:
60385Scth 		rval = bsd_mode_sense_dad_mode_err_recov(pkt);
60485Scth 		break;
60585Scth 	case MODEPAGE_DISCO_RECO:
60685Scth 		rval = bsd_mode_sense_modepage_disco_reco(pkt);
60785Scth 		break;
60885Scth 	case DAD_MODE_FORMAT:
60985Scth 		rval = bsd_mode_sense_dad_mode_format(pkt);
61085Scth 		break;
61185Scth 	case DAD_MODE_CACHE:
61285Scth 		rval = bsd_mode_sense_dad_mode_cache(pkt);
61385Scth 		break;
61485Scth 	default:
61585Scth 		cmn_err(CE_CONT, "%s: bsd_scsi_mode_sense: "
61685Scth 		    "page 0x%x not supported\n", emul64_name, page_code);
61785Scth 		rval = EIO;
61885Scth 		break;
61985Scth 	}
62085Scth 
62185Scth 	return (rval);
62285Scth }
62385Scth 
62485Scth 
62585Scth static int
bsd_mode_sense_dad_mode_geometry(struct scsi_pkt * pkt)62685Scth bsd_mode_sense_dad_mode_geometry(struct scsi_pkt *pkt)
62785Scth {
62885Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
62985Scth 	union scsi_cdb		*cdb = (union scsi_cdb *)pkt->pkt_cdbp;
63085Scth 	uchar_t			*addr = (uchar_t *)sp->cmd_addr;
63185Scth 	emul64_tgt_t		*tgt;
63285Scth 	int			page_control;
63385Scth 	struct mode_header	header;
63485Scth 	struct mode_geometry	page4;
63585Scth 	int			ncyl;
63685Scth 	int			rval = 0;
63785Scth 
63885Scth 	page_control = (cdb->cdb_opaque[2] >> 6) & 0x03;
63985Scth 
64085Scth 	if (emul64debug) {
64185Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_dad_mode_geometry: "
64285Scth 		    "pc=%d n=%d\n", emul64_name, page_control, sp->cmd_count);
64385Scth 	}
64485Scth 
64585Scth 	if (sp->cmd_count < (sizeof (header) + sizeof (page4))) {
64685Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_dad_mode_geometry: "
64785Scth 		    "size %d required\n",
64885Scth 		    emul64_name, (int)(sizeof (header) + sizeof (page4)));
64985Scth 		return (EIO);
65085Scth 	}
65185Scth 
65285Scth 	(void) bzero(&header, sizeof (header));
65385Scth 	(void) bzero(&page4, sizeof (page4));
65485Scth 
65585Scth 	header.length = sizeof (header) + sizeof (page4) - 1;
65685Scth 	header.bdesc_length = 0;
65785Scth 
65885Scth 	page4.mode_page.code = DAD_MODE_GEOMETRY;
65985Scth 	page4.mode_page.ps = 1;
66085Scth 	page4.mode_page.length = sizeof (page4) - sizeof (struct mode_page);
66185Scth 
66285Scth 	switch (page_control) {
66385Scth 	case MODE_SENSE_PC_CURRENT:
66485Scth 	case MODE_SENSE_PC_DEFAULT:
66585Scth 	case MODE_SENSE_PC_SAVED:
66685Scth 		EMUL64_MUTEX_ENTER(sp->cmd_emul64);
66785Scth 		tgt = find_tgt(sp->cmd_emul64,
66885Scth 		    pkt->pkt_address.a_target, pkt->pkt_address.a_lun);
66985Scth 		EMUL64_MUTEX_EXIT(sp->cmd_emul64);
67085Scth 		ncyl = tgt->emul64_tgt_ncyls;
67185Scth 		page4.cyl_ub = uint_to_byte2(ncyl);
67285Scth 		page4.cyl_mb = uint_to_byte1(ncyl);
67385Scth 		page4.cyl_lb = uint_to_byte0(ncyl);
67485Scth 		page4.heads = uint_to_byte0(tgt->emul64_tgt_nheads);
67585Scth 		page4.rpm = ushort_to_scsi_ushort(dkg_rpm);
67685Scth 		break;
67785Scth 	case MODE_SENSE_PC_CHANGEABLE:
67885Scth 		page4.cyl_ub = 0xff;
67985Scth 		page4.cyl_mb = 0xff;
68085Scth 		page4.cyl_lb = 0xff;
68185Scth 		page4.heads = 0xff;
68285Scth 		page4.rpm = 0xffff;
68385Scth 		break;
68485Scth 	}
68585Scth 
68685Scth 	(void) bcopy(&header, addr, sizeof (header));
68785Scth 	(void) bcopy(&page4, addr + sizeof (header), sizeof (page4));
68885Scth 
68985Scth 	pkt->pkt_resid = sp->cmd_count - sizeof (page4) - sizeof (header);
69085Scth 	rval = 0;
69185Scth 
69285Scth 	return (rval);
69385Scth }
69485Scth 
69585Scth static int
bsd_mode_sense_dad_mode_err_recov(struct scsi_pkt * pkt)69685Scth bsd_mode_sense_dad_mode_err_recov(struct scsi_pkt *pkt)
69785Scth {
69885Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
69985Scth 	union scsi_cdb		*cdb = (union scsi_cdb *)pkt->pkt_cdbp;
70085Scth 	uchar_t			*addr = (uchar_t *)sp->cmd_addr;
70185Scth 	int			page_control;
70285Scth 	struct mode_header	header;
70385Scth 	struct mode_err_recov	page1;
70485Scth 	int			rval = 0;
70585Scth 
70685Scth 	page_control = (cdb->cdb_opaque[2] >> 6) & 0x03;
70785Scth 
70885Scth 	if (emul64debug) {
70985Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_dad_mode_err_recov: "
71085Scth 		    "pc=%d n=%d\n", emul64_name, page_control, sp->cmd_count);
71185Scth 	}
71285Scth 
71385Scth 	if (sp->cmd_count < (sizeof (header) + sizeof (page1))) {
71485Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_dad_mode_err_recov: "
71585Scth 		    "size %d required\n",
71685Scth 		    emul64_name, (int)(sizeof (header) + sizeof (page1)));
71785Scth 		return (EIO);
71885Scth 	}
71985Scth 
72085Scth 	(void) bzero(&header, sizeof (header));
72185Scth 	(void) bzero(&page1, sizeof (page1));
72285Scth 
72385Scth 	header.length = sizeof (header) + sizeof (page1) - 1;
72485Scth 	header.bdesc_length = 0;
72585Scth 
72685Scth 	page1.mode_page.code = DAD_MODE_ERR_RECOV;
72785Scth 	page1.mode_page.ps = 1;
72885Scth 	page1.mode_page.length = sizeof (page1) - sizeof (struct mode_page);
72985Scth 
73085Scth 	switch (page_control) {
73185Scth 	case MODE_SENSE_PC_CURRENT:
73285Scth 	case MODE_SENSE_PC_DEFAULT:
73385Scth 	case MODE_SENSE_PC_SAVED:
73485Scth 		break;
73585Scth 	case MODE_SENSE_PC_CHANGEABLE:
73685Scth 		break;
73785Scth 	}
73885Scth 
73985Scth 	(void) bcopy(&header, addr, sizeof (header));
74085Scth 	(void) bcopy(&page1, addr + sizeof (header), sizeof (page1));
74185Scth 
74285Scth 	pkt->pkt_resid = sp->cmd_count - sizeof (page1) - sizeof (header);
74385Scth 	rval = 0;
74485Scth 
74585Scth 	return (rval);
74685Scth }
74785Scth 
74885Scth static int
bsd_mode_sense_modepage_disco_reco(struct scsi_pkt * pkt)74985Scth bsd_mode_sense_modepage_disco_reco(struct scsi_pkt *pkt)
75085Scth {
75185Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
75285Scth 	union scsi_cdb		*cdb = (union scsi_cdb *)pkt->pkt_cdbp;
75385Scth 	int			rval = 0;
75485Scth 	uchar_t			*addr = (uchar_t *)sp->cmd_addr;
75585Scth 	int			page_control;
75685Scth 	struct mode_header	header;
75785Scth 	struct mode_disco_reco	page2;
75885Scth 
75985Scth 	page_control = (cdb->cdb_opaque[2] >> 6) & 0x03;
76085Scth 
76185Scth 	if (emul64debug) {
76285Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_modepage_disco_reco: "
76385Scth 		    "pc=%d n=%d\n", emul64_name, page_control, sp->cmd_count);
76485Scth 	}
76585Scth 
76685Scth 	if (sp->cmd_count < (sizeof (header) + sizeof (page2))) {
76785Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_modepage_disco_reco: "
76885Scth 		    "size %d required\n",
76985Scth 		    emul64_name, (int)(sizeof (header) + sizeof (page2)));
77085Scth 		return (EIO);
77185Scth 	}
77285Scth 
77385Scth 	(void) bzero(&header, sizeof (header));
77485Scth 	(void) bzero(&page2, sizeof (page2));
77585Scth 
77685Scth 	header.length = sizeof (header) + sizeof (page2) - 1;
77785Scth 	header.bdesc_length = 0;
77885Scth 
77985Scth 	page2.mode_page.code = MODEPAGE_DISCO_RECO;
78085Scth 	page2.mode_page.ps = 1;
78185Scth 	page2.mode_page.length = sizeof (page2) - sizeof (struct mode_page);
78285Scth 
78385Scth 	switch (page_control) {
78485Scth 	case MODE_SENSE_PC_CURRENT:
78585Scth 	case MODE_SENSE_PC_DEFAULT:
78685Scth 	case MODE_SENSE_PC_SAVED:
78785Scth 		break;
78885Scth 	case MODE_SENSE_PC_CHANGEABLE:
78985Scth 		break;
79085Scth 	}
79185Scth 
79285Scth 	(void) bcopy(&header, addr, sizeof (header));
79385Scth 	(void) bcopy(&page2, addr + sizeof (header), sizeof (page2));
79485Scth 
79585Scth 	pkt->pkt_resid = sp->cmd_count - sizeof (page2) - sizeof (header);
79685Scth 	rval = 0;
79785Scth 
79885Scth 	return (rval);
79985Scth }
80085Scth 
80185Scth static int
bsd_mode_sense_dad_mode_format(struct scsi_pkt * pkt)80285Scth bsd_mode_sense_dad_mode_format(struct scsi_pkt *pkt)
80385Scth {
80485Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
80585Scth 	union scsi_cdb		*cdb = (union scsi_cdb *)pkt->pkt_cdbp;
80685Scth 	uchar_t			*addr = (uchar_t *)sp->cmd_addr;
80785Scth 	emul64_tgt_t		*tgt;
80885Scth 	int			page_control;
80985Scth 	struct mode_header	header;
81085Scth 	struct mode_format	page3;
81185Scth 	int			rval = 0;
81285Scth 
81385Scth 	page_control = (cdb->cdb_opaque[2] >> 6) & 0x03;
81485Scth 
81585Scth 	if (emul64debug) {
81685Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_dad_mode_format: "
81785Scth 		    "pc=%d n=%d\n", emul64_name, page_control, sp->cmd_count);
81885Scth 	}
81985Scth 
82085Scth 	if (sp->cmd_count < (sizeof (header) + sizeof (page3))) {
82185Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_dad_mode_format: "
82285Scth 		    "size %d required\n",
82385Scth 		    emul64_name, (int)(sizeof (header) + sizeof (page3)));
82485Scth 		return (EIO);
82585Scth 	}
82685Scth 
82785Scth 	(void) bzero(&header, sizeof (header));
82885Scth 	(void) bzero(&page3, sizeof (page3));
82985Scth 
83085Scth 	header.length = sizeof (header) + sizeof (page3) - 1;
83185Scth 	header.bdesc_length = 0;
83285Scth 
83385Scth 	page3.mode_page.code = DAD_MODE_FORMAT;
83485Scth 	page3.mode_page.ps = 1;
83585Scth 	page3.mode_page.length = sizeof (page3) - sizeof (struct mode_page);
83685Scth 
83785Scth 	switch (page_control) {
83885Scth 	case MODE_SENSE_PC_CURRENT:
83985Scth 	case MODE_SENSE_PC_DEFAULT:
84085Scth 	case MODE_SENSE_PC_SAVED:
84185Scth 		page3.data_bytes_sect = ushort_to_scsi_ushort(DEV_BSIZE);
84285Scth 		page3.interleave = ushort_to_scsi_ushort(1);
84385Scth 		EMUL64_MUTEX_ENTER(sp->cmd_emul64);
84485Scth 		tgt = find_tgt(sp->cmd_emul64,
8457875SChris.Horne@Sun.COM 		    pkt->pkt_address.a_target, pkt->pkt_address.a_lun);
84685Scth 		EMUL64_MUTEX_EXIT(sp->cmd_emul64);
84785Scth 		page3.sect_track = ushort_to_scsi_ushort(tgt->emul64_tgt_nsect);
84885Scth 		break;
84985Scth 	case MODE_SENSE_PC_CHANGEABLE:
85085Scth 		break;
85185Scth 	}
85285Scth 
85385Scth 	(void) bcopy(&header, addr, sizeof (header));
85485Scth 	(void) bcopy(&page3, addr + sizeof (header), sizeof (page3));
85585Scth 
85685Scth 	pkt->pkt_resid = sp->cmd_count - sizeof (page3) - sizeof (header);
85785Scth 	rval = 0;
85885Scth 
85985Scth 	return (rval);
86085Scth }
86185Scth 
86285Scth static int
bsd_mode_sense_dad_mode_cache(struct scsi_pkt * pkt)86385Scth bsd_mode_sense_dad_mode_cache(struct scsi_pkt *pkt)
86485Scth {
86585Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
86685Scth 	union scsi_cdb		*cdb = (union scsi_cdb *)pkt->pkt_cdbp;
86785Scth 	uchar_t			*addr = (uchar_t *)sp->cmd_addr;
86885Scth 	int			page_control;
86985Scth 	struct mode_header	header;
87085Scth 	struct mode_cache	page8;
87185Scth 	int			rval = 0;
87285Scth 
87385Scth 	page_control = (cdb->cdb_opaque[2] >> 6) & 0x03;
87485Scth 
87585Scth 	if (emul64debug) {
87685Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_dad_mode_cache: "
87785Scth 		    "pc=%d n=%d\n", emul64_name, page_control, sp->cmd_count);
87885Scth 	}
87985Scth 
88085Scth 	if (sp->cmd_count < (sizeof (header) + sizeof (page8))) {
88185Scth 		cmn_err(CE_CONT, "%s: bsd_mode_sense_dad_mode_cache: "
88285Scth 		    "size %d required\n",
88385Scth 		    emul64_name, (int)(sizeof (header) + sizeof (page8)));
88485Scth 		return (EIO);
88585Scth 	}
88685Scth 
88785Scth 	(void) bzero(&header, sizeof (header));
88885Scth 	(void) bzero(&page8, sizeof (page8));
88985Scth 
89085Scth 	header.length = sizeof (header) + sizeof (page8) - 1;
89185Scth 	header.bdesc_length = 0;
89285Scth 
89385Scth 	page8.mode_page.code = DAD_MODE_CACHE;
89485Scth 	page8.mode_page.ps = 1;
89585Scth 	page8.mode_page.length = sizeof (page8) - sizeof (struct mode_page);
89685Scth 
89785Scth 	switch (page_control) {
89885Scth 	case MODE_SENSE_PC_CURRENT:
89985Scth 	case MODE_SENSE_PC_DEFAULT:
90085Scth 	case MODE_SENSE_PC_SAVED:
90185Scth 		break;
90285Scth 	case MODE_SENSE_PC_CHANGEABLE:
90385Scth 		break;
90485Scth 	}
90585Scth 
90685Scth 	(void) bcopy(&header, addr, sizeof (header));
90785Scth 	(void) bcopy(&page8, addr + sizeof (header), sizeof (page8));
90885Scth 
90985Scth 	pkt->pkt_resid = sp->cmd_count - sizeof (page8) - sizeof (header);
91085Scth 	rval = 0;
91185Scth 
91285Scth 	return (rval);
91385Scth }
91485Scth 
91585Scth /* ARGSUSED 0 */
91685Scth int
bsd_scsi_mode_select(struct scsi_pkt * pkt)91785Scth bsd_scsi_mode_select(struct scsi_pkt *pkt)
91885Scth {
91985Scth 	return (0);
92085Scth }
92185Scth 
92285Scth int
bsd_scsi_read_capacity_8(struct scsi_pkt * pkt)92385Scth bsd_scsi_read_capacity_8(struct scsi_pkt *pkt)
92485Scth {
92585Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
92685Scth 	emul64_tgt_t		*tgt;
92785Scth 	struct scsi_capacity	cap;
92885Scth 	int			rval = 0;
92985Scth 
93085Scth 	EMUL64_MUTEX_ENTER(sp->cmd_emul64);
93185Scth 	tgt = find_tgt(sp->cmd_emul64,
9327875SChris.Horne@Sun.COM 	    pkt->pkt_address.a_target, pkt->pkt_address.a_lun);
93385Scth 	EMUL64_MUTEX_EXIT(sp->cmd_emul64);
93485Scth 	if (tgt->emul64_tgt_sectors > 0xffffffff)
93585Scth 		cap.capacity = 0xffffffff;
93685Scth 	else
93785Scth 		cap.capacity =
93885Scth 		    uint32_to_scsi_uint32(tgt->emul64_tgt_sectors);
93985Scth 	cap.lbasize = uint32_to_scsi_uint32((uint_t)DEV_BSIZE);
94085Scth 
94185Scth 	pkt->pkt_resid = sp->cmd_count - sizeof (struct scsi_capacity);
94285Scth 
94385Scth 	(void) bcopy(&cap, (caddr_t)sp->cmd_addr,
9447875SChris.Horne@Sun.COM 	    sizeof (struct scsi_capacity));
94585Scth 	return (rval);
94685Scth }
94785Scth 
94885Scth int
bsd_scsi_read_capacity_16(struct scsi_pkt * pkt)94985Scth bsd_scsi_read_capacity_16(struct scsi_pkt *pkt)
95085Scth {
95185Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
95285Scth 	emul64_tgt_t		*tgt;
95385Scth 	struct scsi_capacity_16 cap;
95485Scth 	int			rval = 0;
95585Scth 
95685Scth 	EMUL64_MUTEX_ENTER(sp->cmd_emul64);
95785Scth 	tgt = find_tgt(sp->cmd_emul64,
9587875SChris.Horne@Sun.COM 	    pkt->pkt_address.a_target, pkt->pkt_address.a_lun);
95985Scth 	EMUL64_MUTEX_EXIT(sp->cmd_emul64);
96085Scth 
96185Scth 	cap.sc_capacity = uint64_to_scsi_uint64(tgt->emul64_tgt_sectors);
96285Scth 	cap.sc_lbasize = uint32_to_scsi_uint32((uint_t)DEV_BSIZE);
96385Scth 	cap.sc_rto_en = 0;
96485Scth 	cap.sc_prot_en = 0;
96585Scth 	cap.sc_rsvd0 = 0;
96685Scth 	bzero(&cap.sc_rsvd1[0], sizeof (cap.sc_rsvd1));
96785Scth 
96885Scth 	pkt->pkt_resid = sp->cmd_count - sizeof (struct scsi_capacity_16);
96985Scth 
97085Scth 	(void) bcopy(&cap, (caddr_t)sp->cmd_addr,
9717875SChris.Horne@Sun.COM 	    sizeof (struct scsi_capacity_16));
97285Scth 	return (rval);
97385Scth }
97485Scth int
bsd_scsi_read_capacity(struct scsi_pkt * pkt)97585Scth bsd_scsi_read_capacity(struct scsi_pkt *pkt)
97685Scth {
97785Scth 	return (bsd_scsi_read_capacity_8(pkt));
97885Scth }
97985Scth 
98085Scth 
98185Scth /* ARGSUSED 0 */
98285Scth int
bsd_scsi_reserve(struct scsi_pkt * pkt)98385Scth bsd_scsi_reserve(struct scsi_pkt *pkt)
98485Scth {
98585Scth 	return (0);
98685Scth }
98785Scth 
98885Scth /* ARGSUSED 0 */
98985Scth int
bsd_scsi_release(struct scsi_pkt * pkt)99085Scth bsd_scsi_release(struct scsi_pkt *pkt)
99185Scth {
99285Scth 	return (0);
99385Scth }
99485Scth 
99585Scth 
99685Scth int
bsd_scsi_read_defect_list(struct scsi_pkt * pkt)99785Scth bsd_scsi_read_defect_list(struct scsi_pkt *pkt)
99885Scth {
99985Scth 	pkt->pkt_resid = 0;
100085Scth 	return (0);
100185Scth }
100285Scth 
100385Scth 
100485Scth /* ARGSUSED 0 */
100585Scth int
bsd_scsi_reassign_block(struct scsi_pkt * pkt)100685Scth bsd_scsi_reassign_block(struct scsi_pkt *pkt)
100785Scth {
100885Scth 	return (0);
100985Scth }
101085Scth 
101185Scth 
101285Scth static int
bsd_readblks(struct emul64 * emul64,ushort_t target,ushort_t lun,diskaddr_t blkno,int nblks,unsigned char * bufaddr)10137875SChris.Horne@Sun.COM bsd_readblks(struct emul64 *emul64, ushort_t target, ushort_t lun,
10147875SChris.Horne@Sun.COM     diskaddr_t blkno, int nblks, unsigned char *bufaddr)
101585Scth {
101685Scth 	emul64_tgt_t	*tgt;
101785Scth 	blklist_t	*blk;
101885Scth 	emul64_rng_overlap_t overlap;
101985Scth 	int		i = 0;
102085Scth 
102185Scth 	if (emul64debug) {
102285Scth 		cmn_err(CE_CONT, "%s: bsd_readblks: "
102385Scth 		    "<%d,%d> blk %llu (0x%llx) nblks %d\n",
10247875SChris.Horne@Sun.COM 		    emul64_name, target, lun, blkno, blkno, nblks);
102585Scth 	}
102685Scth 
102785Scth 	emul64_yield_check();
102885Scth 
102985Scth 	EMUL64_MUTEX_ENTER(emul64);
10307875SChris.Horne@Sun.COM 	tgt = find_tgt(emul64, target, lun);
103185Scth 	EMUL64_MUTEX_EXIT(emul64);
103285Scth 	if (tgt == NULL) {
103385Scth 		cmn_err(CE_WARN, "%s: bsd_readblks: no target for %d,%d\n",
10347875SChris.Horne@Sun.COM 		    emul64_name, target, lun);
103585Scth 		goto unlocked_out;
103685Scth 	}
103785Scth 
103885Scth 	if (emul64_collect_stats) {
103985Scth 		mutex_enter(&emul64_stats_mutex);
104085Scth 		emul64_io_ops++;
104185Scth 		emul64_io_blocks += nblks;
104285Scth 		mutex_exit(&emul64_stats_mutex);
104385Scth 	}
104485Scth 	mutex_enter(&tgt->emul64_tgt_blk_lock);
104585Scth 
104685Scth 	/*
104785Scth 	 * Keep the ioctls from changing the nowrite list for the duration
104885Scth 	 * of this I/O by grabbing emul64_tgt_nw_lock.  This will keep the
104985Scth 	 * results from our call to bsd_tgt_overlap from changing while we
105085Scth 	 * do the I/O.
105185Scth 	 */
105285Scth 	rw_enter(&tgt->emul64_tgt_nw_lock, RW_READER);
105385Scth 
105485Scth 	overlap = bsd_tgt_overlap(tgt, blkno, nblks);
105585Scth 	switch (overlap) {
105685Scth 	case O_SAME:
105785Scth 	case O_SUBSET:
105885Scth 	case O_OVERLAP:
105985Scth 		cmn_err(CE_WARN, "%s: bsd_readblks: "
106085Scth 		    "read to blocked area %lld,%d\n",
106185Scth 		    emul64_name, blkno, nblks);
106285Scth 		rw_exit(&tgt->emul64_tgt_nw_lock);
106385Scth 		goto errout;
106485Scth 	case O_NONE:
106585Scth 		break;
106685Scth 	}
106785Scth 	for (i = 0; i < nblks; i++) {
106885Scth 		if (emul64_debug_blklist)
106985Scth 			cmn_err(CE_CONT, "%s: bsd_readblks: "
107085Scth 			    "%d of %d: blkno %lld\n",
107185Scth 			    emul64_name, i+1, nblks, blkno);
107285Scth 		if (blkno > tgt->emul64_tgt_sectors)
107385Scth 			break;
107485Scth 		blk = bsd_findblk(tgt, blkno, NULL);
107585Scth 		if (blk) {
107685Scth 			(void) bcopy(blk->bl_data, bufaddr, DEV_BSIZE);
107785Scth 		} else {
107885Scth 			(void) bzero(bufaddr, DEV_BSIZE);
107985Scth 		}
108085Scth 		blkno++;
108185Scth 		bufaddr += DEV_BSIZE;
108285Scth 	}
108385Scth 	rw_exit(&tgt->emul64_tgt_nw_lock);
108485Scth 
108585Scth errout:
108685Scth 	mutex_exit(&tgt->emul64_tgt_blk_lock);
108785Scth 
108885Scth unlocked_out:
108985Scth 	return ((nblks - i) * DEV_BSIZE);
109085Scth }
109185Scth 
109285Scth 
109385Scth static int
bsd_writeblks(struct emul64 * emul64,ushort_t target,ushort_t lun,diskaddr_t blkno,int nblks,unsigned char * bufaddr)10947875SChris.Horne@Sun.COM bsd_writeblks(struct emul64 *emul64, ushort_t target, ushort_t lun,
10957875SChris.Horne@Sun.COM     diskaddr_t blkno, int nblks, unsigned char *bufaddr)
109685Scth {
109785Scth 	emul64_tgt_t	*tgt;
109885Scth 	blklist_t	*blk;
109985Scth 	emul64_rng_overlap_t overlap;
110085Scth 	avl_index_t	where;
110185Scth 	int		i = 0;
110285Scth 
110385Scth 	if (emul64debug) {
110485Scth 		cmn_err(CE_CONT, "%s: bsd_writeblks: "
110585Scth 		    "<%d,%d> blk %llu (0x%llx) nblks %d\n",
11067875SChris.Horne@Sun.COM 		    emul64_name, target, lun, blkno, blkno, nblks);
110785Scth 	}
110885Scth 
110985Scth 	emul64_yield_check();
111085Scth 
111185Scth 	EMUL64_MUTEX_ENTER(emul64);
11127875SChris.Horne@Sun.COM 	tgt = find_tgt(emul64, target, lun);
111385Scth 	EMUL64_MUTEX_EXIT(emul64);
111485Scth 	if (tgt == NULL) {
111585Scth 		cmn_err(CE_WARN, "%s: bsd_writeblks: no target for %d,%d\n",
11167875SChris.Horne@Sun.COM 		    emul64_name, target, lun);
111785Scth 		goto unlocked_out;
111885Scth 	}
111985Scth 
112085Scth 	if (emul64_collect_stats) {
112185Scth 		mutex_enter(&emul64_stats_mutex);
112285Scth 		emul64_io_ops++;
112385Scth 		emul64_io_blocks += nblks;
112485Scth 		mutex_exit(&emul64_stats_mutex);
112585Scth 	}
112685Scth 	mutex_enter(&tgt->emul64_tgt_blk_lock);
112785Scth 
112885Scth 	/*
112985Scth 	 * Keep the ioctls from changing the nowrite list for the duration
113085Scth 	 * of this I/O by grabbing emul64_tgt_nw_lock.  This will keep the
113185Scth 	 * results from our call to bsd_tgt_overlap from changing while we
113285Scth 	 * do the I/O.
113385Scth 	 */
113485Scth 	rw_enter(&tgt->emul64_tgt_nw_lock, RW_READER);
113585Scth 	overlap = bsd_tgt_overlap(tgt, blkno, nblks);
113685Scth 	switch (overlap) {
113785Scth 	case O_SAME:
113885Scth 	case O_SUBSET:
113985Scth 		if (emul64_collect_stats) {
114085Scth 			mutex_enter(&emul64_stats_mutex);
114185Scth 			emul64_skipped_io++;
114285Scth 			emul64_skipped_blk += nblks;
114385Scth 			mutex_exit(&emul64_stats_mutex);
114485Scth 		}
114585Scth 		rw_exit(&tgt->emul64_tgt_nw_lock);
114685Scth 		mutex_exit(&tgt->emul64_tgt_blk_lock);
114785Scth 		return (0);
114885Scth 	case O_OVERLAP:
114985Scth 	case O_NONE:
115085Scth 		break;
115185Scth 	}
115285Scth 	for (i = 0; i < nblks; i++) {
115385Scth 		if ((overlap == O_NONE) ||
115485Scth 		    (bsd_tgt_overlap(tgt, blkno, 1) == O_NONE)) {
115585Scth 			/*
115685Scth 			 * If there was no overlap for the entire I/O range
115785Scth 			 * or if there is no overlap for this particular
115885Scth 			 * block, then we need to do the write.
115985Scth 			 */
116085Scth 			if (emul64_debug_blklist)
116185Scth 				cmn_err(CE_CONT, "%s: bsd_writeblks: "
116285Scth 				    "%d of %d: blkno %lld\n",
116385Scth 				    emul64_name, i+1, nblks, blkno);
116485Scth 			if (blkno > tgt->emul64_tgt_sectors) {
116585Scth 				cmn_err(CE_WARN, "%s: bsd_writeblks: "
116685Scth 				    "blkno %lld, tgt_sectors %lld\n",
116785Scth 				    emul64_name, blkno,
116885Scth 				    tgt->emul64_tgt_sectors);
116985Scth 				break;
117085Scth 			}
117185Scth 
117285Scth 			blk = bsd_findblk(tgt, blkno, &where);
117385Scth 			if (bcmp(bufaddr, emul64_zeros, DEV_BSIZE) == 0) {
117485Scth 				if (blk) {
117585Scth 					bsd_freeblk(tgt, blk);
117685Scth 				}
117785Scth 			} else {
117885Scth 				if (blk) {
117985Scth 					(void) bcopy(bufaddr, blk->bl_data,
11807875SChris.Horne@Sun.COM 					    DEV_BSIZE);
118185Scth 				} else {
11827875SChris.Horne@Sun.COM 					bsd_allocblk(tgt, blkno,
11837875SChris.Horne@Sun.COM 					    (caddr_t)bufaddr, where);
118485Scth 				}
118585Scth 			}
118685Scth 		}
118785Scth 		blkno++;
118885Scth 		bufaddr += DEV_BSIZE;
118985Scth 	}
119085Scth 
119185Scth 	/*
119285Scth 	 * Now that we're done with our I/O, allow the ioctls to change the
119385Scth 	 * nowrite list.
119485Scth 	 */
119585Scth 	rw_exit(&tgt->emul64_tgt_nw_lock);
119685Scth 
119785Scth errout:
119885Scth 	mutex_exit(&tgt->emul64_tgt_blk_lock);
119985Scth 
120085Scth unlocked_out:
120185Scth 	return ((nblks - i) * DEV_BSIZE);
120285Scth }
120385Scth 
120485Scth emul64_tgt_t *
find_tgt(struct emul64 * emul64,ushort_t target,ushort_t lun)12057875SChris.Horne@Sun.COM find_tgt(struct emul64 *emul64, ushort_t target, ushort_t lun)
120685Scth {
120785Scth 	emul64_tgt_t	*tgt;
120885Scth 
120985Scth 	tgt = emul64->emul64_tgt;
121085Scth 	while (tgt) {
12117875SChris.Horne@Sun.COM 		if (tgt->emul64_tgt_saddr.a_target == target &&
12127875SChris.Horne@Sun.COM 		    tgt->emul64_tgt_saddr.a_lun == lun) {
121385Scth 			break;
121485Scth 		}
121585Scth 		tgt = tgt->emul64_tgt_next;
121685Scth 	}
121785Scth 	return (tgt);
121885Scth 
121985Scth }
122085Scth 
122185Scth /*
122285Scth  * Free all blocks that are part of the specified range.
122385Scth  */
122485Scth int
bsd_freeblkrange(emul64_tgt_t * tgt,emul64_range_t * range)122585Scth bsd_freeblkrange(emul64_tgt_t *tgt, emul64_range_t *range)
122685Scth {
122785Scth 	blklist_t	*blk;
122885Scth 	blklist_t	*nextblk;
122985Scth 
123085Scth 	ASSERT(mutex_owned(&tgt->emul64_tgt_blk_lock));
123185Scth 	for (blk = (blklist_t *)avl_first(&tgt->emul64_tgt_data);
12327875SChris.Horne@Sun.COM 	    blk != NULL;
12337875SChris.Horne@Sun.COM 	    blk = nextblk) {
123485Scth 		/*
123585Scth 		 * We need to get the next block pointer now, because blk
123685Scth 		 * will be freed inside the if statement.
123785Scth 		 */
123885Scth 		nextblk = AVL_NEXT(&tgt->emul64_tgt_data, blk);
123985Scth 
124085Scth 		if (emul64_overlap(range, blk->bl_blkno, (size_t)1) != O_NONE) {
124185Scth 			bsd_freeblk(tgt, blk);
124285Scth 		}
124385Scth 	}
124485Scth 	return (0);
124585Scth }
124685Scth 
124785Scth static blklist_t *
bsd_findblk(emul64_tgt_t * tgt,diskaddr_t blkno,avl_index_t * where)124885Scth bsd_findblk(emul64_tgt_t *tgt, diskaddr_t blkno, avl_index_t *where)
124985Scth {
125085Scth 	blklist_t	*blk;
125185Scth 	blklist_t	search;
125285Scth 
125385Scth 	ASSERT(mutex_owned(&tgt->emul64_tgt_blk_lock));
125485Scth 
125585Scth 	search.bl_blkno = blkno;
125685Scth 	blk = (blklist_t *)avl_find(&tgt->emul64_tgt_data, &search, where);
125785Scth 	return (blk);
125885Scth }
125985Scth 
126085Scth 
126185Scth static void
bsd_allocblk(emul64_tgt_t * tgt,diskaddr_t blkno,caddr_t data,avl_index_t where)126285Scth bsd_allocblk(emul64_tgt_t *tgt,
126385Scth 		diskaddr_t blkno,
126485Scth 		caddr_t data,
126585Scth 		avl_index_t where)
126685Scth {
126785Scth 	blklist_t	*blk;
126885Scth 
126985Scth 	if (emul64_debug_blklist)
127085Scth 		cmn_err(CE_CONT, "%s: bsd_allocblk: %llu\n",
127185Scth 		    emul64_name, blkno);
127285Scth 
127385Scth 	ASSERT(mutex_owned(&tgt->emul64_tgt_blk_lock));
127485Scth 
127585Scth 	blk = (blklist_t *)kmem_zalloc(sizeof (blklist_t), KM_SLEEP);
127685Scth 	blk->bl_data = (uchar_t *)kmem_zalloc(DEV_BSIZE, KM_SLEEP);
127785Scth 	blk->bl_blkno = blkno;
127885Scth 	(void) bcopy(data, blk->bl_data, DEV_BSIZE);
127985Scth 	avl_insert(&tgt->emul64_tgt_data, (void *) blk, where);
128085Scth 
128185Scth 	if (emul64_collect_stats) {
128285Scth 		mutex_enter(&emul64_stats_mutex);
128385Scth 		emul64_nonzero++;
128485Scth 		tgt->emul64_list_length++;
128585Scth 		if (tgt->emul64_list_length > emul64_max_list_length) {
128685Scth 			emul64_max_list_length = tgt->emul64_list_length;
128785Scth 		}
128885Scth 		mutex_exit(&emul64_stats_mutex);
128985Scth 	}
129085Scth }
129185Scth 
129285Scth static void
bsd_freeblk(emul64_tgt_t * tgt,blklist_t * blk)129385Scth bsd_freeblk(emul64_tgt_t *tgt, blklist_t *blk)
129485Scth {
129585Scth 	if (emul64_debug_blklist)
129685Scth 		cmn_err(CE_CONT, "%s: bsd_freeblk: <%d,%d> blk=%lld\n",
129785Scth 		    emul64_name, tgt->emul64_tgt_saddr.a_target,
129885Scth 		    tgt->emul64_tgt_saddr.a_lun, blk->bl_blkno);
129985Scth 
130085Scth 	ASSERT(mutex_owned(&tgt->emul64_tgt_blk_lock));
130185Scth 
130285Scth 	avl_remove(&tgt->emul64_tgt_data, (void *) blk);
130385Scth 	if (emul64_collect_stats) {
130485Scth 		mutex_enter(&emul64_stats_mutex);
130585Scth 		emul64_nonzero--;
130685Scth 		tgt->emul64_list_length--;
130785Scth 		mutex_exit(&emul64_stats_mutex);
130885Scth 	}
130985Scth 	kmem_free(blk->bl_data, DEV_BSIZE);
131085Scth 	kmem_free(blk, sizeof (blklist_t));
131185Scth }
131285Scth 
131385Scth /*
131485Scth  * Look for overlap between a nowrite range and a block range.
131585Scth  *
131685Scth  * NOTE:  Callers of this function must hold the tgt->emul64_tgt_nw_lock
131785Scth  *	  lock.  For the purposes of this function, a reader lock is
131885Scth  *	  sufficient.
131985Scth  */
132085Scth static emul64_rng_overlap_t
bsd_tgt_overlap(emul64_tgt_t * tgt,diskaddr_t blkno,int count)132185Scth bsd_tgt_overlap(emul64_tgt_t *tgt, diskaddr_t blkno, int count)
132285Scth {
132385Scth 	emul64_nowrite_t	*nw;
132485Scth 	emul64_rng_overlap_t	rv = O_NONE;
132585Scth 
132685Scth 	for (nw = tgt->emul64_tgt_nowrite;
13277875SChris.Horne@Sun.COM 	    (nw != NULL) && (rv == O_NONE);
13287875SChris.Horne@Sun.COM 	    nw = nw->emul64_nwnext) {
13297875SChris.Horne@Sun.COM 		rv = emul64_overlap(&nw->emul64_blocked, blkno, (size_t)count);
133085Scth 	}
133185Scth 	return (rv);
133285Scth }
133385Scth 
133485Scth /*
133585Scth  * Operations that do a lot of I/O, such as RAID 5 initializations, result
133685Scth  * in a CPU bound kernel when the device is an emul64 device.  This makes
133785Scth  * the machine look hung.  To avoid this problem, give up the CPU from time
133885Scth  * to time.
133985Scth  */
134085Scth 
134185Scth static void
emul64_yield_check()134285Scth emul64_yield_check()
134385Scth {
134485Scth 	static uint_t	emul64_io_count = 0;	/* # I/Os since last wait */
134585Scth 	static uint_t	emul64_waiting = FALSE;	/* TRUE -> a thread is in */
134685Scth 						/*   cv_timed wait. */
134785Scth 	clock_t		ticks;
134885Scth 
134985Scth 	if (emul64_yield_enable == 0)
135085Scth 		return;
135185Scth 
135285Scth 	mutex_enter(&emul64_yield_mutex);
135385Scth 
135485Scth 	if (emul64_waiting == TRUE) {
135585Scth 		/*
135685Scth 		 * Another thread has already started the timer.  We'll
135785Scth 		 * just wait here until their time expires, and they
135885Scth 		 * broadcast to us.  When they do that, we'll return and
135985Scth 		 * let our caller do more I/O.
136085Scth 		 */
136185Scth 		cv_wait(&emul64_yield_cv, &emul64_yield_mutex);
136285Scth 	} else if (emul64_io_count++ > emul64_yield_period) {
136385Scth 		/*
136485Scth 		 * Set emul64_waiting to let other threads know that we
136585Scth 		 * have started the timer.
136685Scth 		 */
136785Scth 		emul64_waiting = TRUE;
136885Scth 		emul64_num_delay_called++;
136985Scth 		ticks = drv_usectohz(emul64_yield_length);
137085Scth 		if (ticks == 0)
137185Scth 			ticks = 1;
1372*11066Srafael.vanoni@sun.com 		(void) cv_reltimedwait(&emul64_yield_cv, &emul64_yield_mutex,
1373*11066Srafael.vanoni@sun.com 		    ticks, TR_CLOCK_TICK);
137485Scth 		emul64_io_count = 0;
137585Scth 		emul64_waiting = FALSE;
137685Scth 
137785Scth 		/* Broadcast in case others are waiting. */
137885Scth 		cv_broadcast(&emul64_yield_cv);
137985Scth 	}
138085Scth 
138185Scth 	mutex_exit(&emul64_yield_mutex);
138285Scth }
1383