Пример #1
0
/*
 * []----
 * | raw_cmd -- start a SCSI command
 * |
 * | This routine is called from within the SAM-3 Task router.
 * []----
 */
static void
raw_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
	scsi_cmd_table_t	*e;
#ifdef FULL_DEBUG
	char			debug[80];
#endif

	e = &cmd->c_lu->l_cmd_table[cdb[0]];
#ifdef FULL_DEBUG
	(void) snprintf(debug, sizeof (debug), "RAW%d  Cmd %s\n",
	    cmd->c_lu->l_common->l_num,
	    e->cmd_name == NULL ? "(no name)" : e->cmd_name);
	queue_str(mgmtq, Q_STE_IO, msg_log, debug);
#endif
	(*e->cmd_start)(cmd, cdb, cdb_len);
}
Пример #2
0
/*
 * []----
 * | raw_data -- Data phase for command.
 * |
 * | Normally this is only called for the WRITE command. Other commands
 * | that have a data in phase will probably be short circuited when
 * | we call trans_rqst_dataout() and the data is already available.
 * | At least this is true for iSCSI. FC however will need a DataIn phase
 * | for commands like MODE SELECT and PGROUT.
 * []----
 */
static void
raw_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
    size_t data_len)
{
	scsi_cmd_table_t	*e;
#ifdef FULL_DEBUG
	char			debug[80];
#endif

	e = &cmd->c_lu->l_cmd_table[cmd->c_cdb[0]];
#ifdef FULL_DEBUG
	(void) snprintf(debug, sizeof (debug), "RAW%d  Data %s\n",
	    cmd->c_lu->l_common->l_num, e->cmd_name);
	queue_str(mgmtq, Q_STE_IO, msg_log, debug);
#endif
	(*e->cmd_data)(cmd, id, offset, data, data_len);
}
Пример #3
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);
	}
}
Пример #4
0
/*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));
}
Пример #5
0
void *
port_watcher(void *v)
{
	int			s,
				fd,
				on = 1;
	char			debug[80];
	struct sockaddr_in	sin_ip;
	struct sockaddr_in6	sin6_ip;
	struct sockaddr_storage	st;
	socklen_t		socklen;
	iscsi_conn_t		*conn;
	port_args_t		*p = (port_args_t *)v;
	target_queue_t		*q = p->port_mgmtq;
	int			l,
				accept_err_sleep = 1;
	pthread_t		junk;
	struct in_addr		addr;
	struct in6_addr		addr6;

	/*
	 * Try creating an IPv6 socket first
	 * If failed, try creating an IPv4 socket
	 */
	if ((s = socket(PF_INET6, SOCK_STREAM, 0)) == -1) {

		queue_str(q, Q_GEN_ERRS, msg_log, "Opening IPv4 socket");
		if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
			queue_str(q, Q_GEN_ERRS, msg_log,
			    "Can't open socket");
			return (NULL);
		} else {
			bzero(&sin_ip, sizeof (sin_ip));
			sin_ip.sin_family = AF_INET;
			sin_ip.sin_port = htons(p->port_num);
			sin_ip.sin_addr.s_addr = INADDR_ANY;

			(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
			    (char *)&on, sizeof (on));

			if ((bind(s, (struct sockaddr *)&sin_ip,
				sizeof (sin_ip))) < 0) {
				(void) snprintf(debug, sizeof (debug),
				    "bind on port %d failed, errno %d",
				    p->port_num, errno);
				queue_str(q, Q_GEN_ERRS, msg_status, debug);
				return (NULL);
			}
		}

	} else {

		queue_str(q, Q_GEN_DETAILS, msg_log, "Got IPv6 socket");
		bzero(&sin6_ip, sizeof (sin6_ip));
		sin6_ip.sin6_family = AF_INET6;
		sin6_ip.sin6_port = htons(p->port_num);
		sin6_ip.sin6_addr = in6addr_any;

		(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
		    sizeof (on));

		if ((bind(s, (struct sockaddr *)&sin6_ip, sizeof (sin6_ip)))
		    < 0) {
			(void) snprintf(debug, sizeof (debug),
			    "bind on port %d failed, errno %d",
			    p->port_num, errno);
			queue_str(q, Q_GEN_ERRS, msg_status, debug);
			return (NULL);
		}
	}

	if (listen(s, 5) < 0) {
		queue_str(q, Q_GEN_ERRS, msg_status, "listen failed");
		return (NULL);
	}

	/*CONSTANTCONDITION*/
	while (1) {

		socklen = sizeof (st);
		if ((fd = accept(s, (struct sockaddr *)&st,
		    &socklen)) < 0) {
			accept_err_sleep *= 2;
			(void) sleep(accept_err_sleep);
			if (accept_err_sleep > 60) {
				accept_err_sleep = 1;
				queue_prt(q, Q_GEN_ERRS,
				    "accept failed, errno %d", errno);
			}
			continue;
		}

		l = 128 * 1024;
		if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&l,
		    sizeof (l)) < 0)
			queue_str(q, Q_GEN_ERRS, msg_status,
			    "setsockopt failed");
		if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&l,
		    sizeof (l)) < 0)
			queue_str(q, Q_GEN_ERRS, msg_status,
			    "setsockopt failed");
		l = 1;
		if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&l,
		    sizeof (l)) < 0)
			queue_str(q, Q_GEN_ERRS, msg_status,
			    "setsockopt keepalive failed");

		if ((conn = (iscsi_conn_t *)calloc(sizeof (iscsi_conn_t),
		    1)) == NULL) {
			/*
			 * If we fail to get memory this is all rather
			 * pointless, since it's unlikely that queue_str
			 * could malloc memory to send a message.
			 */
			queue_str(q, Q_GEN_ERRS, msg_status,
			    "connection malloc failed");
			return (NULL);
		}

		/*
		 * Save initiator sockaddr for future use
		 */
		conn->c_initiator_sockaddr = st;

		socklen = sizeof (st);
		if (getsockname(fd, (struct sockaddr *)&st,
		    &socklen) == 0) {
			/*
			 * Save target sockaddr for future use
			 */

			addr6 = ((struct sockaddr_in6 *)&st)->sin6_addr;
			if (st.ss_family == AF_INET6 &&
			    IN6_IS_ADDR_V4MAPPED(&addr6)) {
				/*
				 * If target address is IPv4 mapped IPv6 address
				 * convert it to IPv4 address
				 */
				IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
				((struct sockaddr_in *)&st)->sin_addr = addr;
				st.ss_family = AF_INET;
			}

			conn->c_target_sockaddr = st;
		}

		conn->c_fd	= fd;
		conn->c_mgmtq	= q;
		conn->c_up_at	= time(NULL);
		conn->c_state	= S1_FREE;
		(void) pthread_mutex_init(&conn->c_mutex, NULL);
		(void) pthread_mutex_init(&conn->c_state_mutex, NULL);
		(void) pthread_mutex_lock(&port_mutex);
		conn->c_num	= port_conn_num++;
		if (conn_head == NULL) {
			conn_head = conn;
			assert(conn_tail == NULL);
			conn_tail = conn;
		} else {
			conn_tail->c_next = conn;
			conn->c_prev = conn_tail;
			conn_tail = conn;
		}
		(void) pthread_mutex_unlock(&port_mutex);

		(void) pthread_create(&junk, NULL, conn_process, conn);
	}
	return (NULL);
}