📄 iscsi-xmit-pdu.c
字号:
}voidiscsi_send_logout(struct iscsi_session *session){ struct iscsi_logout_hdr stlh; u32 itt; int rc; spin_lock_bh(&session->task_lock); itt = iscsi_alloc_itt(session); spin_unlock_bh(&session->task_lock); memset(&stlh, 0, sizeof(stlh)); stlh.opcode = ISCSI_OP_LOGOUT_CMD | ISCSI_OP_IMMEDIATE; stlh.flags = ISCSI_FLAG_FINAL | (ISCSI_LOGOUT_REASON_CLOSE_SESSION & ISCSI_FLAG_LOGOUT_REASON_MASK); stlh.itt = htonl(itt); stlh.cmdsn = htonl(session->cmd_sn); stlh.expstatsn = htonl(session->exp_stat_sn); rc = iscsi_send_header(session, (struct iscsi_hdr *)&stlh, session->header_digest); if (rc != ISCSI_IO_SUCCESS) { iscsi_host_err(session, "xmit_logout failed\n"); /* drop here ? */ iscsi_drop_session(session); }}/** * iscsi_send_data_out - send a SCSI Data-out PDU * @task: iscsi task * @ttt: target transfer tag * @data_offset: offset of transfer within the complete transfer * @data_len: data trasnfer length * * Note: * If command PDUs are small (no immediate data), we * start new commands as soon as possible, so that we can * overlap the R2T latency with the time it takes to * send data for commands already issued. This increases * throughput without significantly increasing the completion * time of commands already issued. **/static intiscsi_send_data_out(struct iscsi_task *task, u32 ttt, u32 data_offset, u32 data_len){ struct iscsi_session *session = task->session; struct scsi_cmnd *sc = task->scsi_cmnd; struct scatterlist tmpsg, *sg; struct iscsi_data_hdr stdh; u32 data_sn = 0, dlen, remaining, sg_offset; int i, rc = ISCSI_IO_SUCCESS; memset(&stdh, 0, sizeof(stdh)); stdh.opcode = ISCSI_OP_SCSI_DATA; stdh.itt = htonl(task->itt); stdh.ttt = ttt; /* * Find the right sg entry and offset into it if needed. * Why do we not cache this index for DataPDUInOrder? */ sg_offset = data_offset; sg = sc->request_buffer; for (i = 0; i < sc->use_sg; i++) { if (sg_offset < sg->length) break; else { sg_offset -= sg->length; sg++; } } /* * check that the target did not send us some bad values. just * let the cmnd timeout if it does. */ if (sc->request_bufflen < data_offset + data_len || (sc->use_sg && i >= sc->use_sg)) { iscsi_host_err(session, "iscsi_send_data_out - invalid write. " "len %u, offset %u, request_bufflen %u, usg_sg " "%u, task %u\n", data_len, data_offset, sc->request_bufflen, sc->use_sg, task->itt); return ISCSI_IO_INVALID_OP; } /* * PDU loop - might need to send multiple PDUs to satisfy * the transfer, or we can also send a zero length PDU */ remaining = data_len; do { if (signal_pending(current)) { rc = ISCSI_IO_INTR; break; } if (!session->immediate_data) iscsi_run_pending_queue(session); stdh.datasn = htonl(data_sn++); stdh.offset = htonl(data_offset); stdh.expstatsn = htonl(session->exp_stat_sn); if (session->max_xmit_data_segment_len && remaining > session->max_xmit_data_segment_len) /* enforce the target's data segment limit */ dlen = session->max_xmit_data_segment_len; else { /* final PDU of a data burst */ dlen = remaining; stdh.flags = ISCSI_FLAG_FINAL; } hton24(stdh.dlength, dlen); rc = iscsi_send_header(session, (struct iscsi_hdr *)&stdh, session->header_digest); if (rc != ISCSI_IO_SUCCESS) { iscsi_drop_session(session); break; } if (sc->use_sg) rc = iscsi_send_sg_data(session, sc->request_buffer, &i, &sg_offset, sc->use_sg, dlen, session->data_digest); else { sg_init_one(&tmpsg, sc->request_buffer, dlen); rc = iscsi_send_sg_data(session, &tmpsg, &i, &sg_offset, 1, dlen, session->data_digest); } if (rc != ISCSI_IO_SUCCESS && rc != ISCSI_IO_INVALID_OP) iscsi_drop_session(session); data_offset += dlen; remaining -= dlen; } while (remaining > 0 && rc == ISCSI_IO_SUCCESS); return rc;}static inline unsignedget_immediate_data_len(struct iscsi_session *session, struct scsi_cmnd *sc){ int len; if (!session->immediate_data) return 0; if (session->first_burst_len) len = min(session->first_burst_len, session->max_xmit_data_segment_len); else len = session->max_xmit_data_segment_len; return min_t(unsigned, len, sc->request_bufflen);}/* * iscsi_queue_r2t may be called so the task lock must be held * why not handle this in iscsi_send_scsi_cmnd? */voidiscsi_queue_unsolicited_data(struct iscsi_task *task){ unsigned imm_data_len; struct iscsi_session *session = task->session; struct scsi_cmnd *sc = task->scsi_cmnd; /* * With ImmediateData, we may or may not have to send * additional Data PDUs, depending on the amount of data, and * the Max PDU Length, and the first_burst_len. */ if (!test_bit(ISCSI_TASK_WRITE, &task->flags) || !sc->request_bufflen || session->initial_r2t) return; /* * queue up unsolicited data PDUs. the implied initial R2T * doesn't count against the MaxOutstandingR2T, so we can't use * the normal R2T * fields of the task for the implied initial * R2T. Use a special flag for the implied initial R2T, and * let the rx thread update tasks in the tx_tasks collection * if an R2T comes in before the implied initial R2T has been * processed. */ if (session->immediate_data) { imm_data_len = get_immediate_data_len(session, sc); /* * Only queue unsolicited data out PDUs if there is more * data in the request, and the FirstBurstLength hasn't * already been satisfied with the ImmediateData that * will be sent below via iscsi_send_scsi_cmnd(). */ if (sc->request_bufflen <= imm_data_len || imm_data_len >= session->first_burst_len) return; } __set_bit(ISCSI_TASK_INITIAL_R2T, &task->flags); iscsi_queue_r2t(session, task); set_bit(TX_DATA, &session->control_bits); set_bit(TX_WAKE, &session->control_bits);}/** * iscsi_send_r2t_data - see if we need to send more data. * @session: iscsi session * * Note: * This may call iscsi_run_pending_queue under some conditions. **/voidiscsi_send_r2t_data(struct iscsi_session *session){ struct iscsi_task *task; struct scsi_cmnd *sc; u32 ttt, offset, len; unsigned implied_len, imm_data_len; int rc; spin_lock_bh(&session->task_lock); retry: task = iscsi_dequeue_r2t(session); if (!task) goto done; rc = ISCSI_IO_SUCCESS; /* * save the values that get set when we receive an R2T from * the target, so that we can receive another one while * we're sending data. */ ttt = task->ttt; offset = task->data_offset; len = task->data_length; task->ttt = ISCSI_RSVD_TASK_TAG; spin_unlock_bh(&session->task_lock); /* * implied initial R2T * (ISCSI_TASK_INITIAL_R2T bit is only accessed by tx * thread so we do not need atomic ops) */ if (__test_and_clear_bit(ISCSI_TASK_INITIAL_R2T, &task->flags)) { sc = task->scsi_cmnd; /* * FirstBurstLength == 0 means no limit when * ImmediateData == 0 (not documented in README?) */ if (!session->first_burst_len) implied_len = sc->request_bufflen; else implied_len = min_t(unsigned, session->first_burst_len, sc->request_bufflen); if (session->immediate_data) { imm_data_len = get_immediate_data_len(session, sc); implied_len -= imm_data_len; } else imm_data_len = 0; rc = iscsi_send_data_out(task, ISCSI_RSVD_TASK_TAG, imm_data_len, implied_len); } /* normal R2T from the target */ if (ttt != ISCSI_RSVD_TASK_TAG && rc == ISCSI_IO_SUCCESS) iscsi_send_data_out(task, ttt, offset, len); spin_lock_bh(&session->task_lock); __iscsi_put_task(task); if (!signal_pending(current)) goto retry; done: spin_unlock_bh(&session->task_lock);}/** * iscsi_send_scsi_cmnd - Transmit iSCSI Command PDU. * @task: iSCSI task to be transmitted * * Description: * The header digest on the cmd PDU is calculated before sending the cmd. * If ImmediateData is enabled, data digest is computed and data is sent * along with cmd PDU. **/voidiscsi_send_scsi_cmnd(struct iscsi_task *task){ struct iscsi_scsi_cmd_hdr stsch; struct iscsi_session *session = task->session; struct scsi_cmnd *sc = task->scsi_cmnd; int rc, first_sg = 0; struct scatterlist tmpsg; u32 imm_data_len = 0, sg_offset = 0; memset(&stsch, 0, sizeof(stsch)); if (test_bit(ISCSI_TASK_READ, &task->flags)) { stsch.flags |= ISCSI_FLAG_CMD_READ; stsch.data_length = htonl(sc->request_bufflen); } else if (test_bit(ISCSI_TASK_WRITE, &task->flags)) { stsch.flags |= ISCSI_FLAG_CMD_WRITE; stsch.data_length = htonl(sc->request_bufflen); } /* tagged command queueing */ stsch.flags |= (iscsi_command_attr(sc) & ISCSI_FLAG_CMD_ATTR_MASK); stsch.opcode = ISCSI_OP_SCSI_CMD; stsch.itt = htonl(task->itt); task->cmdsn = session->cmd_sn; stsch.cmdsn = htonl(session->cmd_sn); stsch.expstatsn = htonl(session->exp_stat_sn); /* * set the final bit when there are no unsolicited Data-out * PDUs following the command PDU */ if (!test_bit(ISCSI_TASK_INITIAL_R2T, &task->flags)) stsch.flags |= ISCSI_FLAG_FINAL; /* single level LUN format puts LUN in byte 1, 0 everywhere else */ stsch.lun[1] = sc->device->lun; memcpy(stsch.scb, sc->cmnd, min_t(size_t, sizeof(stsch.scb), sc->cmd_len)); if (session->immediate_data && sc->sc_data_direction == DMA_TO_DEVICE) { if (!sc->request_bufflen) /* zero len write? just let it timeout */ return; imm_data_len = get_immediate_data_len(session, sc); /* put the data length in the PDU header */ hton24(stsch.dlength, imm_data_len); stsch.data_length = htonl(sc->request_bufflen); } rc = iscsi_send_header(session, (struct iscsi_hdr *)&stsch, session->header_digest); if (rc != ISCSI_IO_SUCCESS) { iscsi_host_err(session, "iscsi_send_scsi_cmnd failed to send " "scsi cmnd header\n"); iscsi_drop_session(session); return; } if (!imm_data_len) goto done; if (sc->use_sg) rc = iscsi_send_sg_data(session, sc->request_buffer, &first_sg, &sg_offset, sc->use_sg, imm_data_len, session->data_digest); else { sg_init_one(&tmpsg, sc->request_buffer, imm_data_len); rc = iscsi_send_sg_data(session, &tmpsg, &first_sg, &sg_offset, 1, imm_data_len, session->data_digest); } if (rc != ISCSI_IO_SUCCESS) { iscsi_host_err(session, "iscsi_send_scsi_cmnd failed to send " "scsi cmnd data (%u bytes)\n", imm_data_len); if (rc != ISCSI_IO_INVALID_OP) iscsi_drop_session(session); } done: session->cmd_sn++;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -