Exemplo n.º 1
0
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);
	}
}
Exemplo n.º 2
0
/*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);
}
Exemplo n.º 3
0
/*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));
	}
}
Exemplo n.º 4
0
/*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);
	}
}
Exemplo n.º 5
0
/*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);
	}
}