// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
#include "efct_driver.h"
#include "efct_hw.h"
#define enable_tsend_auto_resp(efct) 1
#define enable_treceive_auto_resp(efct) 0
#define SCSI_IOFMT "[%04x][i:%04x t:%04x h:%04x]"
#define scsi_io_printf(io, fmt, ...) \
efc_log_debug(io->efct, "[%s]" SCSI_IOFMT fmt, \
io->node->display_name, io->instance_index,\
io->init_task_tag, io->tgt_task_tag, io->hw_tag, ##__VA_ARGS__)
#define EFCT_LOG_ENABLE_SCSI_TRACE(efct) \
(((efct) != NULL) ? (((efct)->logmask & (1U << 2)) != 0) : 0)
#define scsi_io_trace(io, fmt, ...) \
do { \
if (EFCT_LOG_ENABLE_SCSI_TRACE(io->efct)) \
scsi_io_printf(io, fmt, ##__VA_ARGS__); \
} while (0)
struct efct_io *
efct_scsi_io_alloc(struct efct_node *node)
{
struct efct *efct;
struct efct_xport *xport;
struct efct_io *io;
unsigned long flags;
efct = node->efct;
xport = efct->xport;
io = efct_io_pool_io_alloc(efct->xport->io_pool);
if (!io) {
efc_log_err(efct, "IO alloc Failed\n");
atomic_add_return(1, &xport->io_alloc_failed_count);
return NULL;
}
/* initialize refcount */
kref_init(&io->ref);
io->release = _efct_scsi_io_free;
/* set generic fields */
io->efct = efct;
io->node = node;
kref_get(&node->ref);
/* set type and name */
io->io_type = EFCT_IO_TYPE_IO;
io->display_name = "scsi_io";
io->cmd_ini = false;
io->cmd_tgt = true;
/* Add to node's active_ios list */
INIT_LIST_HEAD(&io->list_entry);
spin_lock_irqsave(&node->active_ios_lock, flags);
list_add(&io->list_entry, &node->active_ios);
spin_unlock_irqrestore(&node->active_ios_lock, flags);
return io;
}
void
_efct_scsi_io_free(struct kref *arg)
{
struct efct_io *io = container_of(arg, struct efct_io, ref);
struct efct *efct = io->efct;
struct efct_node *node = io->node;
unsigned long flags = 0;
scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
if (io->io_free) {
efc_log_err(efct, "IO already freed.\n");
return;
}
spin_lock_irqsave(&node->active_ios_lock, flags);
list_del_init(&io->list_entry);
spin_unlock_irqrestore(&node->active_ios_lock, flags);
kref_put(&node->ref, node->release);
io->node = NULL;
efct_io_pool_io_free(efct->xport->io_pool, io);
}
void
efct_scsi_io_free(struct efct_io *io)
{
scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
WARN_ON(!refcount_read(&io->ref.refcount));
kref_put(&io->ref, io->release);
}
static void
efct_target_io_cb(struct efct_hw_io *hio, u32 length, int status,
u32 ext_status, void *app)
{
u32 flags = 0;
struct efct_io *io = app;
struct efct *efct;
enum efct_scsi_io_status scsi_stat = EFCT_SCSI_STATUS_GOOD;
efct_scsi_io_cb_t cb;
if (!io || !io->efct) {
pr_err("%s: IO can not be NULL\n", __func__);
return;
}
scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status);
efct = io->efct;
io->transferred += length;
if (!io->scsi_tgt_cb) {
efct_scsi_check_pending(efct);
return;
}
/* Call target server completion */
cb = io->scsi_tgt_cb;
/* Clear the callback before invoking the callback */
io->scsi_tgt_cb = NULL;
/* if status was good, and auto-good-response was set,
* then callback target-server with IO_CMPL_RSP_SENT,
* otherwise send IO_CMPL
*/
if (status == 0 && io->auto_resp)
flags |= EFCT_SCSI_IO_CMPL_RSP_SENT;
else
flags |= EFCT_SCSI_IO_CMPL;
switch (status) {
case SLI4_FC_WCQE_STATUS_SUCCESS:
scsi_stat = EFCT_SCSI_STATUS_GOOD;
break;
case SLI4_FC_WCQE_STATUS_DI_ERROR:
if (ext_status & SLI4_FC_DI_ERROR_GE)
scsi_stat = EFCT_SCSI_STATUS_DIF_GUARD_ERR;
else if