520 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*******************************************************************************3 * This file contains the iSCSI Target DataIN value generation functions.4 *5 * (c) Copyright 2007-2013 Datera, Inc.6 *7 * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>8 *9 ******************************************************************************/10 11#include <linux/slab.h>12#include <scsi/iscsi_proto.h>13#include <target/iscsi/iscsi_target_core.h>14#include "iscsi_target_seq_pdu_list.h"15#include "iscsi_target_erl1.h"16#include "iscsi_target_util.h"17#include "iscsi_target.h"18#include "iscsi_target_datain_values.h"19 20struct iscsi_datain_req *iscsit_allocate_datain_req(void)21{22 struct iscsi_datain_req *dr;23 24 dr = kmem_cache_zalloc(lio_dr_cache, GFP_ATOMIC);25 if (!dr) {26 pr_err("Unable to allocate memory for"27 " struct iscsi_datain_req\n");28 return NULL;29 }30 INIT_LIST_HEAD(&dr->cmd_datain_node);31 32 return dr;33}34 35void iscsit_attach_datain_req(struct iscsit_cmd *cmd, struct iscsi_datain_req *dr)36{37 spin_lock(&cmd->datain_lock);38 list_add_tail(&dr->cmd_datain_node, &cmd->datain_list);39 spin_unlock(&cmd->datain_lock);40}41 42void iscsit_free_datain_req(struct iscsit_cmd *cmd, struct iscsi_datain_req *dr)43{44 spin_lock(&cmd->datain_lock);45 list_del(&dr->cmd_datain_node);46 spin_unlock(&cmd->datain_lock);47 48 kmem_cache_free(lio_dr_cache, dr);49}50 51void iscsit_free_all_datain_reqs(struct iscsit_cmd *cmd)52{53 struct iscsi_datain_req *dr, *dr_tmp;54 55 spin_lock(&cmd->datain_lock);56 list_for_each_entry_safe(dr, dr_tmp, &cmd->datain_list, cmd_datain_node) {57 list_del(&dr->cmd_datain_node);58 kmem_cache_free(lio_dr_cache, dr);59 }60 spin_unlock(&cmd->datain_lock);61}62 63struct iscsi_datain_req *iscsit_get_datain_req(struct iscsit_cmd *cmd)64{65 if (list_empty(&cmd->datain_list)) {66 pr_err("cmd->datain_list is empty for ITT:"67 " 0x%08x\n", cmd->init_task_tag);68 return NULL;69 }70 71 return list_first_entry(&cmd->datain_list, struct iscsi_datain_req,72 cmd_datain_node);73}74 75/*76 * For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=Yes.77 */78static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_yes(79 struct iscsit_cmd *cmd,80 struct iscsi_datain *datain)81{82 u32 next_burst_len, read_data_done, read_data_left;83 struct iscsit_conn *conn = cmd->conn;84 struct iscsi_datain_req *dr;85 86 dr = iscsit_get_datain_req(cmd);87 if (!dr)88 return NULL;89 90 if (dr->recovery && dr->generate_recovery_values) {91 if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(92 cmd, dr) < 0)93 return NULL;94 95 dr->generate_recovery_values = 0;96 }97 98 next_burst_len = (!dr->recovery) ?99 cmd->next_burst_len : dr->next_burst_len;100 read_data_done = (!dr->recovery) ?101 cmd->read_data_done : dr->read_data_done;102 103 read_data_left = (cmd->se_cmd.data_length - read_data_done);104 if (!read_data_left) {105 pr_err("ITT: 0x%08x read_data_left is zero!\n",106 cmd->init_task_tag);107 return NULL;108 }109 110 if ((read_data_left <= conn->conn_ops->MaxRecvDataSegmentLength) &&111 (read_data_left <= (conn->sess->sess_ops->MaxBurstLength -112 next_burst_len))) {113 datain->length = read_data_left;114 115 datain->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);116 if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)117 datain->flags |= ISCSI_FLAG_DATA_ACK;118 } else {119 if ((next_burst_len +120 conn->conn_ops->MaxRecvDataSegmentLength) <121 conn->sess->sess_ops->MaxBurstLength) {122 datain->length =123 conn->conn_ops->MaxRecvDataSegmentLength;124 next_burst_len += datain->length;125 } else {126 datain->length = (conn->sess->sess_ops->MaxBurstLength -127 next_burst_len);128 next_burst_len = 0;129 130 datain->flags |= ISCSI_FLAG_CMD_FINAL;131 if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)132 datain->flags |= ISCSI_FLAG_DATA_ACK;133 }134 }135 136 datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;137 datain->offset = read_data_done;138 139 if (!dr->recovery) {140 cmd->next_burst_len = next_burst_len;141 cmd->read_data_done += datain->length;142 } else {143 dr->next_burst_len = next_burst_len;144 dr->read_data_done += datain->length;145 }146 147 if (!dr->recovery) {148 if (datain->flags & ISCSI_FLAG_DATA_STATUS)149 dr->dr_complete = DATAIN_COMPLETE_NORMAL;150 151 return dr;152 }153 154 if (!dr->runlength) {155 if (datain->flags & ISCSI_FLAG_DATA_STATUS) {156 dr->dr_complete =157 (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?158 DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :159 DATAIN_COMPLETE_CONNECTION_RECOVERY;160 }161 } else {162 if ((dr->begrun + dr->runlength) == dr->data_sn) {163 dr->dr_complete =164 (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?165 DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :166 DATAIN_COMPLETE_CONNECTION_RECOVERY;167 }168 }169 170 return dr;171}172 173/*174 * For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=Yes.175 */176static struct iscsi_datain_req *iscsit_set_datain_values_no_and_yes(177 struct iscsit_cmd *cmd,178 struct iscsi_datain *datain)179{180 u32 offset, read_data_done, read_data_left, seq_send_order;181 struct iscsit_conn *conn = cmd->conn;182 struct iscsi_datain_req *dr;183 struct iscsi_seq *seq;184 185 dr = iscsit_get_datain_req(cmd);186 if (!dr)187 return NULL;188 189 if (dr->recovery && dr->generate_recovery_values) {190 if (iscsit_create_recovery_datain_values_datasequenceinorder_no(191 cmd, dr) < 0)192 return NULL;193 194 dr->generate_recovery_values = 0;195 }196 197 read_data_done = (!dr->recovery) ?198 cmd->read_data_done : dr->read_data_done;199 seq_send_order = (!dr->recovery) ?200 cmd->seq_send_order : dr->seq_send_order;201 202 read_data_left = (cmd->se_cmd.data_length - read_data_done);203 if (!read_data_left) {204 pr_err("ITT: 0x%08x read_data_left is zero!\n",205 cmd->init_task_tag);206 return NULL;207 }208 209 seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);210 if (!seq)211 return NULL;212 213 seq->sent = 1;214 215 if (!dr->recovery && !seq->next_burst_len)216 seq->first_datasn = cmd->data_sn;217 218 offset = (seq->offset + seq->next_burst_len);219 220 if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=221 cmd->se_cmd.data_length) {222 datain->length = (cmd->se_cmd.data_length - offset);223 datain->offset = offset;224 225 datain->flags |= ISCSI_FLAG_CMD_FINAL;226 if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)227 datain->flags |= ISCSI_FLAG_DATA_ACK;228 229 seq->next_burst_len = 0;230 seq_send_order++;231 } else {232 if ((seq->next_burst_len +233 conn->conn_ops->MaxRecvDataSegmentLength) <234 conn->sess->sess_ops->MaxBurstLength) {235 datain->length =236 conn->conn_ops->MaxRecvDataSegmentLength;237 datain->offset = (seq->offset + seq->next_burst_len);238 239 seq->next_burst_len += datain->length;240 } else {241 datain->length = (conn->sess->sess_ops->MaxBurstLength -242 seq->next_burst_len);243 datain->offset = (seq->offset + seq->next_burst_len);244 245 datain->flags |= ISCSI_FLAG_CMD_FINAL;246 if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)247 datain->flags |= ISCSI_FLAG_DATA_ACK;248 249 seq->next_burst_len = 0;250 seq_send_order++;251 }252 }253 254 if ((read_data_done + datain->length) == cmd->se_cmd.data_length)255 datain->flags |= ISCSI_FLAG_DATA_STATUS;256 257 datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;258 if (!dr->recovery) {259 cmd->seq_send_order = seq_send_order;260 cmd->read_data_done += datain->length;261 } else {262 dr->seq_send_order = seq_send_order;263 dr->read_data_done += datain->length;264 }265 266 if (!dr->recovery) {267 if (datain->flags & ISCSI_FLAG_CMD_FINAL)268 seq->last_datasn = datain->data_sn;269 if (datain->flags & ISCSI_FLAG_DATA_STATUS)270 dr->dr_complete = DATAIN_COMPLETE_NORMAL;271 272 return dr;273 }274 275 if (!dr->runlength) {276 if (datain->flags & ISCSI_FLAG_DATA_STATUS) {277 dr->dr_complete =278 (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?279 DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :280 DATAIN_COMPLETE_CONNECTION_RECOVERY;281 }282 } else {283 if ((dr->begrun + dr->runlength) == dr->data_sn) {284 dr->dr_complete =285 (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?286 DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :287 DATAIN_COMPLETE_CONNECTION_RECOVERY;288 }289 }290 291 return dr;292}293 294/*295 * For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=No.296 */297static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_no(298 struct iscsit_cmd *cmd,299 struct iscsi_datain *datain)300{301 u32 next_burst_len, read_data_done, read_data_left;302 struct iscsit_conn *conn = cmd->conn;303 struct iscsi_datain_req *dr;304 struct iscsi_pdu *pdu;305 306 dr = iscsit_get_datain_req(cmd);307 if (!dr)308 return NULL;309 310 if (dr->recovery && dr->generate_recovery_values) {311 if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(312 cmd, dr) < 0)313 return NULL;314 315 dr->generate_recovery_values = 0;316 }317 318 next_burst_len = (!dr->recovery) ?319 cmd->next_burst_len : dr->next_burst_len;320 read_data_done = (!dr->recovery) ?321 cmd->read_data_done : dr->read_data_done;322 323 read_data_left = (cmd->se_cmd.data_length - read_data_done);324 if (!read_data_left) {325 pr_err("ITT: 0x%08x read_data_left is zero!\n",326 cmd->init_task_tag);327 return dr;328 }329 330 pdu = iscsit_get_pdu_holder_for_seq(cmd, NULL);331 if (!pdu)332 return dr;333 334 if ((read_data_done + pdu->length) == cmd->se_cmd.data_length) {335 pdu->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);336 if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)337 pdu->flags |= ISCSI_FLAG_DATA_ACK;338 339 next_burst_len = 0;340 } else {341 if ((next_burst_len + conn->conn_ops->MaxRecvDataSegmentLength) <342 conn->sess->sess_ops->MaxBurstLength)343 next_burst_len += pdu->length;344 else {345 pdu->flags |= ISCSI_FLAG_CMD_FINAL;346 if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)347 pdu->flags |= ISCSI_FLAG_DATA_ACK;348 349 next_burst_len = 0;350 }351 }352 353 pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;354 if (!dr->recovery) {355 cmd->next_burst_len = next_burst_len;356 cmd->read_data_done += pdu->length;357 } else {358 dr->next_burst_len = next_burst_len;359 dr->read_data_done += pdu->length;360 }361 362 datain->flags = pdu->flags;363 datain->length = pdu->length;364 datain->offset = pdu->offset;365 datain->data_sn = pdu->data_sn;366 367 if (!dr->recovery) {368 if (datain->flags & ISCSI_FLAG_DATA_STATUS)369 dr->dr_complete = DATAIN_COMPLETE_NORMAL;370 371 return dr;372 }373 374 if (!dr->runlength) {375 if (datain->flags & ISCSI_FLAG_DATA_STATUS) {376 dr->dr_complete =377 (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?378 DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :379 DATAIN_COMPLETE_CONNECTION_RECOVERY;380 }381 } else {382 if ((dr->begrun + dr->runlength) == dr->data_sn) {383 dr->dr_complete =384 (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?385 DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :386 DATAIN_COMPLETE_CONNECTION_RECOVERY;387 }388 }389 390 return dr;391}392 393/*394 * For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=No.395 */396static struct iscsi_datain_req *iscsit_set_datain_values_no_and_no(397 struct iscsit_cmd *cmd,398 struct iscsi_datain *datain)399{400 u32 read_data_done, read_data_left, seq_send_order;401 struct iscsit_conn *conn = cmd->conn;402 struct iscsi_datain_req *dr;403 struct iscsi_pdu *pdu;404 struct iscsi_seq *seq = NULL;405 406 dr = iscsit_get_datain_req(cmd);407 if (!dr)408 return NULL;409 410 if (dr->recovery && dr->generate_recovery_values) {411 if (iscsit_create_recovery_datain_values_datasequenceinorder_no(412 cmd, dr) < 0)413 return NULL;414 415 dr->generate_recovery_values = 0;416 }417 418 read_data_done = (!dr->recovery) ?419 cmd->read_data_done : dr->read_data_done;420 seq_send_order = (!dr->recovery) ?421 cmd->seq_send_order : dr->seq_send_order;422 423 read_data_left = (cmd->se_cmd.data_length - read_data_done);424 if (!read_data_left) {425 pr_err("ITT: 0x%08x read_data_left is zero!\n",426 cmd->init_task_tag);427 return NULL;428 }429 430 seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);431 if (!seq)432 return NULL;433 434 seq->sent = 1;435 436 if (!dr->recovery && !seq->next_burst_len)437 seq->first_datasn = cmd->data_sn;438 439 pdu = iscsit_get_pdu_holder_for_seq(cmd, seq);440 if (!pdu)441 return NULL;442 443 if (seq->pdu_send_order == seq->pdu_count) {444 pdu->flags |= ISCSI_FLAG_CMD_FINAL;445 if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)446 pdu->flags |= ISCSI_FLAG_DATA_ACK;447 448 seq->next_burst_len = 0;449 seq_send_order++;450 } else451 seq->next_burst_len += pdu->length;452 453 if ((read_data_done + pdu->length) == cmd->se_cmd.data_length)454 pdu->flags |= ISCSI_FLAG_DATA_STATUS;455 456 pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;457 if (!dr->recovery) {458 cmd->seq_send_order = seq_send_order;459 cmd->read_data_done += pdu->length;460 } else {461 dr->seq_send_order = seq_send_order;462 dr->read_data_done += pdu->length;463 }464 465 datain->flags = pdu->flags;466 datain->length = pdu->length;467 datain->offset = pdu->offset;468 datain->data_sn = pdu->data_sn;469 470 if (!dr->recovery) {471 if (datain->flags & ISCSI_FLAG_CMD_FINAL)472 seq->last_datasn = datain->data_sn;473 if (datain->flags & ISCSI_FLAG_DATA_STATUS)474 dr->dr_complete = DATAIN_COMPLETE_NORMAL;475 476 return dr;477 }478 479 if (!dr->runlength) {480 if (datain->flags & ISCSI_FLAG_DATA_STATUS) {481 dr->dr_complete =482 (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?483 DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :484 DATAIN_COMPLETE_CONNECTION_RECOVERY;485 }486 } else {487 if ((dr->begrun + dr->runlength) == dr->data_sn) {488 dr->dr_complete =489 (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?490 DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :491 DATAIN_COMPLETE_CONNECTION_RECOVERY;492 }493 }494 495 return dr;496}497 498struct iscsi_datain_req *iscsit_get_datain_values(499 struct iscsit_cmd *cmd,500 struct iscsi_datain *datain)501{502 struct iscsit_conn *conn = cmd->conn;503 504 if (conn->sess->sess_ops->DataSequenceInOrder &&505 conn->sess->sess_ops->DataPDUInOrder)506 return iscsit_set_datain_values_yes_and_yes(cmd, datain);507 else if (!conn->sess->sess_ops->DataSequenceInOrder &&508 conn->sess->sess_ops->DataPDUInOrder)509 return iscsit_set_datain_values_no_and_yes(cmd, datain);510 else if (conn->sess->sess_ops->DataSequenceInOrder &&511 !conn->sess->sess_ops->DataPDUInOrder)512 return iscsit_set_datain_values_yes_and_no(cmd, datain);513 else if (!conn->sess->sess_ops->DataSequenceInOrder &&514 !conn->sess->sess_ops->DataPDUInOrder)515 return iscsit_set_datain_values_no_and_no(cmd, datain);516 517 return NULL;518}519EXPORT_SYMBOL(iscsit_get_datain_values);520