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*7875SChris.Horne@Sun.COM * Copyright 2008 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 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 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 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; 242*7875SChris.Horne@Sun.COM i < sizeof (emul64_properties) / sizeof (struct prop_map); 243*7875SChris.Horne@Sun.COM i++, pmp++) { 24485Scth if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 245*7875SChris.Horne@Sun.COM DDI_PROP_DONTPASS, pmp->pm_name, &properties, 246*7875SChris.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 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 27085Scth bsd_scsi_start_stop_unit(struct scsi_pkt *pkt) 27185Scth { 27285Scth return (0); 27385Scth } 27485Scth 27585Scth /* ARGSUSED 0 */ 27685Scth int 27785Scth bsd_scsi_test_unit_ready(struct scsi_pkt *pkt) 27885Scth { 27985Scth return (0); 28085Scth } 28185Scth 28285Scth /* ARGSUSED 0 */ 28385Scth int 28485Scth bsd_scsi_request_sense(struct scsi_pkt *pkt) 28585Scth { 28685Scth return (0); 28785Scth } 28885Scth 28985Scth int 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 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 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]) { 390*7875SChris.Horne@Sun.COM case 0x00: 391*7875SChris.Horne@Sun.COM return (bsd_scsi_inq_page0(pkt, pqdtype)); 392*7875SChris.Horne@Sun.COM case 0x83: 393*7875SChris.Horne@Sun.COM return (bsd_scsi_inq_page83(pkt, pqdtype)); 394*7875SChris.Horne@Sun.COM default: 395*7875SChris.Horne@Sun.COM cmn_err(CE_WARN, "%s: bsd_scsi_inquiry: " 396*7875SChris.Horne@Sun.COM "unsupported 0x%x", 397*7875SChris.Horne@Sun.COM emul64_name, cdb->cdb_opaque[2]); 398*7875SChris.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 42385Scth bsd_scsi_format(struct scsi_pkt *pkt) 42485Scth { 42585Scth return (0); 42685Scth } 42785Scth 42885Scth int 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, 441*7875SChris.Horne@Sun.COM pkt->pkt_address.a_target, pkt->pkt_address.a_lun, 442*7875SChris.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, 453*7875SChris.Horne@Sun.COM pkt->pkt_address.a_target, pkt->pkt_address.a_lun, 454*7875SChris.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, 465*7875SChris.Horne@Sun.COM pkt->pkt_address.a_target, pkt->pkt_address.a_lun, 466*7875SChris.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, 477*7875SChris.Horne@Sun.COM pkt->pkt_address.a_target, pkt->pkt_address.a_lun, 478*7875SChris.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, 491*7875SChris.Horne@Sun.COM pkt->pkt_address.a_target, pkt->pkt_address.a_lun, 492*7875SChris.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, 505*7875SChris.Horne@Sun.COM pkt->pkt_address.a_target, pkt->pkt_address.a_lun, 506*7875SChris.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 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 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 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 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 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 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, 845*7875SChris.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 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 91785Scth bsd_scsi_mode_select(struct scsi_pkt *pkt) 91885Scth { 91985Scth return (0); 92085Scth } 92185Scth 92285Scth int 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, 932*7875SChris.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, 944*7875SChris.Horne@Sun.COM sizeof (struct scsi_capacity)); 94585Scth return (rval); 94685Scth } 94785Scth 94885Scth int 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, 958*7875SChris.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, 971*7875SChris.Horne@Sun.COM sizeof (struct scsi_capacity_16)); 97285Scth return (rval); 97385Scth } 97485Scth int 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 98385Scth bsd_scsi_reserve(struct scsi_pkt *pkt) 98485Scth { 98585Scth return (0); 98685Scth } 98785Scth 98885Scth /* ARGSUSED 0 */ 98985Scth int 99085Scth bsd_scsi_release(struct scsi_pkt *pkt) 99185Scth { 99285Scth return (0); 99385Scth } 99485Scth 99585Scth 99685Scth int 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 100685Scth bsd_scsi_reassign_block(struct scsi_pkt *pkt) 100785Scth { 100885Scth return (0); 100985Scth } 101085Scth 101185Scth 101285Scth static int 1013*7875SChris.Horne@Sun.COM bsd_readblks(struct emul64 *emul64, ushort_t target, ushort_t lun, 1014*7875SChris.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", 1024*7875SChris.Horne@Sun.COM emul64_name, target, lun, blkno, blkno, nblks); 102585Scth } 102685Scth 102785Scth emul64_yield_check(); 102885Scth 102985Scth EMUL64_MUTEX_ENTER(emul64); 1030*7875SChris.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", 1034*7875SChris.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 1094*7875SChris.Horne@Sun.COM bsd_writeblks(struct emul64 *emul64, ushort_t target, ushort_t lun, 1095*7875SChris.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", 1106*7875SChris.Horne@Sun.COM emul64_name, target, lun, blkno, blkno, nblks); 110785Scth } 110885Scth 110985Scth emul64_yield_check(); 111085Scth 111185Scth EMUL64_MUTEX_ENTER(emul64); 1112*7875SChris.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", 1116*7875SChris.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, 1180*7875SChris.Horne@Sun.COM DEV_BSIZE); 118185Scth } else { 1182*7875SChris.Horne@Sun.COM bsd_allocblk(tgt, blkno, 1183*7875SChris.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 * 1205*7875SChris.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) { 1211*7875SChris.Horne@Sun.COM if (tgt->emul64_tgt_saddr.a_target == target && 1212*7875SChris.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 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); 1232*7875SChris.Horne@Sun.COM blk != NULL; 1233*7875SChris.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 * 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 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 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 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; 1327*7875SChris.Horne@Sun.COM (nw != NULL) && (rv == O_NONE); 1328*7875SChris.Horne@Sun.COM nw = nw->emul64_nwnext) { 1329*7875SChris.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 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; 137285Scth (void) cv_timedwait(&emul64_yield_cv, 137385Scth &emul64_yield_mutex, ddi_get_lbolt() + ticks); 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