📄 iscsi.c
字号:
goto out; } head = &session->cmnd_hash[cmnd_hashfn(cmnd->pdu.bhs.itt)]; spin_lock(&session->cmnd_hash_lock); tmp = __cmnd_find_hash(session, itt, ISCSI_RESERVED_TAG); if (!tmp) { list_add_tail(&cmnd->hash_list, head); set_cmnd_hashed(cmnd); } else err = -ISCSI_REASON_TASK_IN_PROGRESS; spin_unlock(&session->cmnd_hash_lock); if (!err) { update_stat_sn(cmnd); err = check_cmd_sn(cmnd); }out: return err;}static void __cmnd_remove_hash(struct iscsi_cmnd *cmnd){ list_del(&cmnd->hash_list);}static void cmnd_remove_hash(struct iscsi_cmnd *cmnd){ struct iscsi_session *session = cmnd->conn->session; struct iscsi_cmnd *tmp; spin_lock(&session->cmnd_hash_lock); tmp = __cmnd_find_hash(session, cmnd->pdu.bhs.itt, ISCSI_RESERVED_TAG); if (tmp && tmp == cmnd) __cmnd_remove_hash(tmp); else eprintk("%p:%x not found\n", cmnd, cmnd_itt(cmnd)); spin_unlock(&session->cmnd_hash_lock);}static void cmnd_skip_data(struct iscsi_cmnd *req){ struct iscsi_cmnd *rsp; struct iscsi_scsi_rsp_hdr *rsp_hdr; u32 size; rsp = get_rsp_cmnd(req); rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs; if (cmnd_opcode(rsp) != ISCSI_OP_SCSI_RSP) { eprintk("unexpected response command %u\n", cmnd_opcode(rsp)); return; } size = cmnd_write_size(req); if (size) { rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; rsp_hdr->residual_count = cpu_to_be32(size); } size = cmnd_read_size(req); if (size) { if (cmnd_hdr(req)->flags & ISCSI_CMD_WRITE) { rsp_hdr->flags |= ISCSI_FLG_BIRESIDUAL_UNDERFLOW; rsp_hdr->bi_residual_count = cpu_to_be32(size); } else { rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; rsp_hdr->residual_count = cpu_to_be32(size); } } req->pdu.bhs.opcode = (req->pdu.bhs.opcode & ~ISCSI_OPCODE_MASK) | ISCSI_OP_SCSI_REJECT; cmnd_skip_pdu(req);}static int cmnd_recv_pdu(struct iscsi_conn *conn, struct tio *tio, u32 offset, u32 size){ int idx, i; char *addr; dprintk(D_GENERIC, "%p %u,%u\n", tio, offset, size); offset += tio->offset; if (!(offset < tio->offset + tio->size) || !(offset + size <= tio->offset + tio->size)) { eprintk("%u %u %u %u", offset, size, tio->offset, tio->size); return -EIO; } assert(offset < tio->offset + tio->size); assert(offset + size <= tio->offset + tio->size); idx = offset >> PAGE_CACHE_SHIFT; offset &= ~PAGE_CACHE_MASK; conn->read_msg.msg_iov = conn->read_iov; conn->read_size = size = (size + 3) & -4; conn->read_overflow = 0; i = 0; while (1) { assert(tio->pvec[idx]); addr = page_address(tio->pvec[idx]); assert(addr); conn->read_iov[i].iov_base = addr + offset; if (offset + size <= PAGE_CACHE_SIZE) { conn->read_iov[i].iov_len = size; conn->read_msg.msg_iovlen = ++i; break; } conn->read_iov[i].iov_len = PAGE_CACHE_SIZE - offset; size -= conn->read_iov[i].iov_len; offset = 0; if (++i >= ISCSI_CONN_IOV_MAX) { conn->read_msg.msg_iovlen = i; conn->read_overflow = size; conn->read_size -= size; break; } idx++; } return 0;}static void set_offset_and_length(struct iet_volume *lu, u8 *cmd, loff_t *off, u32 *len){ assert(lu); switch (cmd[0]) { case READ_6: case WRITE_6: *off = ((cmd[1] & 0x1f) << 16) + (cmd[2] << 8) + cmd[3]; *len = cmd[4]; if (!*len) *len = 256; break; case READ_10: case WRITE_10: case WRITE_VERIFY: *off = be32_to_cpu(*(u32 *)&cmd[2]); *len = (cmd[7] << 8) + cmd[8]; break; case READ_16: case WRITE_16: *off = be64_to_cpu(*(u64 *)&cmd[2]); *len = be32_to_cpu(*(u32 *)&cmd[10]); break; default: BUG(); } *off <<= lu->blk_shift; *len <<= lu->blk_shift;}static u32 translate_lun(u16 * data){ u8 *p = (u8 *) data; u32 lun = ~0U; switch (*p >> 6) { case 0: lun = p[1]; break; case 1: lun = (0x3f & p[0]) << 8 | p[1]; break; case 2: case 3: default: eprintk("%u %u %u %u\n", data[0], data[1], data[2], data[3]); break; } return lun;}static void send_r2t(struct iscsi_cmnd *req){ struct iscsi_cmnd *rsp; struct iscsi_r2t_hdr *rsp_hdr; u32 length, offset, burst; LIST_HEAD(send); length = req->r2t_length; burst = req->conn->session->param.max_burst_length; offset = be32_to_cpu(cmnd_hdr(req)->data_length) - length; do { rsp = iscsi_cmnd_create_rsp_cmnd(req, 0); rsp->pdu.bhs.ttt = req->target_task_tag; rsp_hdr = (struct iscsi_r2t_hdr *)&rsp->pdu.bhs; rsp_hdr->opcode = ISCSI_OP_R2T; rsp_hdr->flags = ISCSI_FLG_FINAL; memcpy(rsp_hdr->lun, cmnd_hdr(req)->lun, 8); rsp_hdr->itt = cmnd_hdr(req)->itt; rsp_hdr->r2t_sn = cpu_to_be32(req->r2t_sn++); rsp_hdr->buffer_offset = cpu_to_be32(offset); if (length > burst) { rsp_hdr->data_length = cpu_to_be32(burst); length -= burst; offset += burst; } else { rsp_hdr->data_length = cpu_to_be32(length); length = 0; } dprintk(D_WRITE, "%x %u %u %u %u\n", cmnd_itt(req), be32_to_cpu(rsp_hdr->data_length), be32_to_cpu(rsp_hdr->buffer_offset), be32_to_cpu(rsp_hdr->r2t_sn), req->outstanding_r2t); list_add_tail(&rsp->list, &send); if (++req->outstanding_r2t >= req->conn->session->param.max_outstanding_r2t) break; } while (length); iscsi_cmnds_init_write(&send);}static void scsi_cmnd_exec(struct iscsi_cmnd *cmnd){ if (cmnd->r2t_length) { if (!cmnd->is_unsolicited_data) send_r2t(cmnd); } else { if (cmnd->lun) { iscsi_scsi_queuecmnd(cmnd); } else { iscsi_device_queue_cmnd(cmnd); } }}static int noop_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd){ u32 size, tmp; int i, err = 0; if (cmnd_ttt(cmnd) != cpu_to_be32(ISCSI_RESERVED_TAG)) { /* * We don't request a NOP-Out by sending a NOP-In. * See 10.18.2 in the draft 20. */ eprintk("initiator bug %x\n", cmnd_itt(cmnd)); err = -ISCSI_REASON_PROTOCOL_ERROR; goto out; } if (cmnd_itt(cmnd) == cpu_to_be32(ISCSI_RESERVED_TAG)) { if (!(cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE)) eprintk("%s\n","initiator bug!"); update_stat_sn(cmnd); err = check_cmd_sn(cmnd); goto out; } else if ((err = cmnd_insert_hash(cmnd)) < 0) { eprintk("ignore this request %x\n", cmnd_itt(cmnd)); goto out; } if ((size = cmnd->pdu.datasize)) { size = (size + 3) & -4; conn->read_msg.msg_iov = conn->read_iov; if (cmnd->pdu.bhs.itt != cpu_to_be32(ISCSI_RESERVED_TAG)) { struct tio *tio; int pg_cnt = get_pgcnt(size, 0); assert(pg_cnt < ISCSI_CONN_IOV_MAX); cmnd->tio = tio = tio_alloc(pg_cnt); tio_set(tio, size, 0); for (i = 0; i < pg_cnt; i++) { conn->read_iov[i].iov_base = page_address(tio->pvec[i]); tmp = min_t(u32, size, PAGE_CACHE_SIZE); conn->read_iov[i].iov_len = tmp; conn->read_size += tmp; size -= tmp; } } else { for (i = 0; i < ISCSI_CONN_IOV_MAX; i++) { conn->read_iov[i].iov_base = dummy_data; tmp = min_t(u32, size, sizeof(dummy_data)); conn->read_iov[i].iov_len = tmp; conn->read_size += tmp; size -= tmp; } } assert(!size); conn->read_overflow = size; conn->read_msg.msg_iovlen = i; }out: return err;}static u32 get_next_ttt(struct iscsi_session *session){ u32 ttt; if (session->next_ttt == ISCSI_RESERVED_TAG) session->next_ttt++; ttt = session->next_ttt++; return cpu_to_be32(ttt);}static void scsi_cmnd_start(struct iscsi_conn *conn, struct iscsi_cmnd *req){ struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); dprintk(D_GENERIC, "scsi command: %02x\n", req_hdr->scb[0]); req->lun = volume_get(conn->session->target, translate_lun(req_hdr->lun)); if (!req->lun) { switch (req_hdr->scb[0]) { case INQUIRY: case REPORT_LUNS: break; default: eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); create_sense_rsp(req, ILLEGAL_REQUEST, 0x25, 0x0); cmnd_skip_data(req); goto out; } } else set_cmnd_lunit(req); switch (req_hdr->scb[0]) { case SERVICE_ACTION_IN: if ((req_hdr->scb[1] & 0x1f) != 0x10) goto error; case INQUIRY: case REPORT_LUNS: case TEST_UNIT_READY: case SYNCHRONIZE_CACHE: case VERIFY: case VERIFY_16: case START_STOP: case READ_CAPACITY: case MODE_SENSE: case REQUEST_SENSE: case RESERVE: case RELEASE: case RESERVE_10: case RELEASE_10: { if (!(req_hdr->flags & ISCSI_CMD_FINAL) || req->pdu.datasize) { /* unexpected unsolicited data */ eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc); cmnd_skip_data(req); } break; } case READ_6: case READ_10: case READ_16: { loff_t offset; u32 length; if (!(req_hdr->flags & ISCSI_CMD_FINAL) || req->pdu.datasize) { /* unexpected unsolicited data */ eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc); cmnd_skip_data(req); } set_offset_and_length(req->lun, req_hdr->scb, &offset, &length); req->tio = tio_alloc(get_pgcnt(length, offset)); tio_set(req->tio, length, offset); break; } case WRITE_6: case WRITE_10: case WRITE_16: case WRITE_VERIFY: { struct iscsi_sess_param *param = &conn->session->param; loff_t offset; u32 length; req->r2t_length = be32_to_cpu(req_hdr->data_length) - req->pdu.datasize; req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL); req->target_task_tag = get_next_ttt(conn->session); if (!param->immediate_data && req->pdu.datasize) eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); if (param->initial_r2t && !(req_hdr->flags & ISCSI_CMD_FINAL)) eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); if (req_hdr->scb[0] == WRITE_VERIFY && req_hdr->scb[1] & 0x02) eprintk("Verification is ignored %x\n", cmnd_itt(req)); set_offset_and_length(req->lun, req_hdr->scb, &offset, &length); if (cmnd_write_size(req) != length) eprintk("%x %u %u\n", cmnd_itt(req), cmnd_write_size(req), length); req->tio = tio_alloc(get_pgcnt(length, offset)); tio_set(req->tio, length, offset); if (req->pdu.datasize) { if (cmnd_recv_pdu(conn, req->tio, 0, req->pdu.datasize) < 0) assert(0); } break; } error: default: eprintk("Unsupported %x\n", req_hdr->scb[0]); create_sense_rsp(req, ILLEGAL_REQUEST, 0x20, 0x0); cmnd_skip_data(req); break; }out: return;}static void data_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd){ struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs; struct iscsi_cmnd *scsi_cmnd = NULL; u32 offset = be32_to_cpu(req->buffer_offset); update_stat_sn(cmnd); cmnd->req = scsi_cmnd = cmnd_find_hash(conn->session, req->itt, req->ttt); if (!scsi_cmnd) { eprintk("unable to find scsi task %x %x\n", cmnd_itt(cmnd), cmnd_ttt(cmnd)); goto skip_data; } if (scsi_cmnd->r2t_length < cmnd->pdu.datasize) { eprintk("invalid data len %x %u %u\n", cmnd_itt(scsi_cmnd), cmnd->pdu.datasize, scsi_cmnd->r2t_length); goto skip_data; } if (scsi_cmnd->r2t_length + offset != cmnd_write_size(scsi_cmnd)) { eprintk("%x %u %u %u\n", cmnd_itt(scsi_cmnd), scsi_cmnd->r2t_length, offset, cmnd_write_size(scsi_cmnd)); goto skip_data; } scsi_cmnd->r2t_length -= cmnd->pdu.datasize; if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) { /* unsolicited burst data */ if (scsi_cmnd->pdu.bhs.flags & ISCSI_FLG_FINAL) { eprintk("unexpected data from %x %x\n", cmnd_itt(cmnd), cmnd_ttt(cmnd)); goto skip_data; } } dprintk(D_WRITE, "%u %p %p %p %u %u\n", req->ttt, cmnd, scsi_cmnd, scsi_cmnd->tio, offset, cmnd->pdu.datasize); if (cmnd_recv_pdu(conn, scsi_cmnd->tio, offset, cmnd->pdu.datasize) < 0) goto skip_data; return;skip_data: cmnd->pdu.bhs.opcode = ISCSI_OP_DATA_REJECT; cmnd_skip_pdu(cmnd); return;}static void data_out_end(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd){ struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *) &cmnd->pdu.bhs; struct iscsi_cmnd *scsi_cmnd; u32 offset; assert(cmnd); scsi_cmnd = cmnd->req; assert(scsi_cmnd); if (conn->read_overflow) { eprintk("%x %u\n", cmnd_itt(cmnd), conn->read_overflow); assert(scsi_cmnd->tio); offset = be32_to_cpu(req->buffer_offset); offset += cmnd->pdu.datasize - conn->read_overflow; if (cmnd_recv_pdu(conn, scsi_cmnd->tio, offset, conn->read_overflow) < 0) assert(0); return; } if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) { if (req->flags & ISCSI_FLG_FINAL) { scsi_cmnd->is_unsolicited_data = 0; if (!cmnd_pending(scsi_cmnd)) scsi_cmnd_exec(scsi_cmnd); } } else { /* TODO : proper error handling */ if (!(req->flags & ISCSI_FLG_FINAL) && scsi_cmnd->r2t_length == 0) eprintk("initiator error %x\n", cmnd_itt(scsi_cmnd)); if (!(req->flags & ISCSI_FLG_FINAL)) goto out; scsi_cmnd->outstanding_r2t--; if (scsi_cmnd->r2t_length == 0) assert(list_empty(&scsi_cmnd->pdu_list)); scsi_cmnd_exec(scsi_cmnd); }out: iscsi_cmnd_remove(cmnd); return;}static int __cmnd_abort(struct iscsi_cmnd *cmnd){ if (!cmnd_waitio(cmnd)) { cmnd_release(cmnd, 1); return 0; } else return -ISCSI_RESPONSE_UNKNOWN_TASK;}static int cmnd_abort(struct iscsi_session *session, u32 itt){ struct iscsi_cmnd *cmnd; int err = -ISCSI_RESPONSE_UNKNOWN_TASK; if ((cmnd = cmnd_find_hash(session, itt, ISCSI_RESERVED_TAG))) { eprintk("%x %x %x %u %u %u %u\n", cmnd_itt(cmnd), cmnd_opcode(cmnd), cmnd->r2t_length, cmnd_scsicode(cmnd), cmnd_write_size(cmnd), cmnd->is_unsolicited_data, cmnd->outstanding_r2t); err = __cmnd_abort(cmnd); } return err;}static int target_reset(struct iscsi_cmnd *req, u32 lun, int all){ struct iscsi_target *target = req->conn->session->target; struct iscsi_session *session; struct iscsi_conn *conn; struct iscsi_cmnd *cmnd, *tmp; list_for_each_entry(session, &target->session_list, list) { list_for_each_entry(conn, &session->conn_list, list) { list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -