123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- /*******************************************************************************
- * This file contains the iSCSI Target DataIN value generation functions.
- *
- * (c) Copyright 2007-2013 Datera, Inc.
- *
- * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- ******************************************************************************/
- #include <scsi/iscsi_proto.h>
- #include <target/iscsi/iscsi_target_core.h>
- #include "iscsi_target_seq_pdu_list.h"
- #include "iscsi_target_erl1.h"
- #include "iscsi_target_util.h"
- #include "iscsi_target.h"
- #include "iscsi_target_datain_values.h"
- struct iscsi_datain_req *iscsit_allocate_datain_req(void)
- {
- struct iscsi_datain_req *dr;
- dr = kmem_cache_zalloc(lio_dr_cache, GFP_ATOMIC);
- if (!dr) {
- pr_err("Unable to allocate memory for"
- " struct iscsi_datain_req\n");
- return NULL;
- }
- INIT_LIST_HEAD(&dr->cmd_datain_node);
- return dr;
- }
- void iscsit_attach_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
- {
- spin_lock(&cmd->datain_lock);
- list_add_tail(&dr->cmd_datain_node, &cmd->datain_list);
- spin_unlock(&cmd->datain_lock);
- }
- void iscsit_free_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
- {
- spin_lock(&cmd->datain_lock);
- list_del(&dr->cmd_datain_node);
- spin_unlock(&cmd->datain_lock);
- kmem_cache_free(lio_dr_cache, dr);
- }
- void iscsit_free_all_datain_reqs(struct iscsi_cmd *cmd)
- {
- struct iscsi_datain_req *dr, *dr_tmp;
- spin_lock(&cmd->datain_lock);
- list_for_each_entry_safe(dr, dr_tmp, &cmd->datain_list, cmd_datain_node) {
- list_del(&dr->cmd_datain_node);
- kmem_cache_free(lio_dr_cache, dr);
- }
- spin_unlock(&cmd->datain_lock);
- }
- struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *cmd)
- {
- if (list_empty(&cmd->datain_list)) {
- pr_err("cmd->datain_list is empty for ITT:"
- " 0x%08x\n", cmd->init_task_tag);
- return NULL;
- }
- return list_first_entry(&cmd->datain_list, struct iscsi_datain_req,
- cmd_datain_node);
- }
- /*
- * For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=Yes.
- */
- static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_yes(
- struct iscsi_cmd *cmd,
- struct iscsi_datain *datain)
- {
- u32 next_burst_len, read_data_done, read_data_left;
- struct iscsi_conn *conn = cmd->conn;
- struct iscsi_datain_req *dr;
- dr = iscsit_get_datain_req(cmd);
- if (!dr)
- return NULL;
- if (dr->recovery && dr->generate_recovery_values) {
- if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
- cmd, dr) < 0)
- return NULL;
- dr->generate_recovery_values = 0;
- }
- next_burst_len = (!dr->recovery) ?
- cmd->next_burst_len : dr->next_burst_len;
- read_data_done = (!dr->recovery) ?
- cmd->read_data_done : dr->read_data_done;
- read_data_left = (cmd->se_cmd.data_length - read_data_done);
- if (!read_data_left) {
- pr_err("ITT: 0x%08x read_data_left is zero!\n",
- cmd->init_task_tag);
- return NULL;
- }
- if ((read_data_left <= conn->conn_ops->MaxRecvDataSegmentLength) &&
- (read_data_left <= (conn->sess->sess_ops->MaxBurstLength -
- next_burst_len))) {
- datain->length = read_data_left;
- datain->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
- if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
- datain->flags |= ISCSI_FLAG_DATA_ACK;
- } else {
- if ((next_burst_len +
- conn->conn_ops->MaxRecvDataSegmentLength) <
- conn->sess->sess_ops->MaxBurstLength) {
- datain->length =
- conn->conn_ops->MaxRecvDataSegmentLength;
- next_burst_len += datain->length;
- } else {
- datain->length = (conn->sess->sess_ops->MaxBurstLength -
- next_burst_len);
- next_burst_len = 0;
- datain->flags |= ISCSI_FLAG_CMD_FINAL;
- if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
- datain->flags |= ISCSI_FLAG_DATA_ACK;
- }
- }
- datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
- datain->offset = read_data_done;
- if (!dr->recovery) {
- cmd->next_burst_len = next_burst_len;
- cmd->read_data_done += datain->length;
- } else {
- dr->next_burst_len = next_burst_len;
- dr->read_data_done += datain->length;
- }
- if (!dr->recovery) {
- if (datain->flags & ISCSI_FLAG_DATA_STATUS)
- dr->dr_complete = DATAIN_COMPLETE_NORMAL;
- return dr;
- }
- if (!dr->runlength) {
- if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
- dr->dr_complete =
- (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
- DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
- DATAIN_COMPLETE_CONNECTION_RECOVERY;
- }
- } else {
- if ((dr->begrun + dr->runlength) == dr->data_sn) {
- dr->dr_complete =
- (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
- DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
- DATAIN_COMPLETE_CONNECTION_RECOVERY;
- }
- }
- return dr;
- }
- /*
- * For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=Yes.
- */
- static struct iscsi_datain_req *iscsit_set_datain_values_no_and_yes(
- struct iscsi_cmd *cmd,
- struct iscsi_datain *datain)
- {
- u32 offset, read_data_done, read_data_left, seq_send_order;
- struct iscsi_conn *conn = cmd->conn;
- struct iscsi_datain_req *dr;
- struct iscsi_seq *seq;
- dr = iscsit_get_datain_req(cmd);
- if (!dr)
- return NULL;
- if (dr->recovery && dr->generate_recovery_values) {
- if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
- cmd, dr) < 0)
- return NULL;
- dr->generate_recovery_values = 0;
- }
- read_data_done = (!dr->recovery) ?
- cmd->read_data_done : dr->read_data_done;
- seq_send_order = (!dr->recovery) ?
- cmd->seq_send_order : dr->seq_send_order;
- read_data_left = (cmd->se_cmd.data_length - read_data_done);
- if (!read_data_left) {
- pr_err("ITT: 0x%08x read_data_left is zero!\n",
- cmd->init_task_tag);
- return NULL;
- }
- seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
- if (!seq)
- return NULL;
- seq->sent = 1;
- if (!dr->recovery && !seq->next_burst_len)
- seq->first_datasn = cmd->data_sn;
- offset = (seq->offset + seq->next_burst_len);
- if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
- cmd->se_cmd.data_length) {
- datain->length = (cmd->se_cmd.data_length - offset);
- datain->offset = offset;
- datain->flags |= ISCSI_FLAG_CMD_FINAL;
- if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
- datain->flags |= ISCSI_FLAG_DATA_ACK;
- seq->next_burst_len = 0;
- seq_send_order++;
- } else {
- if ((seq->next_burst_len +
- conn->conn_ops->MaxRecvDataSegmentLength) <
- conn->sess->sess_ops->MaxBurstLength) {
- datain->length =
- conn->conn_ops->MaxRecvDataSegmentLength;
- datain->offset = (seq->offset + seq->next_burst_len);
- seq->next_burst_len += datain->length;
- } else {
- datain->length = (conn->sess->sess_ops->MaxBurstLength -
- seq->next_burst_len);
- datain->offset = (seq->offset + seq->next_burst_len);
- datain->flags |= ISCSI_FLAG_CMD_FINAL;
- if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
- datain->flags |= ISCSI_FLAG_DATA_ACK;
- seq->next_burst_len = 0;
- seq_send_order++;
- }
- }
- if ((read_data_done + datain->length) == cmd->se_cmd.data_length)
- datain->flags |= ISCSI_FLAG_DATA_STATUS;
- datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
- if (!dr->recovery) {
- cmd->seq_send_order = seq_send_order;
- cmd->read_data_done += datain->length;
- } else {
- dr->seq_send_order = seq_send_order;
- dr->read_data_done += datain->length;
- }
- if (!dr->recovery) {
- if (datain->flags & ISCSI_FLAG_CMD_FINAL)
- seq->last_datasn = datain->data_sn;
- if (datain->flags & ISCSI_FLAG_DATA_STATUS)
- dr->dr_complete = DATAIN_COMPLETE_NORMAL;
- return dr;
- }
- if (!dr->runlength) {
- if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
- dr->dr_complete =
- (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
- DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
- DATAIN_COMPLETE_CONNECTION_RECOVERY;
- }
- } else {
- if ((dr->begrun + dr->runlength) == dr->data_sn) {
- dr->dr_complete =
- (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
- DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
- DATAIN_COMPLETE_CONNECTION_RECOVERY;
- }
- }
- return dr;
- }
- /*
- * For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=No.
- */
- static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_no(
- struct iscsi_cmd *cmd,
- struct iscsi_datain *datain)
- {
- u32 next_burst_len, read_data_done, read_data_left;
- struct iscsi_conn *conn = cmd->conn;
- struct iscsi_datain_req *dr;
- struct iscsi_pdu *pdu;
- dr = iscsit_get_datain_req(cmd);
- if (!dr)
- return NULL;
- if (dr->recovery && dr->generate_recovery_values) {
- if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
- cmd, dr) < 0)
- return NULL;
- dr->generate_recovery_values = 0;
- }
- next_burst_len = (!dr->recovery) ?
- cmd->next_burst_len : dr->next_burst_len;
- read_data_done = (!dr->recovery) ?
- cmd->read_data_done : dr->read_data_done;
- read_data_left = (cmd->se_cmd.data_length - read_data_done);
- if (!read_data_left) {
- pr_err("ITT: 0x%08x read_data_left is zero!\n",
- cmd->init_task_tag);
- return dr;
- }
- pdu = iscsit_get_pdu_holder_for_seq(cmd, NULL);
- if (!pdu)
- return dr;
- if ((read_data_done + pdu->length) == cmd->se_cmd.data_length) {
- pdu->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
- if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
- pdu->flags |= ISCSI_FLAG_DATA_ACK;
- next_burst_len = 0;
- } else {
- if ((next_burst_len + conn->conn_ops->MaxRecvDataSegmentLength) <
- conn->sess->sess_ops->MaxBurstLength)
- next_burst_len += pdu->length;
- else {
- pdu->flags |= ISCSI_FLAG_CMD_FINAL;
- if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
- pdu->flags |= ISCSI_FLAG_DATA_ACK;
- next_burst_len = 0;
- }
- }
- pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
- if (!dr->recovery) {
- cmd->next_burst_len = next_burst_len;
- cmd->read_data_done += pdu->length;
- } else {
- dr->next_burst_len = next_burst_len;
- dr->read_data_done += pdu->length;
- }
- datain->flags = pdu->flags;
- datain->length = pdu->length;
- datain->offset = pdu->offset;
- datain->data_sn = pdu->data_sn;
- if (!dr->recovery) {
- if (datain->flags & ISCSI_FLAG_DATA_STATUS)
- dr->dr_complete = DATAIN_COMPLETE_NORMAL;
- return dr;
- }
- if (!dr->runlength) {
- if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
- dr->dr_complete =
- (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
- DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
- DATAIN_COMPLETE_CONNECTION_RECOVERY;
- }
- } else {
- if ((dr->begrun + dr->runlength) == dr->data_sn) {
- dr->dr_complete =
- (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
- DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
- DATAIN_COMPLETE_CONNECTION_RECOVERY;
- }
- }
- return dr;
- }
- /*
- * For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=No.
- */
- static struct iscsi_datain_req *iscsit_set_datain_values_no_and_no(
- struct iscsi_cmd *cmd,
- struct iscsi_datain *datain)
- {
- u32 read_data_done, read_data_left, seq_send_order;
- struct iscsi_conn *conn = cmd->conn;
- struct iscsi_datain_req *dr;
- struct iscsi_pdu *pdu;
- struct iscsi_seq *seq = NULL;
- dr = iscsit_get_datain_req(cmd);
- if (!dr)
- return NULL;
- if (dr->recovery && dr->generate_recovery_values) {
- if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
- cmd, dr) < 0)
- return NULL;
- dr->generate_recovery_values = 0;
- }
- read_data_done = (!dr->recovery) ?
- cmd->read_data_done : dr->read_data_done;
- seq_send_order = (!dr->recovery) ?
- cmd->seq_send_order : dr->seq_send_order;
- read_data_left = (cmd->se_cmd.data_length - read_data_done);
- if (!read_data_left) {
- pr_err("ITT: 0x%08x read_data_left is zero!\n",
- cmd->init_task_tag);
- return NULL;
- }
- seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
- if (!seq)
- return NULL;
- seq->sent = 1;
- if (!dr->recovery && !seq->next_burst_len)
- seq->first_datasn = cmd->data_sn;
- pdu = iscsit_get_pdu_holder_for_seq(cmd, seq);
- if (!pdu)
- return NULL;
- if (seq->pdu_send_order == seq->pdu_count) {
- pdu->flags |= ISCSI_FLAG_CMD_FINAL;
- if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
- pdu->flags |= ISCSI_FLAG_DATA_ACK;
- seq->next_burst_len = 0;
- seq_send_order++;
- } else
- seq->next_burst_len += pdu->length;
- if ((read_data_done + pdu->length) == cmd->se_cmd.data_length)
- pdu->flags |= ISCSI_FLAG_DATA_STATUS;
- pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
- if (!dr->recovery) {
- cmd->seq_send_order = seq_send_order;
- cmd->read_data_done += pdu->length;
- } else {
- dr->seq_send_order = seq_send_order;
- dr->read_data_done += pdu->length;
- }
- datain->flags = pdu->flags;
- datain->length = pdu->length;
- datain->offset = pdu->offset;
- datain->data_sn = pdu->data_sn;
- if (!dr->recovery) {
- if (datain->flags & ISCSI_FLAG_CMD_FINAL)
- seq->last_datasn = datain->data_sn;
- if (datain->flags & ISCSI_FLAG_DATA_STATUS)
- dr->dr_complete = DATAIN_COMPLETE_NORMAL;
- return dr;
- }
- if (!dr->runlength) {
- if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
- dr->dr_complete =
- (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
- DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
- DATAIN_COMPLETE_CONNECTION_RECOVERY;
- }
- } else {
- if ((dr->begrun + dr->runlength) == dr->data_sn) {
- dr->dr_complete =
- (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
- DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
- DATAIN_COMPLETE_CONNECTION_RECOVERY;
- }
- }
- return dr;
- }
- struct iscsi_datain_req *iscsit_get_datain_values(
- struct iscsi_cmd *cmd,
- struct iscsi_datain *datain)
- {
- struct iscsi_conn *conn = cmd->conn;
- if (conn->sess->sess_ops->DataSequenceInOrder &&
- conn->sess->sess_ops->DataPDUInOrder)
- return iscsit_set_datain_values_yes_and_yes(cmd, datain);
- else if (!conn->sess->sess_ops->DataSequenceInOrder &&
- conn->sess->sess_ops->DataPDUInOrder)
- return iscsit_set_datain_values_no_and_yes(cmd, datain);
- else if (conn->sess->sess_ops->DataSequenceInOrder &&
- !conn->sess->sess_ops->DataPDUInOrder)
- return iscsit_set_datain_values_yes_and_no(cmd, datain);
- else if (!conn->sess->sess_ops->DataSequenceInOrder &&
- !conn->sess->sess_ops->DataPDUInOrder)
- return iscsit_set_datain_values_no_and_no(cmd, datain);
- return NULL;
- }
|