/*ARGSUSED*/ static void sbc_write(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { union scsi_cdb *u; diskaddr_t addr; uint64_t err_blkno; uint32_t cnt; uchar_t addl_sense_len; disk_params_t *d; disk_io_t *io; size_t max_out; void *mmap_area; if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL) return; /*LINTED*/ u = (union scsi_cdb *)cdb; switch (u->scc_cmd) { case SCMD_WRITE: /* * SBC-2 revision 16, section 5.24 * Reserve bit checks. */ if ((cdb[1] & 0xe0) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG0ADDR(u); cnt = GETG0COUNT(u); /* * SBC-2 Revision 16/Section 5.24 WRITE(6) * A TRANSFER LENGHT of 0 indicates that 256 logical blocks * shall be written. */ if (cnt == 0) cnt = 256; break; case SCMD_WRITE_G1: /* * SBC-2 revision 16, section 5.25 * Reserve bit checks. */ if ((cdb[1] & 0x6) || cdb[6] || SAM_CONTROL_BYTE_RESERVED(cdb[9])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG1ADDR(u); cnt = GETG1COUNT(u); break; case SCMD_WRITE_G4: /* * SBC-2 revision 16, section 5.27 * Reserve bit checks. */ if ((cdb[1] & 0x6) || cdb[14] || SAM_CONTROL_BYTE_RESERVED(cdb[15])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)GETG4LONGADDR(u); cnt = GETG4COUNT(u); break; default: queue_prt(mgmtq, Q_STE_ERRS, "Unprocessed WRITE type"); spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } if ((addr + cnt) > d->d_size) { if (addr > d->d_size) err_blkno = addr; else err_blkno = d->d_size; /* * XXX: What's SBC-2 say about ASC/ASCQ here. Solaris * doesn't care about these values when key is set * to KEY_ILLEGAL_REQUEST. */ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN) addl_sense_len = INFORMATION_SENSE_DESCR; else addl_sense_len = 0; spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, addl_sense_len); spc_sense_info(cmd, err_blkno); spc_sense_ascq(cmd, 0x21, 0x00); trans_send_complete(cmd, STATUS_CHECK); queue_prt(mgmtq, Q_STE_ERRS, "SBC%x LUN%d WRITE Illegal sector " "(0x%llx + 0x%x) > 0x%ullx", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr, cnt, d->d_size); return; } if (cnt == 0) { queue_prt(mgmtq, Q_STE_NONIO, "SBC%x LUN%d WRITE zero block count for addr 0x%x", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr); trans_send_complete(cmd, STATUS_GOOD); return; } io = (disk_io_t *)cmd->c_emul_id; if (io == NULL) { io = sbc_io_alloc(cmd); io->da_lba = addr; io->da_lba_cnt = cnt; io->da_clear_overlap = False; io->da_aio.a_aio_cmplt = sbc_write_cmplt; io->da_aio.a_id = io; /* * Only update the statistics the first time through * for this particular command. If the requested transfer * is larger than the transport can handle this routine * will be called many times. */ cmd->c_lu->l_cmds_write++; cmd->c_lu->l_sects_write += cnt; } #ifdef FULL_DEBUG queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d blk 0x%llx, cnt %d, offset 0x%llx, size %d", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr, cnt, io->da_offset, io->da_data_len); #endif /* * If a transport sets the maximum output value to zero we'll * just request the entire amount. Otherwise, transfer no more * than the maximum output or the reminder, whichever is less. */ max_out = cmd->c_lu->l_targ->s_maxout; io->da_data_len = max_out ? MIN(max_out, (cnt * 512) - io->da_offset) : (cnt * 512); mmap_area = T10_MMAP_AREA(cmd); if (mmap_area != MAP_FAILED) { io->da_data_alloc = False; io->da_data = (char *)mmap_area + (addr * 512LL) + io->da_offset; sbc_overlap_check(io); } else if ((io->da_data = (char *)malloc(io->da_data_len)) == NULL) { trans_send_complete(cmd, STATUS_BUSY); return; } else { io->da_data_alloc = True; } if (trans_rqst_dataout(cmd, io->da_data, io->da_data_len, io->da_offset, io) == False) { trans_send_complete(cmd, STATUS_BUSY); } }
/*ARGSUSED*/ static void raw_read(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { /*LINTED*/ union scsi_cdb *u = (union scsi_cdb *)cdb; diskaddr_t addr; off_t offset = 0; uint32_t cnt; uint32_t min; raw_io_t *io; uint64_t err_blkno; int sense_len; char debug[80]; raw_params_t *r; uchar_t addl_sense_len; t10_cmd_t *c; if ((r = (raw_params_t *)T10_PARAMS_AREA(cmd)) == NULL) return; if (r->r_dtype == DTYPE_SEQUENTIAL) { raw_read_tape(cmd, cdb, cdb_len); return; } switch (u->scc_cmd) { case SCMD_READ: /* * SBC-2 Revision 16, section 5.5 * Reserve bit checks */ if ((cdb[1] & 0xe0) || (cdb[5] & 0x38)) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, 0x24, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG0ADDR(u); cnt = GETG0COUNT(u); /* * SBC-2 Revision 16 * Section: 5.5 READ(6) command * A TRANSFER LENGTH field set to zero specifies * that 256 logical blocks shall be read. */ if (cnt == 0) cnt = 256; break; case SCMD_READ_G1: /* * SBC-2 Revision 16, section 5.6 * Reserve bit checks. */ if ((cdb[1] & 6) || cdb[6] || (cdb[9] & 0x38)) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, 0x24, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG1ADDR(u); cnt = GETG1COUNT(u); break; case SCMD_READ_G4: /* * SBC-2 Revision 16, section 5.8 * Reserve bit checks */ if ((cdb[1] & 0x6) || (cdb[10] & 6) || cdb[14] || (cdb[15] & 0x38)) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, 0x24, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = GETG4LONGADDR(u); cnt = GETG4COUNT(u); break; default: spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); trans_send_complete(cmd, STATUS_CHECK); return; } if ((addr + cnt) > r->r_size) { /* * request exceed the capacity of disk * set error block number to capacity + 1 */ err_blkno = r->r_size + 1; /* * XXX: What's SBC-2 say about ASC/ASCQ here. Solaris * doesn't care about these values when key is set * to KEY_ILLEGAL_REQUEST. */ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN) addl_sense_len = INFORMATION_SENSE_DESCR; else addl_sense_len = 0; spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, addl_sense_len); spc_sense_info(cmd, err_blkno); spc_sense_ascq(cmd, 0x21, 0x00); trans_send_complete(cmd, STATUS_CHECK); (void) snprintf(debug, sizeof (debug), "RAW%d READ Illegal sector (0x%llx + 0x%x) > 0x%llx", cmd->c_lu->l_common->l_num, addr, cnt, r->r_size); queue_str(mgmtq, Q_STE_ERRS, msg_log, debug); return; } cmd->c_lu->l_cmds_read++; cmd->c_lu->l_sects_read += cnt; if (cnt == 0) { trans_send_complete(cmd, STATUS_GOOD); return; } do { min = MIN((cnt * 512) - offset, T10_MAX_OUT(cmd)); if ((offset + min) < (cnt * 512LL)) c = trans_cmd_dup(cmd); else c = cmd; if ((io = (raw_io_t *)calloc(1, sizeof (*io))) == NULL) { /* * We're pretty much dead in the water. If we can't * allocate memory. It's unlikey we'll be able to * allocate a sense buffer or queue the command * up to be sent back to the transport for delivery. */ spc_sense_create(c, KEY_HARDWARE_ERROR, 0); trans_send_complete(c, STATUS_CHECK); return; } io->r_cmd = c; io->r_lba = addr; io->r_lba_cnt = cnt; io->r_offset = offset; io->r_data_len = min; io->r_aio.a_aio_cmplt = raw_read_cmplt; io->r_aio.a_id = io; #ifdef FULL_DEBUG (void) snprintf(debug, sizeof (debug), "RAW%d blk 0x%llx, cnt %d, offset 0x%llx, size %d", c->c_lu->l_common->l_num, addr, cnt, io->r_offset, min); queue_str(mgmtq, Q_STE_IO, msg_log, debug); #endif if ((io->r_data = (char *)malloc(min)) == NULL) { err_blkno = addr + ((offset + 511) / 512); if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN) sense_len = INFORMATION_SENSE_DESCR; else sense_len = 0; spc_sense_create(c, KEY_HARDWARE_ERROR, sense_len); spc_sense_info(c, err_blkno); trans_send_complete(c, STATUS_CHECK); return; } trans_aioread(c, io->r_data, min, (addr * 512LL) + (off_t)io->r_offset, &io->r_aio); offset += min; } while (offset < (off_t)(cnt * 512)); }
/*ARGSUSED*/ static void sbc_read(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { /*LINTED*/ union scsi_cdb *u = (union scsi_cdb *)cdb; diskaddr_t addr; off_t offset = 0; uint32_t cnt, min; disk_io_t *io; void *mmap_data = T10_MMAP_AREA(cmd); uint64_t err_blkno; disk_params_t *d; uchar_t addl_sense_len; if ((d = (disk_params_t *)T10_PARAMS_AREA(cmd)) == NULL) return; switch (u->scc_cmd) { case SCMD_READ: /* * SBC-2 Revision 16, section 5.5 * Reserve bit checks */ if ((cdb[1] & 0xe0) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG0ADDR(u); cnt = GETG0COUNT(u); /* * SBC-2 Revision 16 * Section: 5.5 READ(6) command * A TRANSFER LENGTH field set to zero specifies * that 256 logical blocks shall be read. */ if (cnt == 0) cnt = 256; break; case SCMD_READ_G1: /* * SBC-2 Revision 16, section 5.6 * Reserve bit checks. */ if ((cdb[1] & 6) || cdb[6] || SAM_CONTROL_BYTE_RESERVED(cdb[9])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = (diskaddr_t)(uint32_t)GETG1ADDR(u); cnt = GETG1COUNT(u); break; case SCMD_READ_G4: /* * SBC-2 Revision 16, section 5.8 * Reserve bit checks */ if ((cdb[1] & 0x6) || cdb[14] || SAM_CONTROL_BYTE_RESERVED(cdb[15])) { spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } addr = GETG4LONGADDR(u); cnt = GETG4COUNT(u); break; default: spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); trans_send_complete(cmd, STATUS_CHECK); return; } if ((addr + cnt) > d->d_size) { if (addr > d->d_size) err_blkno = addr; else err_blkno = d->d_size; /* * XXX: What's SBC-2 say about ASC/ASCQ here. Solaris * doesn't care about these values when key is set * to KEY_ILLEGAL_REQUEST. */ if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN) addl_sense_len = INFORMATION_SENSE_DESCR; else addl_sense_len = 0; spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, addl_sense_len); spc_sense_info(cmd, err_blkno); spc_sense_ascq(cmd, 0x21, 0x00); trans_send_complete(cmd, STATUS_CHECK); queue_prt(mgmtq, Q_STE_ERRS, "SBC%x LUN%d READ Illegal sector " "(0x%llx + 0x%x) > 0x%ullx", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr, cnt, d->d_size); return; } cmd->c_lu->l_cmds_read++; cmd->c_lu->l_sects_read += cnt; if (cnt == 0) { trans_send_complete(cmd, STATUS_GOOD); return; } do { io = sbc_io_alloc(cmd); min = MIN((cnt * 512) - offset, T10_MAX_OUT(cmd)); io->da_lba = addr; io->da_lba_cnt = cnt; io->da_offset = offset; io->da_data_len = min; #ifdef FULL_DEBUG queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d blk 0x%llx, cnt %d, offset 0x%llx, size %d", cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, addr, cnt, io->da_offset, min); #endif if (mmap_data != MAP_FAILED) { io->da_clear_overlap = True; io->da_data_alloc = False; io->da_aio.a_aio.aio_return = min; io->da_data = (char *)mmap_data + (addr * 512LL) + io->da_offset; sbc_overlap_store(io); sbc_read_cmplt((emul_handle_t)io); } else { if ((io->da_data = (char *)malloc(min)) == NULL) { trans_send_complete(cmd, STATUS_BUSY); return; } io->da_clear_overlap = False; io->da_data_alloc = True; io->da_aio.a_aio_cmplt = sbc_read_cmplt; io->da_aio.a_id = io; trans_aioread(cmd, io->da_data, min, (addr * 512LL) + (off_t)io->da_offset, (aio_result_t *)io); } offset += min; } while (offset < (off_t)(cnt * 512)); }