static void do_dataout(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len, size_t opt_data_len) { char *opt_data = NULL; raw_io_t *io; if ((io = calloc(1, sizeof (*io))) == NULL) { spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); trans_send_complete(cmd, STATUS_CHECK); return; } if ((opt_data_len != 0) && ((opt_data = malloc(opt_data_len)) == NULL)) { spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); trans_send_complete(cmd, STATUS_CHECK); return; } io->r_cdb = cdb; io->r_cdb_len = cdb_len; io->r_data = opt_data; io->r_data_len = opt_data_len; if (trans_rqst_dataout(cmd, opt_data, opt_data_len, 0, io, raw_free_io) == False) { spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); trans_send_complete(cmd, STATUS_CHECK); } }
/*ARGSUSED*/ static void raw_write_tape(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { size_t request_len; size_t xfer; raw_io_t *io; request_len = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4]; request_len *= (cdb[1] & 0x1) ? 512 : 1; if ((io = calloc(1, sizeof (*io))) == NULL) { spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); trans_send_complete(cmd, STATUS_CHECK); return; } if ((io->r_data = malloc(request_len)) == NULL) { spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); trans_send_complete(cmd, STATUS_CHECK); } io->r_data_len = request_len; io->r_cmd = cmd; xfer = min(T10_MAX_OUT(cmd), request_len); (void) trans_rqst_dataout(cmd, io->r_data, xfer, io->r_offset, io, raw_free_io); }
/*ARGSUSED*/ void raw_write_tape_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data, size_t data_len) { raw_io_t *io = (raw_io_t *)id; size_t xfer; if ((io->r_offset + data_len) < io->r_data_len) { io->r_offset += data_len; xfer = min(T10_MAX_OUT(cmd), io->r_data_len - io->r_offset); (void) trans_rqst_dataout(cmd, io->r_data + io->r_offset, xfer, io->r_offset, io, raw_free_io); return; } else { trans_send_complete(cmd, do_uscsi(cmd, io, RawDataToDevice)); } }
/*ARGSUSED*/ static void raw_write(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { /*LINTED*/ union scsi_cdb *cdbp = (union scsi_cdb *)cdb; off_t addr; uint64_t err_blkno; uint32_t cnt; uchar_t addl_sense_len; char debug[80]; /* debug */ raw_params_t *r; raw_io_t *io; size_t max_out; if ((r = (raw_params_t *)T10_PARAMS_AREA(cmd)) == NULL) return; if (r->r_dtype == DTYPE_SEQUENTIAL) { raw_write_tape(cmd, cdb, cdb_len); return; } switch (cdb[0]) { case SCMD_WRITE: /* * SBC-2 revision 16, section 5.24 * 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 = (off_t)cdbp->g0_addr2 << 16 | (off_t)cdbp->g0_addr1 << 8 | (off_t)cdbp->g0_addr0; cnt = cdbp->g0_count0; /* * 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] || (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 = (off_t)cdbp->g1_addr3 << 24 | (off_t)cdbp->g1_addr2 << 16 | (off_t)cdbp->g1_addr1 << 8 | (off_t)cdbp->g1_addr0; cnt = cdbp->g1_count1 << 8 | cdbp->g1_count0; break; case SCMD_WRITE_G4: /* * SBC-2 revision 16, section 5.27 * Reserve bit checks. */ if ((cdb[1] & 0x6) || 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 = (off_t)(cdbp->g4_addr3 & 0xff) << 56 | (off_t)(cdbp->g4_addr2 & 0xff) << 48 | (off_t)(cdbp->g4_addr1 & 0xff) << 40 | (off_t)(cdbp->g4_addr0 & 0xff) << 32 | (off_t)(cdbp->g4_addtl_cdb_data3 & 0xff) << 24 | (off_t)(cdbp->g4_addtl_cdb_data2 & 0xff) << 16 | (off_t)(cdbp->g4_addtl_cdb_data1 & 0xff) << 8 | (off_t)(cdbp->g4_addtl_cdb_data0 & 0xff); cnt = cdbp->g4_count3 << 24 | cdbp->g4_count2 << 16 | cdbp->g4_count1 << 8 | cdbp->g4_count0; break; default: queue_str(mgmtq, Q_STE_ERRS, msg_log, "Unprocessed WRITE type"); spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); spc_sense_ascq(cmd, 0x24, 0x00); trans_send_complete(cmd, STATUS_CHECK); return; } if ((addr < 0) || ((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 WRITE 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; } if (cnt == 0) { trans_send_complete(cmd, STATUS_GOOD); return; } io = (raw_io_t *)cmd->c_emul_id; if (io == NULL) { if ((io = (raw_io_t *)calloc(1, sizeof (*io))) == NULL) { spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); trans_send_complete(cmd, STATUS_CHECK); return; } io->r_lba = addr; io->r_lba_cnt = cnt; io->r_cmd = cmd; io->r_aio.a_aio_cmplt = raw_write_cmplt; io->r_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; } /* * 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->r_data_len = max_out ? MIN(max_out, (cnt * 512) - io->r_offset) : (cnt * 512); #ifdef FULL_DEBUG (void) snprintf(debug, sizeof (debug), "RAW%d blk 0x%llx, cnt %d, offset 0x%llx, size %d", cmd->c_lu->l_common->l_num, addr, cnt, io->r_offset, io->r_data_len); queue_str(mgmtq, Q_STE_IO, msg_log, debug); #endif if ((io->r_data = (char *)malloc(io->r_data_len)) == NULL) { /* * NOTE: May need a different ASC code */ err_blkno = addr + ((io->r_offset + 511) / 512); if (err_blkno > FIXED_SENSE_ADDL_INFO_LEN) addl_sense_len = INFORMATION_SENSE_DESCR; else addl_sense_len = 0; spc_sense_create(cmd, KEY_HARDWARE_ERROR, addl_sense_len); spc_sense_info(cmd, err_blkno); trans_send_complete(cmd, STATUS_CHECK); return; } if (trans_rqst_dataout(cmd, io->r_data, io->r_data_len, io->r_offset, io, raw_free_io) == False) { spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); trans_send_complete(cmd, STATUS_CHECK); } }
/*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); } }