// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
/*
* Functions to build and send ELS/CT/BLS commands and responses.
*/
#include "efc.h"
#include "efc_els.h"
#include "../libefc_sli/sli4.h"
#define EFC_LOG_ENABLE_ELS_TRACE(efc) \
(((efc) != NULL) ? (((efc)->logmask & (1U << 1)) != 0) : 0)
#define node_els_trace() \
do { \
if (EFC_LOG_ENABLE_ELS_TRACE(efc)) \
efc_log_info(efc, "[%s] %-20s\n", \
node->display_name, __func__); \
} while (0)
#define els_io_printf(els, fmt, ...) \
efc_log_err((struct efc *)els->node->efc,\
"[%s] %-8s " fmt, \
els->node->display_name,\
els->display_name, ##__VA_ARGS__)
#define EFC_ELS_RSP_LEN 1024
#define EFC_ELS_GID_PT_RSP_LEN 8096
struct efc_els_io_req *
efc_els_io_alloc(struct efc_node *node, u32 reqlen)
{
return efc_els_io_alloc_size(node, reqlen, EFC_ELS_RSP_LEN);
}
struct efc_els_io_req *
efc_els_io_alloc_size(struct efc_node *node, u32 reqlen, u32 rsplen)
{
struct efc *efc;
struct efc_els_io_req *els;
unsigned long flags = 0;
efc = node->efc;
if (!node->els_io_enabled) {
efc_log_err(efc, "els io alloc disabled\n");
return NULL;
}
els = mempool_alloc(efc->els_io_pool, GFP_ATOMIC);
if (!els) {
atomic_add_return(1, &efc->els_io_alloc_failed_count);
return NULL;
}
/* initialize refcount */
kref_init(&els->ref);
els->release = _efc_els_io_free;
/* populate generic io fields */
els->node = node;
/* now allocate DMA for request and response */
els->io.req.size = reqlen;
els->io.req.virt = dma_alloc_coherent(&efc->pci->dev, els->io.req.size,
&els->io.req.phys, GFP_KERNEL);
if (!els->io.req.virt) {
mempool_free(els, efc->els_io_pool);
return NULL;
}
els->io.rsp.size = rsplen;
els->io.rsp.virt = dma_alloc_coherent(&efc->pci->dev, els->io.rsp.size,
&els->io.rsp.phys, GFP_KERNEL);
if (!els->io.rsp.virt) {
dma_free_coherent(&efc->pci->dev, els->io.req.size,
els->io.req.virt, els->io.req.phys);
mempool_free(els, efc->els_io_pool);
els = NULL;
}
if (els) {
/* initialize fields */
els->els_retries_remaining = EFC_FC_ELS_DEFAULT_RETRIES;
/* add els structure to ELS IO list */
INIT_LIST_HEAD(&els->list_entry);
spin_lock_irqsave(&node->els_ios_lock, flags);
list_add_tail(&els->list_entry, &node->els_ios_list);
spin_unlock_irqrestore(&node->els_ios_lock, flags);
}
return els;
}
void
efc_els_io_free(struct efc_els_io_req *els)
{
kref_put(&els->ref, els->release);
}
void
_efc_els_io_free(struct kref *arg)
{
struct efc_els_io_req *els =
container_of(arg, struct efc_els_io_req, ref);
struct efc *efc;
struct efc_node *node;
int send_empty_event = false;
unsigned long flags = 0;
node = els->node;
efc = node->efc;
spin_lock_irqsave(&node->els_ios_lock, flags);
list_del(&els->list_entry);
/* Send list empty event if the IO allocator
* is disabled, and the list is empty
* If node->els_io_enabled was not checked,
* the event would be posted continually
*/
send_empty_event = (!node->els_io_enabled &&
list_empty(&node->els_ios_list));
spin_unlock_irqrestore(&node->els_ios_lock, flags);
/* free ELS request and response buffers */
dma_free_coherent(&efc->pci->dev, els->io.rsp.size,
els->io.rsp.virt, els->io.rsp.phys);
dma_free_coherent(&efc->pci->dev, els->io.req.size,
els->io.req.virt, els->io.req.phys);
mempool_free(els, efc->els_io_pool);
if (send_empty_event)
efc_scsi_io_list_empty(node->efc, node);
}
static void
efc_els_retry(struct efc_els_io_req *els);
static void
efc_els_delay_timer_cb(struct timer_list *t)
{
struct efc_els_io_req *els = from_timer(els, t, delay_timer);
/* Retry delay timer expired, retry the ELS request */
efc_els_retry(els);
}
static int
efc_els_req_cb(void *arg, u32 length, int status, u32 ext_status<