// SPDX-License-Identifier: GPL-2.0-or-later
/*******************************************************************************
* This file contains error recovery level one used by the iSCSI Target driver.
*
* (c) Copyright 2007-2013 Datera, Inc.
*
* Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
*
******************************************************************************/
#include <linux/list.h>
#include <linux/slab.h>
#include <scsi/iscsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
#include <target/iscsi/iscsi_transport.h>
#include <target/iscsi/iscsi_target_core.h>
#include "iscsi_target_seq_pdu_list.h"
#include "iscsi_target_datain_values.h"
#include "iscsi_target_device.h"
#include "iscsi_target_tpg.h"
#include "iscsi_target_util.h"
#include "iscsi_target_erl0.h"
#include "iscsi_target_erl1.h"
#include "iscsi_target_erl2.h"
#include "iscsi_target.h"
#define OFFLOAD_BUF_SIZE 32768U
/*
* Used to dump excess datain payload for certain error recovery
* situations. Receive in OFFLOAD_BUF_SIZE max of datain per rx_data().
*
* dump_padding_digest denotes if padding and data digests need
* to be dumped.
*/
int iscsit_dump_data_payload(
struct iscsit_conn *conn,
u32 buf_len,
int dump_padding_digest)
{
char *buf;
int ret = DATAOUT_WITHIN_COMMAND_RECOVERY, rx_got;
u32 length, offset = 0, size;
struct kvec iov;
if (conn->sess->sess_ops->RDMAExtensions)
return 0;
if (dump_padding_digest) {
buf_len = ALIGN(buf_len, 4);
if (conn->conn_ops->DataDigest)
buf_len += ISCSI_CRC_LEN;
}
length = min(buf_len, OFFLOAD_BUF_SIZE);
buf = kzalloc(length, GFP_ATOMIC);
if (!buf) {
pr_err("Unable to allocate %u bytes for offload"
" buffer.\n", length);
return -1;
}
memset(&iov, 0, sizeof(struct kvec));
while (offset < buf_len) {
size = min(buf_len - offset, length);
iov.iov_len = size;
iov.iov_base = buf;
rx_got = rx_data(conn, &iov, 1, size);
if (rx_got != size) {
ret = DATAOUT_CANNOT_RECOVER;
break;
}
offset += size;
}
kfree(buf);
return ret;
}
/*
* Used for retransmitting R2Ts from a R2T SNACK request.
*/
static int iscsit_send_recovery_r2t_for_snack(
struct iscsit_cmd *cmd,
struct iscsi_r2t *r2t)
{
/*
* If the struct iscsi_r2t has not been sent yet, we can safely
* ignore retransmission
* of the R2TSN in question.
*/
spin_lock_bh(&cmd->r2t_lock);
if (!r2t->sent_r2t) {
spin_unlock_bh(&cmd->r2t_lock);
return 0;
}
r2t->sent_r2t = 0;
spin_unlock_bh(&cmd->r2t_lock);
iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, ISTATE_SEND_R2T);
return 0;
}
static int iscsit_handle_r2t_snack(
struct iscsit_cmd *cmd,
unsigned char *buf,
u32 begrun,
u32 runlength)
{
u32 last_r2tsn;
struct iscsi_r2t *r2t;
/*
* Make sure the initiator is not requesting retransmission
* of R2TSNs already acknowledged by a TMR TASK_REASSIGN.
*/
if ((cmd->cmd_flags & ICF_GOT_DATACK_SNACK) &&
(begrun <= cmd->acked_data_sn)) {
pr_err("ITT: 0x%08x, R2T SNACK requesting"
" retransmission of R2TSN: 0x%08x to 0x%08x but already"
" acked to R2TSN: 0x%08x by TMR TASK_REASSIGN,"
" protocol error.\n", cmd->init_task_tag, begrun,
(begrun + runlength), cmd->acked_data_sn);
return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf);
}
if (runlength) {
if ((begrun + runlength) > cmd<