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