/* * []---- * | 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); }
/* * []---- * | 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); }
/*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 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)); }
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); }