Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,777 @@
// 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"
#include "efct_unsol.h"
#include "efct_scsi.h"
LIST_HEAD(efct_devices);
static int logmask;
module_param(logmask, int, 0444);
MODULE_PARM_DESC(logmask, "logging bitmask (default 0)");
static struct libefc_function_template efct_libefc_templ = {
.issue_mbox_rqst = efct_issue_mbox_rqst,
.send_els = efct_els_hw_srrs_send,
.send_bls = efct_efc_bls_send,
.new_nport = efct_scsi_tgt_new_nport,
.del_nport = efct_scsi_tgt_del_nport,
.scsi_new_node = efct_scsi_new_initiator,
.scsi_del_node = efct_scsi_del_initiator,
.hw_seq_free = efct_efc_hw_sequence_free,
};
static int
efct_device_init(void)
{
int rc;
/* driver-wide init for target-server */
rc = efct_scsi_tgt_driver_init();
if (rc) {
pr_err("efct_scsi_tgt_init failed rc=%d\n", rc);
return rc;
}
rc = efct_scsi_reg_fc_transport();
if (rc) {
efct_scsi_tgt_driver_exit();
pr_err("failed to register to FC host\n");
return rc;
}
return 0;
}
static void
efct_device_shutdown(void)
{
efct_scsi_release_fc_transport();
efct_scsi_tgt_driver_exit();
}
static void *
efct_device_alloc(u32 nid)
{
struct efct *efct = NULL;
efct = kzalloc_node(sizeof(*efct), GFP_KERNEL, nid);
if (!efct)
return efct;
INIT_LIST_HEAD(&efct->list_entry);
list_add_tail(&efct->list_entry, &efct_devices);
return efct;
}
static void
efct_teardown_msix(struct efct *efct)
{
u32 i;
for (i = 0; i < efct->n_msix_vec; i++) {
free_irq(pci_irq_vector(efct->pci, i),
&efct->intr_context[i]);
}
pci_free_irq_vectors(efct->pci);
}
static int
efct_efclib_config(struct efct *efct, struct libefc_function_template *tt)
{
struct efc *efc;
struct sli4 *sli;
int rc = 0;
efc = kzalloc_obj(*efc);
if (!efc)
return -ENOMEM;
efct->efcport = efc;
memcpy(&efc->tt, tt, sizeof(*tt));
efc->base = efct;
efc->pci = efct->pci;
efc->def_wwnn = efct_get_wwnn(&efct->hw);
efc->def_wwpn = efct_get_wwpn(&efct->hw);
efc->enable_tgt = 1;
efc->log_level = EFC_LOG_LIB;
sli = &efct->hw.sli;
efc->max_xfer_size = sli->sge_supported_length *
sli_get_max_sgl(&efct->hw.sli);
efc->sli = sli;
efc->fcfi = efct->hw.fcf_indicator;
rc = efcport_init(efc);
if (rc)
efc_log_err(efc, "efcport_init failed\n");
return rc;
}
static int efct_request_firmware_update(struct efct *efct);
static const char*
efct_pci_model(u16 device)
{
switch (device) {
case EFCT_DEVICE_LANCER_G6: return "LPE31004";
case EFCT_DEVICE_LANCER_G7: return "LPE36000";
default: return "unknown";
}
}
static int
efct_device_attach(struct efct *efct)
{
u32 rc = 0, i = 0;
if (efct->attached) {
efc_log_err(efct, "Device is already attached\n");
return -EIO;
}
snprintf(efct->name, sizeof(efct->name), "[%s%d] ", "fc",
efct->instance_index);
efct->logmask = logmask;
efct->filter_def = EFCT_DEFAULT_FILTER;
efct->max_isr_time_msec = EFCT_OS_MAX_ISR_TIME_MSEC;
efct->model = efct_pci_model(efct->pci->device);
efct->efct_req_fw_upgrade = true;
/* Allocate transport object and bring online */
efct->xport = efct_xport_alloc(efct);
if (!efct->xport) {
efc_log_err(efct, "failed to allocate transport object\n");
rc = -ENOMEM;
goto out;
}
rc = efct_xport_attach(efct->xport);
if (rc) {
efc_log_err(efct, "failed to attach transport object\n");
goto xport_out;
}
rc = efct_xport_initialize(efct->xport);
if (rc) {
efc_log_err(efct, "failed to initialize transport object\n");
goto xport_out;
}
rc = efct_efclib_config(efct, &efct_libefc_templ);
if (rc) {
efc_log_err(efct, "failed to init efclib\n");
goto efclib_out;
}
for (i = 0; i < efct->n_msix_vec; i++) {
efc_log_debug(efct, "irq %d enabled\n", i);
enable_irq(pci_irq_vector(efct->pci, i));
}
efct->attached = true;
if (efct->efct_req_fw_upgrade)
efct_request_firmware_update(efct);
return rc;
efclib_out:
efct_xport_detach(efct->xport);
xport_out:
efct_xport_free(efct->xport);
efct->xport = NULL;
out:
return rc;
}
static int
efct_device_detach(struct efct *efct)
{
int i;
if (!efct || !efct->attached) {
pr_err("Device is not attached\n");
return -EIO;
}
if (efct_xport_control(efct->xport, EFCT_XPORT_SHUTDOWN))
efc_log_err(efct, "Transport Shutdown timed out\n");
for (i = 0; i < efct->n_msix_vec; i++)
disable_irq(pci_irq_vector(efct->pci, i));
efct_xport_detach(efct->xport);
efct_xport_free(efct->xport);
efct->xport = NULL;
efcport_destroy(efct->efcport);
kfree(efct->efcport);
efct->attached = false;
return 0;
}
static void
efct_fw_write_cb(int status, u32 actual_write_length,
u32 change_status, void *arg)
{
struct efct_fw_write_result *result = arg;
result->status = status;
result->actual_xfer = actual_write_length;
result->change_status = change_status;
complete(&result->done);
}
static int
efct_firmware_write(struct efct *efct, const u8 *buf, size_t buf_len,
u8 *change_status)
{
int rc = 0;
u32 bytes_left;
u32 xfer_size;
u32 offset;
struct efc_dma dma;
int last = 0;
struct efct_fw_write_result result;
init_completion(&result.done);
bytes_left = buf_len;
offset = 0;
dma.size = FW_WRITE_BUFSIZE;
dma.virt = dma_alloc_coherent(&efct->pci->dev,
dma.size, &dma.phys, GFP_KERNEL);
if (!dma.virt)
return -ENOMEM;
while (bytes_left > 0) {
if (bytes_left > FW_WRITE_BUFSIZE)
xfer_size = FW_WRITE_BUFSIZE;
else
xfer_size = bytes_left;
memcpy(dma.virt, buf + offset, xfer_size);
if (bytes_left == xfer_size)
last = 1;
efct_hw_firmware_write(&efct->hw, &dma, xfer_size, offset,
last, efct_fw_write_cb, &result);
if (wait_for_completion_interruptible(&result.done) != 0) {
rc = -ENXIO;
break;
}
if (result.actual_xfer == 0 || result.status != 0) {
rc = -EFAULT;
break;
}
if (last)
*change_status = result.change_status;
bytes_left -= result.actual_xfer;
offset += result.actual_xfer;
}
dma_free_coherent(&efct->pci->dev, dma.size, dma.virt, dma.phys);
return rc;
}
static int
efct_fw_reset(struct efct *efct)
{
/*
* Firmware reset to activate the new firmware.
* Function 0 will update and load the new firmware
* during attach.
*/
if (timer_pending(&efct->xport->stats_timer))
timer_delete(&efct->xport->stats_timer);
if (efct_hw_reset(&efct->hw, EFCT_HW_RESET_FIRMWARE)) {
efc_log_info(efct, "failed to reset firmware\n");
return -EIO;
}
efc_log_info(efct, "successfully reset firmware.Now resetting port\n");
efct_device_detach(efct);
return efct_device_attach(efct);
}
static int
efct_request_firmware_update(struct efct *efct)
{
int rc = 0;
u8 file_name[256], fw_change_status = 0;
const struct firmware *fw;
struct efct_hw_grp_hdr *fw_image;
snprintf(file_name, 256, "%s.grp", efct->model);
rc = request_firmware(&fw, file_name, &efct->pci->dev);
if (rc) {
efc_log_debug(efct, "Firmware file(%s) not found.\n", file_name);
return rc;
}
fw_image = (struct efct_hw_grp_hdr *)fw->data;
if (!strncmp(efct->hw.sli.fw_name[0], fw_image->revision,
strnlen(fw_image->revision, 16))) {
efc_log_debug(efct,
"Skip update. Firmware is already up to date.\n");
goto exit;
}
efc_log_info(efct, "Firmware update is initiated. %s -> %s\n",
efct->hw.sli.fw_name[0], fw_image->revision);
rc = efct_firmware_write(efct, fw->data, fw->size, &fw_change_status);
if (rc) {
efc_log_err(efct, "Firmware update failed. rc = %d\n", rc);
goto exit;
}
efc_log_info(efct, "Firmware updated successfully\n");
switch (fw_change_status) {
case 0x00:
efc_log_info(efct, "New firmware is active.\n");
break;
case 0x01:
efc_log_info(efct,
"System reboot needed to activate the new firmware\n");
break;
case 0x02:
case 0x03:
efc_log_info(efct,
"firmware reset to activate the new firmware\n");
efct_fw_reset(efct);
break;
default:
efc_log_info(efct, "Unexpected value change_status:%d\n",
fw_change_status);
break;
}
exit:
release_firmware(fw);
return rc;
}
static void
efct_device_free(struct efct *efct)
{
if (efct) {
list_del(&efct->list_entry);
kfree(efct);
}
}
static int
efct_device_interrupts_required(struct efct *efct)
{
int rc;
rc = efct_hw_setup(&efct->hw, efct, efct->pci);
if (rc < 0)
return rc;
return efct->hw.config.n_eq;
}
static irqreturn_t
efct_intr_thread(int irq, void *handle)
{
struct efct_intr_context *intr_ctx = handle;
struct efct *efct = intr_ctx->efct;
efct_hw_process(&efct->hw, intr_ctx->index, efct->max_isr_time_msec);
return IRQ_HANDLED;
}
static int
efct_setup_msix(struct efct *efct, u32 num_intrs)
{
int rc = 0, i;
if (!pci_find_capability(efct->pci, PCI_CAP_ID_MSIX)) {
dev_err(&efct->pci->dev,
"%s : MSI-X not available\n", __func__);
return -EIO;
}
efct->n_msix_vec = num_intrs;
rc = pci_alloc_irq_vectors(efct->pci, num_intrs, num_intrs,
PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
if (rc < 0) {
dev_err(&efct->pci->dev, "Failed to alloc irq : %d\n", rc);
return rc;
}
for (i = 0; i < num_intrs; i++) {
struct efct_intr_context *intr_ctx = NULL;
intr_ctx = &efct->intr_context[i];
intr_ctx->efct = efct;
intr_ctx->index = i;
rc = request_threaded_irq(pci_irq_vector(efct->pci, i),
NULL, efct_intr_thread, IRQF_ONESHOT,
EFCT_DRIVER_NAME, intr_ctx);
if (rc) {
dev_err(&efct->pci->dev,
"Failed to register %d vector: %d\n", i, rc);
goto out;
}
}
return rc;
out:
while (--i >= 0)
free_irq(pci_irq_vector(efct->pci, i),
&efct->intr_context[i]);
pci_free_irq_vectors(efct->pci);
return rc;
}
static const struct pci_device_id efct_pci_table[] = {
{PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G6), 0},
{PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G7), 0},
{} /* terminate list */
};
static int
efct_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct efct *efct = NULL;
int rc;
u32 i, r;
int num_interrupts = 0;
int nid;
dev_info(&pdev->dev, "%s\n", EFCT_DRIVER_NAME);
rc = pci_enable_device_mem(pdev);
if (rc)
return rc;
pci_set_master(pdev);
rc = pci_set_mwi(pdev);
if (rc) {
dev_info(&pdev->dev, "pci_set_mwi returned %d\n", rc);
goto mwi_out;
}
rc = pci_request_regions(pdev, EFCT_DRIVER_NAME);
if (rc) {
dev_err(&pdev->dev, "pci_request_regions failed %d\n", rc);
goto req_regions_out;
}
/* Fetch the Numa node id for this device */
nid = dev_to_node(&pdev->dev);
if (nid < 0) {
dev_err(&pdev->dev, "Warning Numa node ID is %d\n", nid);
nid = 0;
}
/* Allocate efct */
efct = efct_device_alloc(nid);
if (!efct) {
dev_err(&pdev->dev, "Failed to allocate efct\n");
rc = -ENOMEM;
goto alloc_out;
}
efct->pci = pdev;
efct->numa_node = nid;
/* Map all memory BARs */
for (i = 0, r = 0; i < EFCT_PCI_MAX_REGS; i++) {
if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
efct->reg[r] = ioremap(pci_resource_start(pdev, i),
pci_resource_len(pdev, i));
r++;
}
/*
* If the 64-bit attribute is set, both this BAR and the
* next form the complete address. Skip processing the
* next BAR.
*/
if (pci_resource_flags(pdev, i) & IORESOURCE_MEM_64)
i++;
}
pci_set_drvdata(pdev, efct);
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc) {
dev_err(&pdev->dev, "setting DMA_BIT_MASK failed\n");
goto dma_mask_out;
}
num_interrupts = efct_device_interrupts_required(efct);
if (num_interrupts < 0) {
efc_log_err(efct, "efct_device_interrupts_required failed\n");
rc = -1;
goto dma_mask_out;
}
/*
* Initialize MSIX interrupts, note,
* efct_setup_msix() enables the interrupt
*/
rc = efct_setup_msix(efct, num_interrupts);
if (rc) {
dev_err(&pdev->dev, "Can't setup msix\n");
goto dma_mask_out;
}
/* Disable interrupt for now */
for (i = 0; i < efct->n_msix_vec; i++) {
efc_log_debug(efct, "irq %d disabled\n", i);
disable_irq(pci_irq_vector(efct->pci, i));
}
rc = efct_device_attach(efct);
if (rc)
goto attach_out;
return 0;
attach_out:
efct_teardown_msix(efct);
dma_mask_out:
pci_set_drvdata(pdev, NULL);
for (i = 0; i < EFCT_PCI_MAX_REGS; i++) {
if (efct->reg[i])
iounmap(efct->reg[i]);
}
efct_device_free(efct);
alloc_out:
pci_release_regions(pdev);
req_regions_out:
pci_clear_mwi(pdev);
mwi_out:
pci_disable_device(pdev);
return rc;
}
static void
efct_pci_remove(struct pci_dev *pdev)
{
struct efct *efct = pci_get_drvdata(pdev);
u32 i;
if (!efct)
return;
efct_device_detach(efct);
efct_teardown_msix(efct);
for (i = 0; i < EFCT_PCI_MAX_REGS; i++) {
if (efct->reg[i])
iounmap(efct->reg[i]);
}
pci_set_drvdata(pdev, NULL);
efct_device_free(efct);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
static void
efct_device_prep_for_reset(struct efct *efct, struct pci_dev *pdev)
{
if (efct) {
efc_log_debug(efct,
"PCI channel disable preparing for reset\n");
efct_device_detach(efct);
/* Disable interrupt and pci device */
efct_teardown_msix(efct);
}
pci_disable_device(pdev);
}
static void
efct_device_prep_for_recover(struct efct *efct)
{
if (efct) {
efc_log_debug(efct, "PCI channel preparing for recovery\n");
efct_hw_io_abort_all(&efct->hw);
}
}
/**
* efct_pci_io_error_detected - method for handling PCI I/O error
* @pdev: pointer to PCI device.
* @state: the current PCI connection state.
*
* This routine is registered to the PCI subsystem for error handling. This
* function is called by the PCI subsystem after a PCI bus error affecting
* this device has been detected. When this routine is invoked, it dispatches
* device error detected handling routine, which will perform the proper
* error detected operation.
*
* Return codes
* PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
* PCI_ERS_RESULT_DISCONNECT - device could not be recovered
*/
static pci_ers_result_t
efct_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
{
struct efct *efct = pci_get_drvdata(pdev);
pci_ers_result_t rc;
switch (state) {
case pci_channel_io_normal:
efct_device_prep_for_recover(efct);
rc = PCI_ERS_RESULT_CAN_RECOVER;
break;
case pci_channel_io_frozen:
efct_device_prep_for_reset(efct, pdev);
rc = PCI_ERS_RESULT_NEED_RESET;
break;
case pci_channel_io_perm_failure:
efct_device_detach(efct);
rc = PCI_ERS_RESULT_DISCONNECT;
break;
default:
efc_log_debug(efct, "Unknown PCI error state:0x%x\n", state);
efct_device_prep_for_reset(efct, pdev);
rc = PCI_ERS_RESULT_NEED_RESET;
break;
}
return rc;
}
static pci_ers_result_t
efct_pci_io_slot_reset(struct pci_dev *pdev)
{
int rc;
struct efct *efct = pci_get_drvdata(pdev);
rc = pci_enable_device_mem(pdev);
if (rc) {
efc_log_err(efct, "failed to enable PCI device after reset\n");
return PCI_ERS_RESULT_DISCONNECT;
}
/*
* As the new kernel behavior of pci_restore_state() API call clears
* device saved_state flag, need to save the restored state again.
*/
pci_save_state(pdev);
pci_set_master(pdev);
rc = efct_setup_msix(efct, efct->n_msix_vec);
if (rc)
efc_log_err(efct, "rc %d returned, IRQ allocation failed\n",
rc);
/* Perform device reset */
efct_device_detach(efct);
/* Bring device to online*/
efct_device_attach(efct);
return PCI_ERS_RESULT_RECOVERED;
}
static void
efct_pci_io_resume(struct pci_dev *pdev)
{
struct efct *efct = pci_get_drvdata(pdev);
/* Perform device reset */
efct_device_detach(efct);
/* Bring device to online*/
efct_device_attach(efct);
}
MODULE_DEVICE_TABLE(pci, efct_pci_table);
static const struct pci_error_handlers efct_pci_err_handler = {
.error_detected = efct_pci_io_error_detected,
.slot_reset = efct_pci_io_slot_reset,
.resume = efct_pci_io_resume,
};
static struct pci_driver efct_pci_driver = {
.name = EFCT_DRIVER_NAME,
.id_table = efct_pci_table,
.probe = efct_pci_probe,
.remove = efct_pci_remove,
.err_handler = &efct_pci_err_handler,
};
static
int __init efct_init(void)
{
int rc;
rc = efct_device_init();
if (rc) {
pr_err("efct_device_init failed rc=%d\n", rc);
return rc;
}
rc = pci_register_driver(&efct_pci_driver);
if (rc) {
pr_err("pci_register_driver failed rc=%d\n", rc);
efct_device_shutdown();
}
return rc;
}
static void __exit efct_exit(void)
{
pci_unregister_driver(&efct_pci_driver);
efct_device_shutdown();
}
module_init(efct_init);
module_exit(efct_exit);
MODULE_VERSION(EFCT_DRIVER_VERSION);
MODULE_DESCRIPTION("Emulex Fibre Channel Target driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Broadcom");
@@ -0,0 +1,108 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
#if !defined(__EFCT_DRIVER_H__)
#define __EFCT_DRIVER_H__
/***************************************************************************
* OS specific includes
*/
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/firmware.h>
#include "../include/efc_common.h"
#include "../libefc/efclib.h"
#include "efct_hw.h"
#include "efct_io.h"
#include "efct_xport.h"
#define EFCT_DRIVER_NAME "efct"
#define EFCT_DRIVER_VERSION "1.0.0.0"
/* EFCT_DEFAULT_FILTER-
* MRQ filter to segregate the IO flow.
*/
#define EFCT_DEFAULT_FILTER "0x01ff22ff,0,0,0"
/* EFCT_OS_MAX_ISR_TIME_MSEC -
* maximum time driver code should spend in an interrupt
* or kernel thread context without yielding
*/
#define EFCT_OS_MAX_ISR_TIME_MSEC 1000
#define EFCT_FC_MAX_SGL 64
#define EFCT_FC_DIF_SEED 0
/* Watermark */
#define EFCT_WATERMARK_HIGH_PCT 90
#define EFCT_WATERMARK_LOW_PCT 80
#define EFCT_IO_WATERMARK_PER_INITIATOR 8
#define EFCT_PCI_MAX_REGS 6
#define MAX_PCI_INTERRUPTS 16
struct efct_intr_context {
struct efct *efct;
u32 index;
};
struct efct {
struct pci_dev *pci;
void __iomem *reg[EFCT_PCI_MAX_REGS];
u32 n_msix_vec;
bool attached;
bool soft_wwn_enable;
u8 efct_req_fw_upgrade;
struct efct_intr_context intr_context[MAX_PCI_INTERRUPTS];
u32 numa_node;
char name[EFC_NAME_LENGTH];
u32 instance_index;
struct list_head list_entry;
struct efct_scsi_tgt tgt_efct;
struct efct_xport *xport;
struct efc *efcport;
struct Scsi_Host *shost;
int logmask;
u32 max_isr_time_msec;
const char *desc;
const char *model;
struct efct_hw hw;
u32 rq_selection_policy;
char *filter_def;
int topology;
/* Look up for target node */
struct xarray lookup;
/*
* Target IO timer value:
* Zero: target command timeout disabled.
* Non-zero: Timeout value, in seconds, for target commands
*/
u32 target_io_timer_sec;
int speed;
struct dentry *sess_debugfs_dir;
};
#define FW_WRITE_BUFSIZE (64 * 1024)
struct efct_fw_write_result {
struct completion done;
int status;
u32 actual_xfer;
u32 change_status;
};
extern struct list_head efct_devices;
#endif /* __EFCT_DRIVER_H__ */
File diff suppressed because it is too large Load Diff
+764
View File
@@ -0,0 +1,764 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
#ifndef _EFCT_HW_H
#define _EFCT_HW_H
#include "../libefc_sli/sli4.h"
/*
* EFCT PCI IDs
*/
#define EFCT_VENDOR_ID 0x10df
/* LightPulse 16Gb x 4 FC (lancer-g6) */
#define EFCT_DEVICE_LANCER_G6 0xe307
/* LightPulse 32Gb x 4 FC (lancer-g7) */
#define EFCT_DEVICE_LANCER_G7 0xf407
/*Default RQ entries len used by driver*/
#define EFCT_HW_RQ_ENTRIES_MIN 512
#define EFCT_HW_RQ_ENTRIES_DEF 1024
#define EFCT_HW_RQ_ENTRIES_MAX 4096
/*Defines the size of the RQ buffers used for each RQ*/
#define EFCT_HW_RQ_SIZE_HDR 128
#define EFCT_HW_RQ_SIZE_PAYLOAD 1024
/*Define the maximum number of multi-receive queues*/
#define EFCT_HW_MAX_MRQS 8
/*
* Define count of when to set the WQEC bit in a submitted
* WQE, causing a consummed/released completion to be posted.
*/
#define EFCT_HW_WQEC_SET_COUNT 32
/*Send frame timeout in seconds*/
#define EFCT_HW_SEND_FRAME_TIMEOUT 10
/*
* FDT Transfer Hint value, reads greater than this value
* will be segmented to implement fairness. A value of zero disables
* the feature.
*/
#define EFCT_HW_FDT_XFER_HINT 8192
#define EFCT_HW_TIMECHECK_ITERATIONS 100
#define EFCT_HW_MAX_NUM_MQ 1
#define EFCT_HW_MAX_NUM_RQ 32
#define EFCT_HW_MAX_NUM_EQ 16
#define EFCT_HW_MAX_NUM_WQ 32
#define EFCT_HW_DEF_NUM_EQ 1
#define OCE_HW_MAX_NUM_MRQ_PAIRS 16
#define EFCT_HW_MQ_DEPTH 128
#define EFCT_HW_EQ_DEPTH 1024
/*
* A CQ will be assigned to each WQ
* (CQ must have 2X entries of the WQ for abort
* processing), plus a separate one for each RQ PAIR and one for MQ
*/
#define EFCT_HW_MAX_NUM_CQ \
((EFCT_HW_MAX_NUM_WQ * 2) + 1 + (OCE_HW_MAX_NUM_MRQ_PAIRS * 2))
#define EFCT_HW_Q_HASH_SIZE 128
#define EFCT_HW_RQ_HEADER_SIZE 128
#define EFCT_HW_RQ_HEADER_INDEX 0
#define EFCT_HW_REQUE_XRI_REGTAG 65534
/* Options for efct_hw_command() */
enum efct_cmd_opts {
/* command executes synchronously and busy-waits for completion */
EFCT_CMD_POLL,
/* command executes asynchronously. Uses callback */
EFCT_CMD_NOWAIT,
};
enum efct_hw_reset {
EFCT_HW_RESET_FUNCTION,
EFCT_HW_RESET_FIRMWARE,
EFCT_HW_RESET_MAX
};
enum efct_hw_topo {
EFCT_HW_TOPOLOGY_AUTO,
EFCT_HW_TOPOLOGY_NPORT,
EFCT_HW_TOPOLOGY_LOOP,
EFCT_HW_TOPOLOGY_NONE,
EFCT_HW_TOPOLOGY_MAX
};
/* pack fw revision values into a single uint64_t */
#define HW_FWREV(a, b, c, d) (((uint64_t)(a) << 48) | ((uint64_t)(b) << 32) \
| ((uint64_t)(c) << 16) | ((uint64_t)(d)))
#define EFCT_FW_VER_STR(a, b, c, d) (#a "." #b "." #c "." #d)
enum efct_hw_io_type {
EFCT_HW_ELS_REQ,
EFCT_HW_ELS_RSP,
EFCT_HW_FC_CT,
EFCT_HW_FC_CT_RSP,
EFCT_HW_BLS_ACC,
EFCT_HW_BLS_RJT,
EFCT_HW_IO_TARGET_READ,
EFCT_HW_IO_TARGET_WRITE,
EFCT_HW_IO_TARGET_RSP,
EFCT_HW_IO_DNRX_REQUEUE,
EFCT_HW_IO_MAX,
};
enum efct_hw_io_state {
EFCT_HW_IO_STATE_FREE,
EFCT_HW_IO_STATE_INUSE,
EFCT_HW_IO_STATE_WAIT_FREE,
EFCT_HW_IO_STATE_WAIT_SEC_HIO,
};
#define EFCT_TARGET_WRITE_SKIPS 1
#define EFCT_TARGET_READ_SKIPS 2
struct efct_hw;
struct efct_io;
#define EFCT_CMD_CTX_POOL_SZ 32
/**
* HW command context.
* Stores the state for the asynchronous commands sent to the hardware.
*/
struct efct_command_ctx {
struct list_head list_entry;
int (*cb)(struct efct_hw *hw, int status, u8 *mqe, void *arg);
void *arg; /* Argument for callback */
/* buffer holding command / results */
u8 buf[SLI4_BMBX_SIZE];
void *ctx; /* upper layer context */
};
struct efct_hw_sgl {
uintptr_t addr;
size_t len;
};
union efct_hw_io_param_u {
struct sli_bls_params bls;
struct sli_els_params els;
struct sli_ct_params fc_ct;
struct sli_fcp_tgt_params fcp_tgt;
};
/* WQ steering mode */
enum efct_hw_wq_steering {
EFCT_HW_WQ_STEERING_CLASS,
EFCT_HW_WQ_STEERING_REQUEST,
EFCT_HW_WQ_STEERING_CPU,
};
/* HW wqe object */
struct efct_hw_wqe {
struct list_head list_entry;
bool abort_wqe_submit_needed;
bool send_abts;
u32 id;
u32 abort_reqtag;
u8 *wqebuf;
};
struct efct_hw_io;
/* Typedef for HW "done" callback */
typedef int (*efct_hw_done_t)(struct efct_hw_io *, u32 len, int status,
u32 ext, void *ul_arg);
/**
* HW IO object.
*
* Stores the per-IO information necessary
* for both SLI and efct.
* @ref: reference counter for hw io object
* @state: state of IO: free, busy, wait_free
* @list_entry used for busy, wait_free, free lists
* @wqe Work queue object, with link for pending
* @hw pointer back to hardware context
* @xfer_rdy transfer ready data
* @type IO type
* @xbusy Exchange is active in FW
* @abort_in_progress if TRUE, abort is in progress
* @status_saved if TRUE, latched status should be returned
* @wq_class WQ class if steering mode is Class
* @reqtag request tag for this HW IO
* @wq WQ assigned to the exchange
* @done Function called on IO completion
* @arg argument passed to IO done callback
* @abort_done Function called on abort completion
* @abort_arg argument passed to abort done callback
* @wq_steering WQ steering mode request
* @saved_status Saved status
* @saved_len Status length
* @saved_ext Saved extended status
* @eq EQ on which this HIO came up
* @sge_offset SGE data offset
* @def_sgl_count Count of SGEs in default SGL
* @abort_reqtag request tag for an abort of this HW IO
* @indicator Exchange indicator
* @def_sgl default SGL
* @sgl pointer to current active SGL
* @sgl_count count of SGEs in io->sgl
* @first_data_sge index of first data SGE
* @n_sge number of active SGEs
*/
struct efct_hw_io {
struct kref ref;
enum efct_hw_io_state state;
void (*release)(struct kref *arg);
struct list_head list_entry;
struct efct_hw_wqe wqe;
struct efct_hw *hw;
struct efc_dma xfer_rdy;
u16 type;
bool xbusy;
int abort_in_progress;
bool status_saved;
u8 wq_class;
u16 reqtag;
struct hw_wq *wq;
efct_hw_done_t done;
void *arg;
efct_hw_done_t abort_done;
void *abort_arg;
enum efct_hw_wq_steering wq_steering;
u32 saved_status;
u32 saved_len;
u32 saved_ext;
struct hw_eq *eq;
u32 sge_offset;
u32 def_sgl_count;
u32 abort_reqtag;
u32 indicator;
struct efc_dma def_sgl;
struct efc_dma *sgl;
u32 sgl_count;
u32 first_data_sge;
u32 n_sge;
};
enum efct_hw_port {
EFCT_HW_PORT_INIT,
EFCT_HW_PORT_SHUTDOWN,
};
/* Node group rpi reference */
struct efct_hw_rpi_ref {
atomic_t rpi_count;
atomic_t rpi_attached;
};
enum efct_hw_link_stat {
EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT,
EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT,
EFCT_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT,
EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT,
EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT,
EFCT_HW_LINK_STAT_CRC_COUNT,
EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT,
EFCT_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT,
EFCT_HW_LINK_STAT_ARB_TIMEOUT_COUNT,
EFCT_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT,
EFCT_HW_LINK_STAT_CURR_RCV_B2B_CREDIT,
EFCT_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT,
EFCT_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT,
EFCT_HW_LINK_STAT_RCV_EOFA_COUNT,
EFCT_HW_LINK_STAT_RCV_EOFDTI_COUNT,
EFCT_HW_LINK_STAT_RCV_EOFNI_COUNT,
EFCT_HW_LINK_STAT_RCV_SOFF_COUNT,
EFCT_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT,
EFCT_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT,
EFCT_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT,
EFCT_HW_LINK_STAT_MAX,
};
enum efct_hw_host_stat {
EFCT_HW_HOST_STAT_TX_KBYTE_COUNT,
EFCT_HW_HOST_STAT_RX_KBYTE_COUNT,
EFCT_HW_HOST_STAT_TX_FRAME_COUNT,
EFCT_HW_HOST_STAT_RX_FRAME_COUNT,
EFCT_HW_HOST_STAT_TX_SEQ_COUNT,
EFCT_HW_HOST_STAT_RX_SEQ_COUNT,
EFCT_HW_HOST_STAT_TOTAL_EXCH_ORIG,
EFCT_HW_HOST_STAT_TOTAL_EXCH_RESP,
EFCT_HW_HOSY_STAT_RX_P_BSY_COUNT,
EFCT_HW_HOST_STAT_RX_F_BSY_COUNT,
EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT,
EFCT_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT,
EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT,
EFCT_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT,
EFCT_HW_HOST_STAT_MAX,
};
enum efct_hw_state {
EFCT_HW_STATE_UNINITIALIZED,
EFCT_HW_STATE_QUEUES_ALLOCATED,
EFCT_HW_STATE_ACTIVE,
EFCT_HW_STATE_RESET_IN_PROGRESS,
EFCT_HW_STATE_TEARDOWN_IN_PROGRESS,
};
struct efct_hw_link_stat_counts {
u8 overflow;
u32 counter;
};
struct efct_hw_host_stat_counts {
u32 counter;
};
/* Structure used for the hash lookup of queue IDs */
struct efct_queue_hash {
bool in_use;
u16 id;
u16 index;
};
/* WQ callback object */
struct hw_wq_callback {
u16 instance_index; /* use for request tag */
void (*callback)(void *arg, u8 *cqe, int status);
void *arg;
struct list_head list_entry;
};
struct reqtag_pool {
spinlock_t lock; /* pool lock */
struct hw_wq_callback *tags[U16_MAX];
struct list_head freelist;
};
struct efct_hw_config {
u32 n_eq;
u32 n_cq;
u32 n_mq;
u32 n_rq;
u32 n_wq;
u32 n_io;
u32 n_sgl;
u32 speed;
u32 topology;
/* size of the buffers for first burst */
u32 rq_default_buffer_size;
u8 esoc;
/* MRQ RQ selection policy */
u8 rq_selection_policy;
/* RQ quanta if rq_selection_policy == 2 */
u8 rr_quanta;
u32 filter_def[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
};
struct efct_hw {
struct efct *os;
struct sli4 sli;
u16 ulp_start;
u16 ulp_max;
u32 dump_size;
enum efct_hw_state state;
bool hw_setup_called;
u8 sliport_healthcheck;
u16 fcf_indicator;
/* HW configuration */
struct efct_hw_config config;
/* calculated queue sizes for each type */
u32 num_qentries[SLI4_QTYPE_MAX];
/* Storage for SLI queue objects */
struct sli4_queue wq[EFCT_HW_MAX_NUM_WQ];
struct sli4_queue rq[EFCT_HW_MAX_NUM_RQ];
u16 hw_rq_lookup[EFCT_HW_MAX_NUM_RQ];
struct sli4_queue mq[EFCT_HW_MAX_NUM_MQ];
struct sli4_queue cq[EFCT_HW_MAX_NUM_CQ];
struct sli4_queue eq[EFCT_HW_MAX_NUM_EQ];
/* HW queue */
u32 eq_count;
u32 cq_count;
u32 mq_count;
u32 wq_count;
u32 rq_count;
u32 cmd_head_count;
struct list_head eq_list;
struct efct_queue_hash cq_hash[EFCT_HW_Q_HASH_SIZE];
struct efct_queue_hash rq_hash[EFCT_HW_Q_HASH_SIZE];
struct efct_queue_hash wq_hash[EFCT_HW_Q_HASH_SIZE];
/* Storage for HW queue objects */
struct hw_wq *hw_wq[EFCT_HW_MAX_NUM_WQ];
struct hw_rq *hw_rq[EFCT_HW_MAX_NUM_RQ];
struct hw_mq *hw_mq[EFCT_HW_MAX_NUM_MQ];
struct hw_cq *hw_cq[EFCT_HW_MAX_NUM_CQ];
struct hw_eq *hw_eq[EFCT_HW_MAX_NUM_EQ];
/* count of hw_rq[] entries */
u32 hw_rq_count;
/* count of multirq RQs */
u32 hw_mrq_count;
struct hw_wq **wq_cpu_array;
/* Sequence objects used in incoming frame processing */
struct efc_hw_sequence *seq_pool;
/* Maintain an ordered, linked list of outstanding HW commands. */
struct mutex bmbx_lock;
spinlock_t cmd_lock;
struct list_head cmd_head;
struct list_head cmd_pending;
mempool_t *cmd_ctx_pool;
mempool_t *mbox_rqst_pool;
struct sli4_link_event link;
/* pointer array of IO objects */
struct efct_hw_io **io;
/* array of WQE buffs mapped to IO objects */
u8 *wqe_buffs;
/* IO lock to synchronize list access */
spinlock_t io_lock;
/* List of IO objects in use */
struct list_head io_inuse;
/* List of IO objects waiting to be freed */
struct list_head io_wait_free;
/* List of IO objects available for allocation */
struct list_head io_free;
struct efc_dma loop_map;
struct efc_dma xfer_rdy;
struct efc_dma rnode_mem;
atomic_t io_alloc_failed_count;
/* stat: wq sumbit count */
u32 tcmd_wq_submit[EFCT_HW_MAX_NUM_WQ];
/* stat: wq complete count */
u32 tcmd_wq_complete[EFCT_HW_MAX_NUM_WQ];
atomic_t send_frame_seq_id;
struct reqtag_pool *wq_reqtag_pool;
};
enum efct_hw_io_count_type {
EFCT_HW_IO_INUSE_COUNT,
EFCT_HW_IO_FREE_COUNT,
EFCT_HW_IO_WAIT_FREE_COUNT,
EFCT_HW_IO_N_TOTAL_IO_COUNT,
};
/* HW queue data structures */
struct hw_eq {
struct list_head list_entry;
enum sli4_qtype type;
u32 instance;
u32 entry_count;
u32 entry_size;
struct efct_hw *hw;
struct sli4_queue *queue;
struct list_head cq_list;
u32 use_count;
};
struct hw_cq {
struct list_head list_entry;
enum sli4_qtype type;
u32 instance;
u32 entry_count;
u32 entry_size;
struct hw_eq *eq;
struct sli4_queue *queue;
struct list_head q_list;
u32 use_count;
};
struct hw_q {
struct list_head list_entry;
enum sli4_qtype type;
};
struct hw_mq {
struct list_head list_entry;
enum sli4_qtype type;
u32 instance;
u32 entry_count;
u32 entry_size;
struct hw_cq *cq;
struct sli4_queue *queue;
u32 use_count;
};
struct hw_wq {
struct list_head list_entry;
enum sli4_qtype type;
u32 instance;
struct efct_hw *hw;
u32 entry_count;
u32 entry_size;
struct hw_cq *cq;
struct sli4_queue *queue;
u32 class;
/* WQ consumed */
u32 wqec_set_count;
u32 wqec_count;
u32 free_count;
u32 total_submit_count;
struct list_head pending_list;
/* HW IO allocated for use with Send Frame */
struct efct_hw_io *send_frame_io;
/* Stats */
u32 use_count;
u32 wq_pending_count;
};
struct hw_rq {
struct list_head list_entry;
enum sli4_qtype type;
u32 instance;
u32 entry_count;
u32 use_count;
u32 hdr_entry_size;
u32 first_burst_entry_size;
u32 data_entry_size;
bool is_mrq;
u32 base_mrq_id;
struct hw_cq *cq;
u8 filter_mask;
struct sli4_queue *hdr;
struct sli4_queue *first_burst;
struct sli4_queue *data;
struct efc_hw_rq_buffer *hdr_buf;
struct efc_hw_rq_buffer *fb_buf;
struct efc_hw_rq_buffer *payload_buf;
/* RQ tracker for this RQ */
struct efc_hw_sequence **rq_tracker;
};
struct efct_hw_send_frame_context {
struct efct_hw *hw;
struct hw_wq_callback *wqcb;
struct efct_hw_wqe wqe;
void (*callback)(int status, void *arg);
void *arg;
/* General purpose elements */
struct efc_hw_sequence *seq;
struct efc_dma payload;
};
struct efct_hw_grp_hdr {
u32 size;
__be32 magic_number;
u32 word2;
u8 rev_name[128];
u8 date[12];
u8 revision[32];
};
static inline int
efct_hw_get_link_speed(struct efct_hw *hw) {
return hw->link.speed;
}
int
efct_hw_setup(struct efct_hw *hw, void *os, struct pci_dev *pdev);
int efct_hw_init(struct efct_hw *hw);
int
efct_hw_parse_filter(struct efct_hw *hw, void *value);
int
efct_hw_init_queues(struct efct_hw *hw);
int
efct_hw_map_wq_cpu(struct efct_hw *hw);
uint64_t
efct_get_wwnn(struct efct_hw *hw);
uint64_t
efct_get_wwpn(struct efct_hw *hw);
int efct_hw_rx_allocate(struct efct_hw *hw);
int efct_hw_rx_post(struct efct_hw *hw);
void efct_hw_rx_free(struct efct_hw *hw);
int
efct_hw_command(struct efct_hw *hw, u8 *cmd, u32 opts, void *cb,
void *arg);
int
efct_issue_mbox_rqst(void *base, void *cmd, void *cb, void *arg);
struct efct_hw_io *efct_hw_io_alloc(struct efct_hw *hw);
int efct_hw_io_free(struct efct_hw *hw, struct efct_hw_io *io);
u8 efct_hw_io_inuse(struct efct_hw *hw, struct efct_hw_io *io);
int
efct_hw_io_send(struct efct_hw *hw, enum efct_hw_io_type type,
struct efct_hw_io *io, union efct_hw_io_param_u *iparam,
void *cb, void *arg);
int
efct_hw_io_register_sgl(struct efct_hw *hw, struct efct_hw_io *io,
struct efc_dma *sgl,
u32 sgl_count);
int
efct_hw_io_init_sges(struct efct_hw *hw,
struct efct_hw_io *io, enum efct_hw_io_type type);
int
efct_hw_io_add_sge(struct efct_hw *hw, struct efct_hw_io *io,
uintptr_t addr, u32 length);
int
efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort,
bool send_abts, void *cb, void *arg);
u32
efct_hw_io_get_count(struct efct_hw *hw,
enum efct_hw_io_count_type io_count_type);
struct efct_hw_io
*efct_hw_io_lookup(struct efct_hw *hw, u32 indicator);
void efct_hw_io_abort_all(struct efct_hw *hw);
void efct_hw_io_free_internal(struct kref *arg);
/* HW WQ request tag API */
struct reqtag_pool *efct_hw_reqtag_pool_alloc(struct efct_hw *hw);
void efct_hw_reqtag_pool_free(struct efct_hw *hw);
struct hw_wq_callback
*efct_hw_reqtag_alloc(struct efct_hw *hw,
void (*callback)(void *arg, u8 *cqe,
int status), void *arg);
void
efct_hw_reqtag_free(struct efct_hw *hw, struct hw_wq_callback *wqcb);
struct hw_wq_callback
*efct_hw_reqtag_get_instance(struct efct_hw *hw, u32 instance_index);
/* RQ completion handlers for RQ pair mode */
int
efct_hw_rqpair_process_rq(struct efct_hw *hw,
struct hw_cq *cq, u8 *cqe);
int
efct_hw_rqpair_sequence_free(struct efct_hw *hw, struct efc_hw_sequence *seq);
static inline void
efct_hw_sequence_copy(struct efc_hw_sequence *dst,
struct efc_hw_sequence *src)
{
/* Copy src to dst, then zero out the linked list link */
*dst = *src;
}
int
efct_efc_hw_sequence_free(struct efc *efc, struct efc_hw_sequence *seq);
static inline int
efct_hw_sequence_free(struct efct_hw *hw, struct efc_hw_sequence *seq)
{
/* Only RQ pair mode is supported */
return efct_hw_rqpair_sequence_free(hw, seq);
}
int
efct_hw_eq_process(struct efct_hw *hw, struct hw_eq *eq,
u32 max_isr_time_msec);
void efct_hw_cq_process(struct efct_hw *hw, struct hw_cq *cq);
void
efct_hw_wq_process(struct efct_hw *hw, struct hw_cq *cq,
u8 *cqe, int status, u16 rid);
void
efct_hw_xabt_process(struct efct_hw *hw, struct hw_cq *cq,
u8 *cqe, u16 rid);
int
efct_hw_process(struct efct_hw *hw, u32 vector, u32 max_isr_time_msec);
int
efct_hw_queue_hash_find(struct efct_queue_hash *hash, u16 id);
int efct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe);
int
efct_hw_send_frame(struct efct_hw *hw, struct fc_frame_header *hdr,
u8 sof, u8 eof, struct efc_dma *payload,
struct efct_hw_send_frame_context *ctx,
void (*callback)(void *arg, u8 *cqe, int status),
void *arg);
int
efct_els_hw_srrs_send(struct efc *efc, struct efc_disc_io *io);
int
efct_efc_bls_send(struct efc *efc, u32 type, struct sli_bls_params *bls);
int
efct_hw_bls_send(struct efct *efct, u32 type, struct sli_bls_params *bls_params,
void *cb, void *arg);
/* Function for retrieving link statistics */
int
efct_hw_get_link_stats(struct efct_hw *hw,
u8 req_ext_counters,
u8 clear_overflow_flags,
u8 clear_all_counters,
void (*efct_hw_link_stat_cb_t)(int status,
u32 num_counters,
struct efct_hw_link_stat_counts *counters, void *arg),
void *arg);
/* Function for retrieving host statistics */
int
efct_hw_get_host_stats(struct efct_hw *hw,
u8 cc,
void (*efct_hw_host_stat_cb_t)(int status,
u32 num_counters,
struct efct_hw_host_stat_counts *counters, void *arg),
void *arg);
int
efct_hw_firmware_write(struct efct_hw *hw, struct efc_dma *dma,
u32 size, u32 offset, int last,
void (*cb)(int status, u32 bytes_written,
u32 change_status, void *arg),
void *arg);
typedef void (*efct_hw_async_cb_t)(struct efct_hw *hw, int status,
u8 *mqe, void *arg);
int
efct_hw_async_call(struct efct_hw *hw, efct_hw_async_cb_t callback, void *arg);
struct hw_eq *efct_hw_new_eq(struct efct_hw *hw, u32 entry_count);
struct hw_cq *efct_hw_new_cq(struct hw_eq *eq, u32 entry_count);
u32
efct_hw_new_cq_set(struct hw_eq *eqs[], struct hw_cq *cqs[],
u32 num_cqs, u32 entry_count);
struct hw_mq *efct_hw_new_mq(struct hw_cq *cq, u32 entry_count);
struct hw_wq
*efct_hw_new_wq(struct hw_cq *cq, u32 entry_count);
u32
efct_hw_new_rq_set(struct hw_cq *cqs[], struct hw_rq *rqs[],
u32 num_rq_pairs, u32 entry_count);
void efct_hw_del_eq(struct hw_eq *eq);
void efct_hw_del_cq(struct hw_cq *cq);
void efct_hw_del_mq(struct hw_mq *mq);
void efct_hw_del_wq(struct hw_wq *wq);
void efct_hw_del_rq(struct hw_rq *rq);
void efct_hw_queue_teardown(struct efct_hw *hw);
void efct_hw_teardown(struct efct_hw *hw);
int
efct_hw_reset(struct efct_hw *hw, enum efct_hw_reset reset);
int
efct_hw_port_control(struct efct_hw *hw, enum efct_hw_port ctrl,
uintptr_t value,
void (*cb)(int status, uintptr_t value, void *arg),
void *arg);
#endif /* __EFCT_H__ */
@@ -0,0 +1,677 @@
// 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"
#include "efct_unsol.h"
int
efct_hw_init_queues(struct efct_hw *hw)
{
struct hw_eq *eq = NULL;
struct hw_cq *cq = NULL;
struct hw_wq *wq = NULL;
struct hw_mq *mq = NULL;
struct hw_eq *eqs[EFCT_HW_MAX_NUM_EQ];
struct hw_cq *cqs[EFCT_HW_MAX_NUM_EQ];
struct hw_rq *rqs[EFCT_HW_MAX_NUM_EQ];
u32 i = 0, j;
hw->eq_count = 0;
hw->cq_count = 0;
hw->mq_count = 0;
hw->wq_count = 0;
hw->rq_count = 0;
hw->hw_rq_count = 0;
INIT_LIST_HEAD(&hw->eq_list);
for (i = 0; i < hw->config.n_eq; i++) {
/* Create EQ */
eq = efct_hw_new_eq(hw, EFCT_HW_EQ_DEPTH);
if (!eq) {
efct_hw_queue_teardown(hw);
return -ENOMEM;
}
eqs[i] = eq;
/* Create one MQ */
if (!i) {
cq = efct_hw_new_cq(eq,
hw->num_qentries[SLI4_QTYPE_CQ]);
if (!cq) {
efct_hw_queue_teardown(hw);
return -ENOMEM;
}
mq = efct_hw_new_mq(cq, EFCT_HW_MQ_DEPTH);
if (!mq) {
efct_hw_queue_teardown(hw);
return -ENOMEM;
}
}
/* Create WQ */
cq = efct_hw_new_cq(eq, hw->num_qentries[SLI4_QTYPE_CQ]);
if (!cq) {
efct_hw_queue_teardown(hw);
return -ENOMEM;
}
wq = efct_hw_new_wq(cq, hw->num_qentries[SLI4_QTYPE_WQ]);
if (!wq) {
efct_hw_queue_teardown(hw);
return -ENOMEM;
}
}
/* Create CQ set */
if (efct_hw_new_cq_set(eqs, cqs, i, hw->num_qentries[SLI4_QTYPE_CQ])) {
efct_hw_queue_teardown(hw);
return -EIO;
}
/* Create RQ set */
if (efct_hw_new_rq_set(cqs, rqs, i, EFCT_HW_RQ_ENTRIES_DEF)) {
efct_hw_queue_teardown(hw);
return -EIO;
}
for (j = 0; j < i ; j++) {
rqs[j]->filter_mask = 0;
rqs[j]->is_mrq = true;
rqs[j]->base_mrq_id = rqs[0]->hdr->id;
}
hw->hw_mrq_count = i;
return 0;
}
int
efct_hw_map_wq_cpu(struct efct_hw *hw)
{
struct efct *efct = hw->os;
u32 cpu = 0, i;
/* Init cpu_map array */
hw->wq_cpu_array = kcalloc(num_possible_cpus(), sizeof(void *),
GFP_KERNEL);
if (!hw->wq_cpu_array)
return -ENOMEM;
for (i = 0; i < hw->config.n_eq; i++) {
const struct cpumask *maskp;
/* Get a CPU mask for all CPUs affinitized to this vector */
maskp = pci_irq_get_affinity(efct->pci, i);
if (!maskp) {
efc_log_debug(efct, "maskp null for vector:%d\n", i);
continue;
}
/* Loop through all CPUs associated with vector idx */
for_each_cpu_and(cpu, maskp, cpu_present_mask) {
efc_log_debug(efct, "CPU:%d irq vector:%d\n", cpu, i);
hw->wq_cpu_array[cpu] = hw->hw_wq[i];
}
}
return 0;
}
struct hw_eq *
efct_hw_new_eq(struct efct_hw *hw, u32 entry_count)
{
struct hw_eq *eq = kzalloc_obj(*eq);
if (!eq)
return NULL;
eq->type = SLI4_QTYPE_EQ;
eq->hw = hw;
eq->entry_count = entry_count;
eq->instance = hw->eq_count++;
eq->queue = &hw->eq[eq->instance];
INIT_LIST_HEAD(&eq->cq_list);
if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_EQ, eq->queue, entry_count,
NULL)) {
efc_log_err(hw->os, "EQ[%d] alloc failure\n", eq->instance);
kfree(eq);
return NULL;
}
sli_eq_modify_delay(&hw->sli, eq->queue, 1, 0, 8);
hw->hw_eq[eq->instance] = eq;
INIT_LIST_HEAD(&eq->list_entry);
list_add_tail(&eq->list_entry, &hw->eq_list);
efc_log_debug(hw->os, "create eq[%2d] id %3d len %4d\n", eq->instance,
eq->queue->id, eq->entry_count);
return eq;
}
struct hw_cq *
efct_hw_new_cq(struct hw_eq *eq, u32 entry_count)
{
struct efct_hw *hw = eq->hw;
struct hw_cq *cq = kzalloc_obj(*cq);
if (!cq)
return NULL;
cq->eq = eq;
cq->type = SLI4_QTYPE_CQ;
cq->instance = eq->hw->cq_count++;
cq->entry_count = entry_count;
cq->queue = &hw->cq[cq->instance];
INIT_LIST_HEAD(&cq->q_list);
if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_CQ, cq->queue,
cq->entry_count, eq->queue)) {
efc_log_err(hw->os, "CQ[%d] allocation failure len=%d\n",
eq->instance, eq->entry_count);
kfree(cq);
return NULL;
}
hw->hw_cq[cq->instance] = cq;
INIT_LIST_HEAD(&cq->list_entry);
list_add_tail(&cq->list_entry, &eq->cq_list);
efc_log_debug(hw->os, "create cq[%2d] id %3d len %4d\n", cq->instance,
cq->queue->id, cq->entry_count);
return cq;
}
u32
efct_hw_new_cq_set(struct hw_eq *eqs[], struct hw_cq *cqs[],
u32 num_cqs, u32 entry_count)
{
u32 i;
struct efct_hw *hw = eqs[0]->hw;
struct sli4 *sli4 = &hw->sli;
struct hw_cq *cq = NULL;
struct sli4_queue *qs[SLI4_MAX_CQ_SET_COUNT];
struct sli4_queue *assefct[SLI4_MAX_CQ_SET_COUNT];
/* Initialise CQS pointers to NULL */
for (i = 0; i < num_cqs; i++)
cqs[i] = NULL;
for (i = 0; i < num_cqs; i++) {
cq = kzalloc_obj(*cq);
if (!cq)
goto error;
cqs[i] = cq;
cq->eq = eqs[i];
cq->type = SLI4_QTYPE_CQ;
cq->instance = hw->cq_count++;
cq->entry_count = entry_count;
cq->queue = &hw->cq[cq->instance];
qs[i] = cq->queue;
assefct[i] = eqs[i]->queue;
INIT_LIST_HEAD(&cq->q_list);
}
if (sli_cq_alloc_set(sli4, qs, num_cqs, entry_count, assefct)) {
efc_log_err(hw->os, "Failed to create CQ Set.\n");
goto error;
}
for (i = 0; i < num_cqs; i++) {
hw->hw_cq[cqs[i]->instance] = cqs[i];
INIT_LIST_HEAD(&cqs[i]->list_entry);
list_add_tail(&cqs[i]->list_entry, &cqs[i]->eq->cq_list);
}
return 0;
error:
for (i = 0; i < num_cqs; i++) {
kfree(cqs[i]);
cqs[i] = NULL;
}
return -EIO;
}
struct hw_mq *
efct_hw_new_mq(struct hw_cq *cq, u32 entry_count)
{
struct efct_hw *hw = cq->eq->hw;
struct hw_mq *mq = kzalloc_obj(*mq);
if (!mq)
return NULL;
mq->cq = cq;
mq->type = SLI4_QTYPE_MQ;
mq->instance = cq->eq->hw->mq_count++;
mq->entry_count = entry_count;
mq->entry_size = EFCT_HW_MQ_DEPTH;
mq->queue = &hw->mq[mq->instance];
if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_MQ, mq->queue, mq->entry_size,
cq->queue)) {
efc_log_err(hw->os, "MQ allocation failure\n");
kfree(mq);
return NULL;
}
hw->hw_mq[mq->instance] = mq;
INIT_LIST_HEAD(&mq->list_entry);
list_add_tail(&mq->list_entry, &cq->q_list);
efc_log_debug(hw->os, "create mq[%2d] id %3d len %4d\n", mq->instance,
mq->queue->id, mq->entry_count);
return mq;
}
struct hw_wq *
efct_hw_new_wq(struct hw_cq *cq, u32 entry_count)
{
struct efct_hw *hw = cq->eq->hw;
struct hw_wq *wq = kzalloc_obj(*wq);
if (!wq)
return NULL;
wq->hw = cq->eq->hw;
wq->cq = cq;
wq->type = SLI4_QTYPE_WQ;
wq->instance = cq->eq->hw->wq_count++;
wq->entry_count = entry_count;
wq->queue = &hw->wq[wq->instance];
wq->wqec_set_count = EFCT_HW_WQEC_SET_COUNT;
wq->wqec_count = wq->wqec_set_count;
wq->free_count = wq->entry_count - 1;
INIT_LIST_HEAD(&wq->pending_list);
if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_WQ, wq->queue,
wq->entry_count, cq->queue)) {
efc_log_err(hw->os, "WQ allocation failure\n");
kfree(wq);
return NULL;
}
hw->hw_wq[wq->instance] = wq;
INIT_LIST_HEAD(&wq->list_entry);
list_add_tail(&wq->list_entry, &cq->q_list);
efc_log_debug(hw->os, "create wq[%2d] id %3d len %4d cls %d\n",
wq->instance, wq->queue->id, wq->entry_count, wq->class);
return wq;
}
u32
efct_hw_new_rq_set(struct hw_cq *cqs[], struct hw_rq *rqs[],
u32 num_rq_pairs, u32 entry_count)
{
struct efct_hw *hw = cqs[0]->eq->hw;
struct hw_rq *rq = NULL;
struct sli4_queue *qs[SLI4_MAX_RQ_SET_COUNT * 2] = { NULL };
u32 i, q_count, size;
/* Initialise RQS pointers */
for (i = 0; i < num_rq_pairs; i++)
rqs[i] = NULL;
/*
* Allocate an RQ object SET, where each element in set
* encapsulates 2 SLI queues (for rq pair)
*/
for (i = 0, q_count = 0; i < num_rq_pairs; i++, q_count += 2) {
rq = kzalloc_obj(*rq);
if (!rq)
goto error;
rqs[i] = rq;
rq->instance = hw->hw_rq_count++;
rq->cq = cqs[i];
rq->type = SLI4_QTYPE_RQ;
rq->entry_count = entry_count;
/* Header RQ */
rq->hdr = &hw->rq[hw->rq_count];
rq->hdr_entry_size = EFCT_HW_RQ_HEADER_SIZE;
hw->hw_rq_lookup[hw->rq_count] = rq->instance;
hw->rq_count++;
qs[q_count] = rq->hdr;
/* Data RQ */
rq->data = &hw->rq[hw->rq_count];
rq->data_entry_size = hw->config.rq_default_buffer_size;
hw->hw_rq_lookup[hw->rq_count] = rq->instance;
hw->rq_count++;
qs[q_count + 1] = rq->data;
rq->rq_tracker = NULL;
}
if (sli_fc_rq_set_alloc(&hw->sli, num_rq_pairs, qs,
cqs[0]->queue->id,
rqs[0]->entry_count,
rqs[0]->hdr_entry_size,
rqs[0]->data_entry_size)) {
efc_log_err(hw->os, "RQ Set alloc failure for base CQ=%d\n",
cqs[0]->queue->id);
goto error;
}
for (i = 0; i < num_rq_pairs; i++) {
hw->hw_rq[rqs[i]->instance] = rqs[i];
INIT_LIST_HEAD(&rqs[i]->list_entry);
list_add_tail(&rqs[i]->list_entry, &cqs[i]->q_list);
size = sizeof(struct efc_hw_sequence *) * rqs[i]->entry_count;
rqs[i]->rq_tracker = kzalloc(size, GFP_KERNEL);
if (!rqs[i]->rq_tracker)
goto error;
}
return 0;
error:
for (i = 0; i < num_rq_pairs; i++) {
if (rqs[i]) {
kfree(rqs[i]->rq_tracker);
kfree(rqs[i]);
}
}
return -EIO;
}
void
efct_hw_del_eq(struct hw_eq *eq)
{
struct hw_cq *cq;
struct hw_cq *cq_next;
if (!eq)
return;
list_for_each_entry_safe(cq, cq_next, &eq->cq_list, list_entry)
efct_hw_del_cq(cq);
list_del(&eq->list_entry);
eq->hw->hw_eq[eq->instance] = NULL;
kfree(eq);
}
void
efct_hw_del_cq(struct hw_cq *cq)
{
struct hw_q *q;
struct hw_q *q_next;
if (!cq)
return;
list_for_each_entry_safe(q, q_next, &cq->q_list, list_entry) {
switch (q->type) {
case SLI4_QTYPE_MQ:
efct_hw_del_mq((struct hw_mq *)q);
break;
case SLI4_QTYPE_WQ:
efct_hw_del_wq((struct hw_wq *)q);
break;
case SLI4_QTYPE_RQ:
efct_hw_del_rq((struct hw_rq *)q);
break;
default:
break;
}
}
list_del(&cq->list_entry);
cq->eq->hw->hw_cq[cq->instance] = NULL;
kfree(cq);
}
void
efct_hw_del_mq(struct hw_mq *mq)
{
if (!mq)
return;
list_del(&mq->list_entry);
mq->cq->eq->hw->hw_mq[mq->instance] = NULL;
kfree(mq);
}
void
efct_hw_del_wq(struct hw_wq *wq)
{
if (!wq)
return;
list_del(&wq->list_entry);
wq->cq->eq->hw->hw_wq[wq->instance] = NULL;
kfree(wq);
}
void
efct_hw_del_rq(struct hw_rq *rq)
{
struct efct_hw *hw = NULL;
if (!rq)
return;
/* Free RQ tracker */
kfree(rq->rq_tracker);
rq->rq_tracker = NULL;
list_del(&rq->list_entry);
hw = rq->cq->eq->hw;
hw->hw_rq[rq->instance] = NULL;
kfree(rq);
}
void
efct_hw_queue_teardown(struct efct_hw *hw)
{
struct hw_eq *eq;
struct hw_eq *eq_next;
if (!hw->eq_list.next)
return;
list_for_each_entry_safe(eq, eq_next, &hw->eq_list, list_entry)
efct_hw_del_eq(eq);
}
static inline int
efct_hw_rqpair_find(struct efct_hw *hw, u16 rq_id)
{
return efct_hw_queue_hash_find(hw->rq_hash, rq_id);
}
static struct efc_hw_sequence *
efct_hw_rqpair_get(struct efct_hw *hw, u16 rqindex, u16 bufindex)
{
struct sli4_queue *rq_hdr = &hw->rq[rqindex];
struct efc_hw_sequence *seq = NULL;
struct hw_rq *rq = hw->hw_rq[hw->hw_rq_lookup[rqindex]];
unsigned long flags = 0;
if (bufindex >= rq_hdr->length) {
efc_log_err(hw->os,
"RQidx %d bufidx %d exceed ring len %d for id %d\n",
rqindex, bufindex, rq_hdr->length, rq_hdr->id);
return NULL;
}
/* rq_hdr lock also covers rqindex+1 queue */
spin_lock_irqsave(&rq_hdr->lock, flags);
seq = rq->rq_tracker[bufindex];
rq->rq_tracker[bufindex] = NULL;
if (!seq) {
efc_log_err(hw->os,
"RQbuf NULL, rqidx %d, bufidx %d, cur q idx = %d\n",
rqindex, bufindex, rq_hdr->index);
}
spin_unlock_irqrestore(&rq_hdr->lock, flags);
return seq;
}
int
efct_hw_rqpair_process_rq(struct efct_hw *hw, struct hw_cq *cq,
u8 *cqe)
{
u16 rq_id;
u32 index;
int rqindex;
int rq_status;
u32 h_len;
u32 p_len;
struct efc_hw_sequence *seq;
struct hw_rq *rq;
rq_status = sli_fc_rqe_rqid_and_index(&hw->sli, cqe,
&rq_id, &index);
if (rq_status != 0) {
switch (rq_status) {
case SLI4_FC_ASYNC_RQ_BUF_LEN_EXCEEDED:
case SLI4_FC_ASYNC_RQ_DMA_FAILURE:
/* just get RQ buffer then return to chip */
rqindex = efct_hw_rqpair_find(hw, rq_id);
if (rqindex < 0) {
efc_log_debug(hw->os,
"status=%#x: lookup fail id=%#x\n",
rq_status, rq_id);
break;
}
/* get RQ buffer */
seq = efct_hw_rqpair_get(hw, rqindex, index);
/* return to chip */
if (efct_hw_rqpair_sequence_free(hw, seq)) {
efc_log_debug(hw->os,
"status=%#x,fail rtrn buf to RQ\n",
rq_status);
break;
}
break;
case SLI4_FC_ASYNC_RQ_INSUFF_BUF_NEEDED:
case SLI4_FC_ASYNC_RQ_INSUFF_BUF_FRM_DISC:
/*
* since RQ buffers were not consumed, cannot return
* them to chip
*/
efc_log_debug(hw->os, "Warning: RCQE status=%#x,\n",
rq_status);
fallthrough;
default:
break;
}
return -EIO;
}
rqindex = efct_hw_rqpair_find(hw, rq_id);
if (rqindex < 0) {
efc_log_debug(hw->os, "Error: rq_id lookup failed for id=%#x\n",
rq_id);
return -EIO;
}
rq = hw->hw_rq[hw->hw_rq_lookup[rqindex]];
rq->use_count++;
seq = efct_hw_rqpair_get(hw, rqindex, index);
if (WARN_ON(!seq))
return -EIO;
seq->hw = hw;
sli_fc_rqe_length(&hw->sli, cqe, &h_len, &p_len);
seq->header->dma.len = h_len;
seq->payload->dma.len = p_len;
seq->fcfi = sli_fc_rqe_fcfi(&hw->sli, cqe);
seq->hw_priv = cq->eq;
efct_unsolicited_cb(hw->os, seq);
return 0;
}
static int
efct_hw_rqpair_put(struct efct_hw *hw, struct efc_hw_sequence *seq)
{
struct sli4_queue *rq_hdr = &hw->rq[seq->header->rqindex];
struct sli4_queue *rq_payload = &hw->rq[seq->payload->rqindex];
u32 hw_rq_index = hw->hw_rq_lookup[seq->header->rqindex];
struct hw_rq *rq = hw->hw_rq[hw_rq_index];
u32 phys_hdr[2];
u32 phys_payload[2];
int qindex_hdr;
int qindex_payload;
unsigned long flags = 0;
/* Update the RQ verification lookup tables */
phys_hdr[0] = upper_32_bits(seq->header->dma.phys);
phys_hdr[1] = lower_32_bits(seq->header->dma.phys);
phys_payload[0] = upper_32_bits(seq->payload->dma.phys);
phys_payload[1] = lower_32_bits(seq->payload->dma.phys);
/* rq_hdr lock also covers payload / header->rqindex+1 queue */
spin_lock_irqsave(&rq_hdr->lock, flags);
/*
* Note: The header must be posted last for buffer pair mode because
* posting on the header queue posts the payload queue as well.
* We do not ring the payload queue independently in RQ pair mode.
*/
qindex_payload = sli_rq_write(&hw->sli, rq_payload,
(void *)phys_payload);
qindex_hdr = sli_rq_write(&hw->sli, rq_hdr, (void *)phys_hdr);
if (qindex_hdr < 0 ||
qindex_payload < 0) {
efc_log_err(hw->os, "RQ_ID=%#x write failed\n", rq_hdr->id);
spin_unlock_irqrestore(&rq_hdr->lock, flags);
return -EIO;
}
/* ensure the indexes are the same */
WARN_ON(qindex_hdr != qindex_payload);
/* Update the lookup table */
if (!rq->rq_tracker[qindex_hdr]) {
rq->rq_tracker[qindex_hdr] = seq;
} else {
efc_log_debug(hw->os,
"expected rq_tracker[%d][%d] buffer to be NULL\n",
hw_rq_index, qindex_hdr);
}
spin_unlock_irqrestore(&rq_hdr->lock, flags);
return 0;
}
int
efct_hw_rqpair_sequence_free(struct efct_hw *hw, struct efc_hw_sequence *seq)
{
int rc = 0;
/*
* Post the data buffer first. Because in RQ pair mode, ringing the
* doorbell of the header ring will post the data buffer as well.
*/
if (efct_hw_rqpair_put(hw, seq)) {
efc_log_err(hw->os, "error writing buffers\n");
return -EIO;
}
return rc;
}
int
efct_efc_hw_sequence_free(struct efc *efc, struct efc_hw_sequence *seq)
{
struct efct *efct = efc->base;
return efct_hw_rqpair_sequence_free(&efct->hw, seq);
}
+190
View File
@@ -0,0 +1,190 @@
// 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"
#include "efct_io.h"
struct efct_io_pool {
struct efct *efct;
spinlock_t lock; /* IO pool lock */
u32 io_num_ios; /* Total IOs allocated */
struct efct_io *ios[EFCT_NUM_SCSI_IOS];
struct list_head freelist;
};
struct efct_io_pool *
efct_io_pool_create(struct efct *efct, u32 num_sgl)
{
u32 i = 0;
struct efct_io_pool *io_pool;
struct efct_io *io;
/* Allocate the IO pool */
io_pool = kzalloc_obj(*io_pool);
if (!io_pool)
return NULL;
io_pool->efct = efct;
INIT_LIST_HEAD(&io_pool->freelist);
/* initialize IO pool lock */
spin_lock_init(&io_pool->lock);
for (i = 0; i < EFCT_NUM_SCSI_IOS; i++) {
io = kzalloc_obj(*io);
if (!io)
break;
io_pool->io_num_ios++;
io_pool->ios[i] = io;
io->tag = i;
io->instance_index = i;
/* Allocate a response buffer */
io->rspbuf.size = SCSI_RSP_BUF_LENGTH;
io->rspbuf.virt = dma_alloc_coherent(&efct->pci->dev,
io->rspbuf.size,
&io->rspbuf.phys, GFP_KERNEL);
if (!io->rspbuf.virt) {
efc_log_err(efct, "dma_alloc rspbuf failed\n");
efct_io_pool_free(io_pool);
return NULL;
}
/* Allocate SGL */
io->sgl = kzalloc(sizeof(*io->sgl) * num_sgl, GFP_KERNEL);
if (!io->sgl) {
efct_io_pool_free(io_pool);
return NULL;
}
io->sgl_allocated = num_sgl;
io->sgl_count = 0;
INIT_LIST_HEAD(&io->list_entry);
list_add_tail(&io->list_entry, &io_pool->freelist);
}
return io_pool;
}
int
efct_io_pool_free(struct efct_io_pool *io_pool)
{
struct efct *efct;
u32 i;
struct efct_io *io;
if (io_pool) {
efct = io_pool->efct;
for (i = 0; i < io_pool->io_num_ios; i++) {
io = io_pool->ios[i];
if (!io)
continue;
kfree(io->sgl);
dma_free_coherent(&efct->pci->dev,
io->rspbuf.size, io->rspbuf.virt,
io->rspbuf.phys);
memset(&io->rspbuf, 0, sizeof(struct efc_dma));
}
kfree(io_pool);
efct->xport->io_pool = NULL;
}
return 0;
}
struct efct_io *
efct_io_pool_io_alloc(struct efct_io_pool *io_pool)
{
struct efct_io *io = NULL;
struct efct *efct;
unsigned long flags = 0;
efct = io_pool->efct;
spin_lock_irqsave(&io_pool->lock, flags);
if (!list_empty(&io_pool->freelist)) {
io = list_first_entry(&io_pool->freelist, struct efct_io,
list_entry);
list_del_init(&io->list_entry);
}
spin_unlock_irqrestore(&io_pool->lock, flags);
if (!io)
return NULL;
io->io_type = EFCT_IO_TYPE_MAX;
io->hio_type = EFCT_HW_IO_MAX;
io->hio = NULL;
io->transferred = 0;
io->efct = efct;
io->timeout = 0;
io->sgl_count = 0;
io->tgt_task_tag = 0;
io->init_task_tag = 0;
io->hw_tag = 0;
io->display_name = "pending";
io->seq_init = 0;
io->io_free = 0;
io->release = NULL;
atomic_add_return(1, &efct->xport->io_active_count);
atomic_add_return(1, &efct->xport->io_total_alloc);
return io;
}
/* Free an object used to track an IO */
void
efct_io_pool_io_free(struct efct_io_pool *io_pool, struct efct_io *io)
{
struct efct *efct;
struct efct_hw_io *hio = NULL;
unsigned long flags = 0;
efct = io_pool->efct;
spin_lock_irqsave(&io_pool->lock, flags);
hio = io->hio;
io->hio = NULL;
io->io_free = 1;
INIT_LIST_HEAD(&io->list_entry);
list_add(&io->list_entry, &io_pool->freelist);
spin_unlock_irqrestore(&io_pool->lock, flags);
if (hio)
efct_hw_io_free(&efct->hw, hio);
atomic_sub_return(1, &efct->xport->io_active_count);
atomic_add_return(1, &efct->xport->io_total_free);
}
/* Find an I/O given it's node and ox_id */
struct efct_io *
efct_io_find_tgt_io(struct efct *efct, struct efct_node *node,
u16 ox_id, u16 rx_id)
{
struct efct_io *io = NULL;
unsigned long flags = 0;
u8 found = false;
spin_lock_irqsave(&node->active_ios_lock, flags);
list_for_each_entry(io, &node->active_ios, list_entry) {
if ((io->cmd_tgt && io->init_task_tag == ox_id) &&
(rx_id == 0xffff || io->tgt_task_tag == rx_id)) {
if (kref_get_unless_zero(&io->ref))
found = true;
break;
}
}
spin_unlock_irqrestore(&node->active_ios_lock, flags);
return found ? io : NULL;
}
+174
View File
@@ -0,0 +1,174 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
#if !defined(__EFCT_IO_H__)
#define __EFCT_IO_H__
#include "efct_lio.h"
#define EFCT_LOG_ENABLE_IO_ERRORS(efct) \
(((efct) != NULL) ? (((efct)->logmask & (1U << 6)) != 0) : 0)
#define io_error_log(io, fmt, ...) \
do { \
if (EFCT_LOG_ENABLE_IO_ERRORS(io->efct)) \
efc_log_warn(io->efct, fmt, ##__VA_ARGS__); \
} while (0)
#define SCSI_CMD_BUF_LENGTH 48
#define SCSI_RSP_BUF_LENGTH (FCP_RESP_WITH_EXT + SCSI_SENSE_BUFFERSIZE)
#define EFCT_NUM_SCSI_IOS 8192
enum efct_io_type {
EFCT_IO_TYPE_IO = 0,
EFCT_IO_TYPE_ELS,
EFCT_IO_TYPE_CT,
EFCT_IO_TYPE_CT_RESP,
EFCT_IO_TYPE_BLS_RESP,
EFCT_IO_TYPE_ABORT,
EFCT_IO_TYPE_MAX,
};
enum efct_els_state {
EFCT_ELS_REQUEST = 0,
EFCT_ELS_REQUEST_DELAYED,
EFCT_ELS_REQUEST_DELAY_ABORT,
EFCT_ELS_REQ_ABORT,
EFCT_ELS_REQ_ABORTED,
EFCT_ELS_ABORT_IO_COMPL,
};
/**
* Scsi target IO object
* @efct: pointer back to efct
* @instance_index: unique instance index value
* @io: IO display name
* @node: pointer to node
* @list_entry: io list entry
* @io_pending_link: io pending list entry
* @ref: reference counter
* @release: release callback function
* @init_task_tag: initiator task tag (OX_ID) for back-end and SCSI logging
* @tgt_task_tag: target task tag (RX_ID) for back-end and SCSI logging
* @hw_tag: HW layer unique IO id
* @tag: unique IO identifier
* @sgl: SGL
* @sgl_allocated: Number of allocated SGEs
* @sgl_count: Number of SGEs in this SGL
* @tgt_io: backend target private IO data
* @exp_xfer_len: expected data transfer length, based on FC header
* @hw_priv: Declarations private to HW/SLI
* @io_type: indicates what this struct efct_io structure is used for
* @hio: hw io object
* @transferred: Number of bytes transferred
* @auto_resp: set if auto_trsp was set
* @low_latency: set if low latency request
* @wq_steering: selected WQ steering request
* @wq_class: selected WQ class if steering is class
* @xfer_req: transfer size for current request
* @scsi_tgt_cb: target callback function
* @scsi_tgt_cb_arg: target callback function argument
* @abort_cb: abort callback function
* @abort_cb_arg: abort callback function argument
* @bls_cb: BLS callback function
* @bls_cb_arg: BLS callback function argument
* @tmf_cmd: TMF command being processed
* @abort_rx_id: rx_id from the ABTS that initiated the command abort
* @cmd_tgt: True if this is a Target command
* @send_abts: when aborting, indicates ABTS is to be sent
* @cmd_ini: True if this is an Initiator command
* @seq_init: True if local node has sequence initiative
* @iparam: iparams for hw io send call
* @hio_type: HW IO type
* @wire_len: wire length
* @hw_cb: saved HW callback
* @io_to_abort: for abort handling, pointer to IO to abort
* @rspbuf: SCSI Response buffer
* @timeout: Timeout value in seconds for this IO
* @cs_ctl: CS_CTL priority for this IO
* @io_free: Is io object in freelist
* @app_id: application id
*/
struct efct_io {
struct efct *efct;
u32 instance_index;
const char *display_name;
struct efct_node *node;
struct list_head list_entry;
struct list_head io_pending_link;
struct kref ref;
void (*release)(struct kref *arg);
u32 init_task_tag;
u32 tgt_task_tag;
u32 hw_tag;
u32 tag;
struct efct_scsi_sgl *sgl;
u32 sgl_allocated;
u32 sgl_count;
struct efct_scsi_tgt_io tgt_io;
u32 exp_xfer_len;
void *hw_priv;
enum efct_io_type io_type;
struct efct_hw_io *hio;
size_t transferred;
bool auto_resp;
bool low_latency;
u8 wq_steering;
u8 wq_class;
u64 xfer_req;
efct_scsi_io_cb_t scsi_tgt_cb;
void *scsi_tgt_cb_arg;
efct_scsi_io_cb_t abort_cb;
void *abort_cb_arg;
efct_scsi_io_cb_t bls_cb;
void *bls_cb_arg;
enum efct_scsi_tmf_cmd tmf_cmd;
u16 abort_rx_id;
bool cmd_tgt;
bool send_abts;
bool cmd_ini;
bool seq_init;
union efct_hw_io_param_u iparam;
enum efct_hw_io_type hio_type;
u64 wire_len;
void *hw_cb;
struct efct_io *io_to_abort;
struct efc_dma rspbuf;
u32 timeout;
u8 cs_ctl;
u8 io_free;
u32 app_id;
};
struct efct_io_cb_arg {
int status;
int ext_status;
void *app;
};
struct efct_io_pool *
efct_io_pool_create(struct efct *efct, u32 num_sgl);
int
efct_io_pool_free(struct efct_io_pool *io_pool);
u32
efct_io_pool_allocated(struct efct_io_pool *io_pool);
struct efct_io *
efct_io_pool_io_alloc(struct efct_io_pool *io_pool);
void
efct_io_pool_io_free(struct efct_io_pool *io_pool, struct efct_io *io);
struct efct_io *
efct_io_find_tgt_io(struct efct *efct, struct efct_node *node,
u16 ox_id, u16 rx_id);
#endif /* __EFCT_IO_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,189 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
#ifndef __EFCT_LIO_H__
#define __EFCT_LIO_H__
#include "efct_scsi.h"
#include <target/target_core_base.h>
#define efct_lio_io_printf(io, fmt, ...) \
efc_log_debug(io->efct, \
"[%s] [%04x][i:%04x t:%04x h:%04x]" fmt,\
io->node->display_name, io->instance_index, \
io->init_task_tag, io->tgt_task_tag, io->hw_tag,\
##__VA_ARGS__)
#define efct_lio_tmfio_printf(io, fmt, ...) \
efc_log_debug(io->efct, \
"[%s] [%04x][i:%04x t:%04x h:%04x][f:%02x]" fmt,\
io->node->display_name, io->instance_index, \
io->init_task_tag, io->tgt_task_tag, io->hw_tag,\
io->tgt_io.tmf, ##__VA_ARGS__)
#define efct_set_lio_io_state(io, value) (io->tgt_io.state |= value)
struct efct_lio_wq_data {
struct efct *efct;
void *ptr;
struct work_struct work;
};
/* Target private efct structure */
struct efct_scsi_tgt {
u32 max_sge;
u32 max_sgl;
/*
* Variables used to send task set full. We are using a high watermark
* method to send task set full. We will reserve a fixed number of IOs
* per initiator plus a fudge factor. Once we reach this number,
* then the target will start sending task set full/busy responses.
*/
atomic_t initiator_count;
atomic_t ios_in_use;
atomic_t io_high_watermark;
atomic_t watermark_hit;
int watermark_min;
int watermark_max;
struct efct_lio_nport *lio_nport;
struct efct_lio_tpg *tpg;
struct list_head vport_list;
/* Protects vport list*/
spinlock_t efct_lio_lock;
u64 wwnn;
};
struct efct_scsi_tgt_nport {
struct efct_lio_nport *lio_nport;
};
struct efct_node {
struct list_head list_entry;
struct kref ref;
void (*release)(struct kref *arg);
struct efct *efct;
struct efc_node *node;
struct se_session *session;
spinlock_t active_ios_lock;
struct list_head active_ios;
char display_name[EFC_NAME_LENGTH];
u32 port_fc_id;
u32 node_fc_id;
u32 vpi;
u32 rpi;
u32 abort_cnt;
};
#define EFCT_LIO_STATE_SCSI_RECV_CMD (1 << 0)
#define EFCT_LIO_STATE_TGT_SUBMIT_CMD (1 << 1)
#define EFCT_LIO_STATE_TFO_QUEUE_DATA_IN (1 << 2)
#define EFCT_LIO_STATE_TFO_WRITE_PENDING (1 << 3)
#define EFCT_LIO_STATE_TGT_EXECUTE_CMD (1 << 4)
#define EFCT_LIO_STATE_SCSI_SEND_RD_DATA (1 << 5)
#define EFCT_LIO_STATE_TFO_CHK_STOP_FREE (1 << 6)
#define EFCT_LIO_STATE_SCSI_DATA_DONE (1 << 7)
#define EFCT_LIO_STATE_TFO_QUEUE_STATUS (1 << 8)
#define EFCT_LIO_STATE_SCSI_SEND_RSP (1 << 9)
#define EFCT_LIO_STATE_SCSI_RSP_DONE (1 << 10)
#define EFCT_LIO_STATE_TGT_GENERIC_FREE (1 << 11)
#define EFCT_LIO_STATE_SCSI_RECV_TMF (1 << 12)
#define EFCT_LIO_STATE_TGT_SUBMIT_TMR (1 << 13)
#define EFCT_LIO_STATE_TFO_WRITE_PEND_STATUS (1 << 14)
#define EFCT_LIO_STATE_TGT_GENERIC_REQ_FAILURE (1 << 15)
#define EFCT_LIO_STATE_TFO_ABORTED_TASK (1 << 29)
#define EFCT_LIO_STATE_TFO_RELEASE_CMD (1 << 30)
#define EFCT_LIO_STATE_SCSI_CMPL_CMD (1u << 31)
struct efct_scsi_tgt_io {
struct se_cmd cmd;
unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER];
enum dma_data_direction ddir;
int task_attr;
u64 lun;
u32 state;
u8 tmf;
struct efct_io *io_to_abort;
u32 seg_map_cnt;
u32 seg_cnt;
u32 cur_seg;
enum efct_scsi_io_status err;
bool aborting;
bool rsp_sent;
u32 transferred_len;
};
/* Handler return codes */
enum {
SCSI_HANDLER_DATAPHASE_STARTED = 1,
SCSI_HANDLER_RESP_STARTED,
SCSI_HANDLER_VALIDATED_DATAPHASE_STARTED,
SCSI_CMD_NOT_SUPPORTED,
};
#define WWN_NAME_LEN 32
struct efct_lio_vport {
u64 wwpn;
u64 npiv_wwpn;
u64 npiv_wwnn;
unsigned char wwpn_str[WWN_NAME_LEN];
struct se_wwn vport_wwn;
struct efct_lio_tpg *tpg;
struct efct *efct;
struct Scsi_Host *shost;
struct fc_vport *fc_vport;
atomic_t enable;
};
struct efct_lio_nport {
u64 wwpn;
unsigned char wwpn_str[WWN_NAME_LEN];
struct se_wwn nport_wwn;
struct efct_lio_tpg *tpg;
struct efct *efct;
atomic_t enable;
};
struct efct_lio_tpg_attrib {
u32 generate_node_acls;
u32 cache_dynamic_acls;
u32 demo_mode_write_protect;
u32 prod_mode_write_protect;
u32 demo_mode_login_only;
bool session_deletion_wait;
};
struct efct_lio_tpg {
struct se_portal_group tpg;
struct efct_lio_nport *nport;
struct efct_lio_vport *vport;
struct efct_lio_tpg_attrib tpg_attrib;
unsigned short tpgt;
bool enabled;
};
struct efct_lio_nacl {
u64 nport_wwnn;
char nport_name[WWN_NAME_LEN];
struct se_session *session;
struct se_node_acl se_node_acl;
};
struct efct_lio_vport_list_t {
struct list_head list_entry;
struct efct_lio_vport *lio_vport;
};
int efct_scsi_tgt_driver_init(void);
int efct_scsi_tgt_driver_exit(void);
#endif /*__EFCT_LIO_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,203 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
#if !defined(__EFCT_SCSI_H__)
#define __EFCT_SCSI_H__
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
/* efct_scsi_rcv_cmd() efct_scsi_rcv_tmf() flags */
#define EFCT_SCSI_CMD_DIR_IN (1 << 0)
#define EFCT_SCSI_CMD_DIR_OUT (1 << 1)
#define EFCT_SCSI_CMD_SIMPLE (1 << 2)
#define EFCT_SCSI_CMD_HEAD_OF_QUEUE (1 << 3)
#define EFCT_SCSI_CMD_ORDERED (1 << 4)
#define EFCT_SCSI_CMD_UNTAGGED (1 << 5)
#define EFCT_SCSI_CMD_ACA (1 << 6)
#define EFCT_SCSI_FIRST_BURST_ERR (1 << 7)
#define EFCT_SCSI_FIRST_BURST_ABORTED (1 << 8)
/* efct_scsi_send_rd_data/recv_wr_data/send_resp flags */
#define EFCT_SCSI_LAST_DATAPHASE (1 << 0)
#define EFCT_SCSI_NO_AUTO_RESPONSE (1 << 1)
#define EFCT_SCSI_LOW_LATENCY (1 << 2)
#define EFCT_SCSI_SNS_BUF_VALID(sense) ((sense) && \
(0x70 == (((const u8 *)(sense))[0] & 0x70)))
#define EFCT_SCSI_WQ_STEERING_SHIFT 16
#define EFCT_SCSI_WQ_STEERING_MASK (0xf << EFCT_SCSI_WQ_STEERING_SHIFT)
#define EFCT_SCSI_WQ_STEERING_CLASS (0 << EFCT_SCSI_WQ_STEERING_SHIFT)
#define EFCT_SCSI_WQ_STEERING_REQUEST (1 << EFCT_SCSI_WQ_STEERING_SHIFT)
#define EFCT_SCSI_WQ_STEERING_CPU (2 << EFCT_SCSI_WQ_STEERING_SHIFT)
#define EFCT_SCSI_WQ_CLASS_SHIFT (20)
#define EFCT_SCSI_WQ_CLASS_MASK (0xf << EFCT_SCSI_WQ_CLASS_SHIFT)
#define EFCT_SCSI_WQ_CLASS(x) ((x & EFCT_SCSI_WQ_CLASS_MASK) << \
EFCT_SCSI_WQ_CLASS_SHIFT)
#define EFCT_SCSI_WQ_CLASS_LOW_LATENCY 1
struct efct_scsi_cmd_resp {
u8 scsi_status;
u16 scsi_status_qualifier;
u8 *response_data;
u32 response_data_length;
u8 *sense_data;
u32 sense_data_length;
int residual;
u32 response_wire_length;
};
struct efct_vport {
struct efct *efct;
bool is_vport;
struct fc_host_statistics fc_host_stats;
struct Scsi_Host *shost;
struct fc_vport *fc_vport;
u64 npiv_wwpn;
u64 npiv_wwnn;
};
/* Status values returned by IO callbacks */
enum efct_scsi_io_status {
EFCT_SCSI_STATUS_GOOD = 0,
EFCT_SCSI_STATUS_ABORTED,
EFCT_SCSI_STATUS_ERROR,
EFCT_SCSI_STATUS_DIF_GUARD_ERR,
EFCT_SCSI_STATUS_DIF_REF_TAG_ERROR,
EFCT_SCSI_STATUS_DIF_APP_TAG_ERROR,
EFCT_SCSI_STATUS_DIF_UNKNOWN_ERROR,
EFCT_SCSI_STATUS_PROTOCOL_CRC_ERROR,
EFCT_SCSI_STATUS_NO_IO,
EFCT_SCSI_STATUS_ABORT_IN_PROGRESS,
EFCT_SCSI_STATUS_CHECK_RESPONSE,
EFCT_SCSI_STATUS_COMMAND_TIMEOUT,
EFCT_SCSI_STATUS_TIMEDOUT_AND_ABORTED,
EFCT_SCSI_STATUS_SHUTDOWN,
EFCT_SCSI_STATUS_NEXUS_LOST,
};
struct efct_node;
struct efct_io;
struct efc_node;
struct efc_nport;
/* Callback used by send_rd_data(), recv_wr_data(), send_resp() */
typedef int (*efct_scsi_io_cb_t)(struct efct_io *io,
enum efct_scsi_io_status status,
u32 flags, void *arg);
/* Callback used by send_rd_io(), send_wr_io() */
typedef int (*efct_scsi_rsp_io_cb_t)(struct efct_io *io,
enum efct_scsi_io_status status,
struct efct_scsi_cmd_resp *rsp,
u32 flags, void *arg);
/* efct_scsi_cb_t flags */
#define EFCT_SCSI_IO_CMPL (1 << 0)
/* IO completed, response sent */
#define EFCT_SCSI_IO_CMPL_RSP_SENT (1 << 1)
#define EFCT_SCSI_IO_ABORTED (1 << 2)
/* efct_scsi_recv_tmf() request values */
enum efct_scsi_tmf_cmd {
EFCT_SCSI_TMF_ABORT_TASK = 1,
EFCT_SCSI_TMF_QUERY_TASK_SET,
EFCT_SCSI_TMF_ABORT_TASK_SET,
EFCT_SCSI_TMF_CLEAR_TASK_SET,
EFCT_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT,
EFCT_SCSI_TMF_LOGICAL_UNIT_RESET,
EFCT_SCSI_TMF_CLEAR_ACA,
EFCT_SCSI_TMF_TARGET_RESET,
};
/* efct_scsi_send_tmf_resp() response values */
enum efct_scsi_tmf_resp {
EFCT_SCSI_TMF_FUNCTION_COMPLETE = 1,
EFCT_SCSI_TMF_FUNCTION_SUCCEEDED,
EFCT_SCSI_TMF_FUNCTION_IO_NOT_FOUND,
EFCT_SCSI_TMF_FUNCTION_REJECTED,
EFCT_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER,
EFCT_SCSI_TMF_SERVICE_DELIVERY,
};
struct efct_scsi_sgl {
uintptr_t addr;
uintptr_t dif_addr;
size_t len;
};
enum efct_scsi_io_role {
EFCT_SCSI_IO_ROLE_ORIGINATOR,
EFCT_SCSI_IO_ROLE_RESPONDER,
};
struct efct_io *
efct_scsi_io_alloc(struct efct_node *node);
void efct_scsi_io_free(struct efct_io *io);
struct efct_io *efct_io_get_instance(struct efct *efct, u32 index);
int efct_scsi_tgt_driver_init(void);
int efct_scsi_tgt_driver_exit(void);
int efct_scsi_tgt_new_device(struct efct *efct);
int efct_scsi_tgt_del_device(struct efct *efct);
int
efct_scsi_tgt_new_nport(struct efc *efc, struct efc_nport *nport);
void
efct_scsi_tgt_del_nport(struct efc *efc, struct efc_nport *nport);
int
efct_scsi_new_initiator(struct efc *efc, struct efc_node *node);
enum efct_scsi_del_initiator_reason {
EFCT_SCSI_INITIATOR_DELETED,
EFCT_SCSI_INITIATOR_MISSING,
};
int
efct_scsi_del_initiator(struct efc *efc, struct efc_node *node, int reason);
void
efct_scsi_recv_cmd(struct efct_io *io, uint64_t lun, u8 *cdb, u32 cdb_len,
u32 flags);
int
efct_scsi_recv_tmf(struct efct_io *tmfio, u32 lun, enum efct_scsi_tmf_cmd cmd,
struct efct_io *abortio, u32 flags);
int
efct_scsi_send_rd_data(struct efct_io *io, u32 flags, struct efct_scsi_sgl *sgl,
u32 sgl_count, u64 wire_len, efct_scsi_io_cb_t cb, void *arg);
int
efct_scsi_recv_wr_data(struct efct_io *io, u32 flags, struct efct_scsi_sgl *sgl,
u32 sgl_count, u64 wire_len, efct_scsi_io_cb_t cb, void *arg);
int
efct_scsi_send_resp(struct efct_io *io, u32 flags,
struct efct_scsi_cmd_resp *rsp, efct_scsi_io_cb_t cb, void *arg);
int
efct_scsi_send_tmf_resp(struct efct_io *io, enum efct_scsi_tmf_resp rspcode,
u8 addl_rsp_info[3], efct_scsi_io_cb_t cb, void *arg);
int
efct_scsi_tgt_abort_io(struct efct_io *io, efct_scsi_io_cb_t cb, void *arg);
void efct_scsi_io_complete(struct efct_io *io);
int efct_scsi_reg_fc_transport(void);
void efct_scsi_release_fc_transport(void);
int efct_scsi_new_device(struct efct *efct);
void efct_scsi_del_device(struct efct *efct);
void _efct_scsi_io_free(struct kref *arg);
int
efct_scsi_del_vport(struct efct *efct, struct Scsi_Host *shost);
struct efct_vport *
efct_scsi_new_vport(struct efct *efct, struct device *dev);
int efct_scsi_io_dispatch(struct efct_io *io, void *cb);
int efct_scsi_io_dispatch_abort(struct efct_io *io, void *cb);
void efct_scsi_check_pending(struct efct *efct);
struct efct_io *
efct_bls_send_rjt(struct efct_io *io, struct fc_frame_header *hdr);
#endif /* __EFCT_SCSI_H__ */
@@ -0,0 +1,493 @@
// 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_unsol.h"
#define frame_printf(efct, hdr, fmt, ...) \
do { \
char s_id_text[16]; \
efc_node_fcid_display(ntoh24((hdr)->fh_s_id), \
s_id_text, sizeof(s_id_text)); \
efc_log_debug(efct, "[%06x.%s] %02x/%04x/%04x: " fmt, \
ntoh24((hdr)->fh_d_id), s_id_text, \
(hdr)->fh_r_ctl, be16_to_cpu((hdr)->fh_ox_id), \
be16_to_cpu((hdr)->fh_rx_id), ##__VA_ARGS__); \
} while (0)
static struct efct_node *
efct_node_find(struct efct *efct, u32 port_id, u32 node_id)
{
struct efct_node *node;
u64 id = (u64)port_id << 32 | node_id;
/*
* During node shutdown, Lookup will be removed first,
* before announcing to backend. So, no new IOs will be allowed
*/
/* Find a target node, given s_id and d_id */
node = xa_load(&efct->lookup, id);
if (node)
kref_get(&node->ref);
return node;
}
static int
efct_dispatch_frame(struct efct *efct, struct efc_hw_sequence *seq)
{
struct efct_node *node;
struct fc_frame_header *hdr;
u32 s_id, d_id;
hdr = seq->header->dma.virt;
/* extract the s_id and d_id */
s_id = ntoh24(hdr->fh_s_id);
d_id = ntoh24(hdr->fh_d_id);
if (!(hdr->fh_type == FC_TYPE_FCP || hdr->fh_type == FC_TYPE_BLS))
return -EIO;
if (hdr->fh_type == FC_TYPE_FCP) {
node = efct_node_find(efct, d_id, s_id);
if (!node) {
efc_log_err(efct,
"Node not found, drop cmd d_id:%x s_id:%x\n",
d_id, s_id);
efct_hw_sequence_free(&efct->hw, seq);
return 0;
}
efct_dispatch_fcp_cmd(node, seq);
} else {
node = efct_node_find(efct, d_id, s_id);
if (!node) {
efc_log_err(efct, "ABTS: Node not found, d_id:%x s_id:%x\n",
d_id, s_id);
return -EIO;
}
efc_log_err(efct, "Received ABTS for Node:%p\n", node);
efct_node_recv_abts_frame(node, seq);
}
kref_put(&node->ref, node->release);
efct_hw_sequence_free(&efct->hw, seq);
return 0;
}
int
efct_unsolicited_cb(void *arg, struct efc_hw_sequence *seq)
{
struct efct *efct = arg;
/* Process FCP command */
if (!efct_dispatch_frame(efct, seq))
return 0;
/* Forward frame to discovery lib */
efc_dispatch_frame(efct->efcport, seq);
return 0;
}
static int
efct_fc_tmf_rejected_cb(struct efct_io *io,
enum efct_scsi_io_status scsi_status,
u32 flags, void *arg)
{
efct_scsi_io_free(io);
return 0;
}
static void
efct_dispatch_unsol_tmf(struct efct_io *io, u8 tm_flags, u32 lun)
{
u32 i;
struct {
u32 mask;
enum efct_scsi_tmf_cmd cmd;
} tmflist[] = {
{FCP_TMF_ABT_TASK_SET, EFCT_SCSI_TMF_ABORT_TASK_SET},
{FCP_TMF_CLR_TASK_SET, EFCT_SCSI_TMF_CLEAR_TASK_SET},
{FCP_TMF_LUN_RESET, EFCT_SCSI_TMF_LOGICAL_UNIT_RESET},
{FCP_TMF_TGT_RESET, EFCT_SCSI_TMF_TARGET_RESET},
{FCP_TMF_CLR_ACA, EFCT_SCSI_TMF_CLEAR_ACA} };
io->exp_xfer_len = 0;
for (i = 0; i < ARRAY_SIZE(tmflist); i++) {
if (tmflist[i].mask & tm_flags) {
io->tmf_cmd = tmflist[i].cmd;
efct_scsi_recv_tmf(io, lun, tmflist[i].cmd, NULL, 0);
break;
}
}
if (i == ARRAY_SIZE(tmflist)) {
/* Not handled */
efc_log_err(io->node->efct, "TMF x%x rejected\n", tm_flags);
efct_scsi_send_tmf_resp(io, EFCT_SCSI_TMF_FUNCTION_REJECTED,
NULL, efct_fc_tmf_rejected_cb, NULL);
}
}
static int
efct_validate_fcp_cmd(struct efct *efct, struct efc_hw_sequence *seq)
{
/*
* If we received less than FCP_CMND_IU bytes, assume that the frame is
* corrupted in some way and drop it.
* This was seen when jamming the FCTL
* fill bytes field.
*/
if (seq->payload->dma.len < sizeof(struct fcp_cmnd)) {
struct fc_frame_header *fchdr = seq->header->dma.virt;
efc_log_debug(efct,
"drop ox_id %04x payload (%zd) less than (%zd)\n",
be16_to_cpu(fchdr->fh_ox_id),
seq->payload->dma.len, sizeof(struct fcp_cmnd));
return -EIO;
}
return 0;
}
static void
efct_populate_io_fcp_cmd(struct efct_io *io, struct fcp_cmnd *cmnd,
struct fc_frame_header *fchdr, bool sit)
{
io->init_task_tag = be16_to_cpu(fchdr->fh_ox_id);
/* note, tgt_task_tag, hw_tag set when HW io is allocated */
io->exp_xfer_len = be32_to_cpu(cmnd->fc_dl);
io->transferred = 0;
/* The upper 7 bits of CS_CTL is the frame priority thru the SAN.
* Our assertion here is, the priority given to a frame containing
* the FCP cmd should be the priority given to ALL frames contained
* in that IO. Thus we need to save the incoming CS_CTL here.
*/
if (ntoh24(fchdr->fh_f_ctl) & FC_FC_RES_B17)
io->cs_ctl = fchdr->fh_cs_ctl;
else
io->cs_ctl = 0;
io->seq_init = sit;
}
static u32
efct_get_flags_fcp_cmd(struct fcp_cmnd *cmnd)
{
u32 flags = 0;
switch (cmnd->fc_pri_ta & FCP_PTA_MASK) {
case FCP_PTA_SIMPLE:
flags |= EFCT_SCSI_CMD_SIMPLE;
break;
case FCP_PTA_HEADQ:
flags |= EFCT_SCSI_CMD_HEAD_OF_QUEUE;
break;
case FCP_PTA_ORDERED:
flags |= EFCT_SCSI_CMD_ORDERED;
break;
case FCP_PTA_ACA:
flags |= EFCT_SCSI_CMD_ACA;
break;
}
if (cmnd->fc_flags & FCP_CFL_WRDATA)
flags |= EFCT_SCSI_CMD_DIR_IN;
if (cmnd->fc_flags & FCP_CFL_RDDATA)
flags |= EFCT_SCSI_CMD_DIR_OUT;
return flags;
}
static void
efct_sframe_common_send_cb(void *arg, u8 *cqe, int status)
{
struct efct_hw_send_frame_context *ctx = arg;
struct efct_hw *hw = ctx->hw;
/* Free WQ completion callback */
efct_hw_reqtag_free(hw, ctx->wqcb);
/* Free sequence */
efct_hw_sequence_free(hw, ctx->seq);
}
static int
efct_sframe_common_send(struct efct_node *node,
struct efc_hw_sequence *seq,
enum fc_rctl r_ctl, u32 f_ctl,
u8 type, void *payload, u32 payload_len)
{
struct efct *efct = node->efct;
struct efct_hw *hw = &efct->hw;
int rc = 0;
struct fc_frame_header *req_hdr = seq->header->dma.virt;
struct fc_frame_header hdr;
struct efct_hw_send_frame_context *ctx;
u32 heap_size = seq->payload->dma.size;
uintptr_t heap_phys_base = seq->payload->dma.phys;
u8 *heap_virt_base = seq->payload->dma.virt;
u32 heap_offset = 0;
/* Build the FC header reusing the RQ header DMA buffer */
memset(&hdr, 0, sizeof(hdr));
hdr.fh_r_ctl = r_ctl;
/* send it back to whomever sent it to us */
memcpy(hdr.fh_d_id, req_hdr->fh_s_id, sizeof(hdr.fh_d_id));
memcpy(hdr.fh_s_id, req_hdr->fh_d_id, sizeof(hdr.fh_s_id));
hdr.fh_type = type;
hton24(hdr.fh_f_ctl, f_ctl);
hdr.fh_ox_id = req_hdr->fh_ox_id;
hdr.fh_rx_id = req_hdr->fh_rx_id;
hdr.fh_cs_ctl = 0;
hdr.fh_df_ctl = 0;
hdr.fh_seq_cnt = 0;
hdr.fh_parm_offset = 0;
/*
* send_frame_seq_id is an atomic, we just let it increment,
* while storing only the low 8 bits to hdr->seq_id
*/
hdr.fh_seq_id = (u8)atomic_add_return(1, &hw->send_frame_seq_id);
hdr.fh_seq_id--;
/* Allocate and fill in the send frame request context */
ctx = (void *)(heap_virt_base + heap_offset);
heap_offset += sizeof(*ctx);
if (heap_offset > heap_size) {
efc_log_err(efct, "Fill send frame failed offset %d size %d\n",
heap_offset, heap_size);
return -EIO;
}
memset(ctx, 0, sizeof(*ctx));
/* Save sequence */
ctx->seq = seq;
/* Allocate a response payload DMA buffer from the heap */
ctx->payload.phys = heap_phys_base + heap_offset;
ctx->payload.virt = heap_virt_base + heap_offset;
ctx->payload.size = payload_len;
ctx->payload.len = payload_len;
heap_offset += payload_len;
if (heap_offset > heap_size) {
efc_log_err(efct, "Fill send frame failed offset %d size %d\n",
heap_offset, heap_size);
return -EIO;
}
/* Copy the payload in */
memcpy(ctx->payload.virt, payload, payload_len);
/* Send */
rc = efct_hw_send_frame(&efct->hw, (void *)&hdr, FC_SOF_N3,
FC_EOF_T, &ctx->payload, ctx,
efct_sframe_common_send_cb, ctx);
if (rc)
efc_log_debug(efct, "efct_hw_send_frame failed: %d\n", rc);
return rc;
}
static int
efct_sframe_send_fcp_rsp(struct efct_node *node, struct efc_hw_sequence *seq,
void *rsp, u32 rsp_len)
{
return efct_sframe_common_send(node, seq, FC_RCTL_DD_CMD_STATUS,
FC_FC_EX_CTX |
FC_FC_LAST_SEQ |
FC_FC_END_SEQ |
FC_FC_SEQ_INIT,
FC_TYPE_FCP,
rsp, rsp_len);
}
static int
efct_sframe_send_task_set_full_or_busy(struct efct_node *node,
struct efc_hw_sequence *seq)
{
struct fcp_resp_with_ext fcprsp;
struct fcp_cmnd *fcpcmd = seq->payload->dma.virt;
int rc = 0;
unsigned long flags = 0;
struct efct *efct = node->efct;
/* construct task set full or busy response */
memset(&fcprsp, 0, sizeof(fcprsp));
spin_lock_irqsave(&node->active_ios_lock, flags);
fcprsp.resp.fr_status = list_empty(&node->active_ios) ?
SAM_STAT_BUSY : SAM_STAT_TASK_SET_FULL;
spin_unlock_irqrestore(&node->active_ios_lock, flags);
*((u32 *)&fcprsp.ext.fr_resid) = be32_to_cpu(fcpcmd->fc_dl);
/* send it using send_frame */
rc = efct_sframe_send_fcp_rsp(node, seq, &fcprsp, sizeof(fcprsp));
if (rc)
efc_log_debug(efct, "efct_sframe_send_fcp_rsp failed %d\n", rc);
return rc;
}
int
efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq)
{
struct efct *efct = node->efct;
struct fc_frame_header *fchdr = seq->header->dma.virt;
struct fcp_cmnd *cmnd = NULL;
struct efct_io *io = NULL;
u32 lun;
if (!seq->payload) {
efc_log_err(efct, "Sequence payload is NULL.\n");
return -EIO;
}
cmnd = seq->payload->dma.virt;
/* perform FCP_CMND validation check(s) */
if (efct_validate_fcp_cmd(efct, seq))
return -EIO;
lun = scsilun_to_int(&cmnd->fc_lun);
if (lun == U32_MAX)
return -EIO;
io = efct_scsi_io_alloc(node);
if (!io) {
int rc;
/* Use SEND_FRAME to send task set full or busy */
rc = efct_sframe_send_task_set_full_or_busy(node, seq);
if (rc)
efc_log_err(efct, "Failed to send busy task: %d\n", rc);
return rc;
}
io->hw_priv = seq->hw_priv;
io->app_id = 0;
/* RQ pair, if we got here, SIT=1 */
efct_populate_io_fcp_cmd(io, cmnd, fchdr, true);
if (cmnd->fc_tm_flags) {
efct_dispatch_unsol_tmf(io, cmnd->fc_tm_flags, lun);
} else {
u32 flags = efct_get_flags_fcp_cmd(cmnd);
if (cmnd->fc_flags & FCP_CFL_LEN_MASK) {
efc_log_err(efct, "Additional CDB not supported\n");
efct_scsi_io_free(io);
return -EIO;
}
/*
* Can return failure for things like task set full and UAs,
* no need to treat as a dropped frame if rc != 0
*/
efct_scsi_recv_cmd(io, lun, cmnd->fc_cdb,
sizeof(cmnd->fc_cdb), flags);
}
return 0;
}
static int
efct_process_abts(struct efct_io *io, struct fc_frame_header *hdr)
{
struct efct_node *node = io->node;
struct efct *efct = io->efct;
u16 ox_id = be16_to_cpu(hdr->fh_ox_id);
u16 rx_id = be16_to_cpu(hdr->fh_rx_id);
struct efct_io *abortio;
/* Find IO and attempt to take a reference on it */
abortio = efct_io_find_tgt_io(efct, node, ox_id, rx_id);
if (abortio) {
/* Got a reference on the IO. Hold it until backend
* is notified below
*/
efc_log_info(node->efct, "Abort ox_id [%04x] rx_id [%04x]\n",
ox_id, rx_id);
/*
* Save the ox_id for the ABTS as the init_task_tag in our
* manufactured
* TMF IO object
*/
io->display_name = "abts";
io->init_task_tag = ox_id;
/* don't set tgt_task_tag, don't want to confuse with XRI */
/*
* Save the rx_id from the ABTS as it is
* needed for the BLS response,
* regardless of the IO context's rx_id
*/
io->abort_rx_id = rx_id;
/* Call target server command abort */
io->tmf_cmd = EFCT_SCSI_TMF_ABORT_TASK;
efct_scsi_recv_tmf(io, abortio->tgt_io.lun,
EFCT_SCSI_TMF_ABORT_TASK, abortio, 0);
/*
* Backend will have taken an additional
* reference on the IO if needed;
* done with current reference.
*/
kref_put(&abortio->ref, abortio->release);
} else {
/*
* Either IO was not found or it has been
* freed between finding it
* and attempting to get the reference,
*/
efc_log_info(node->efct, "Abort: ox_id [%04x], IO not found\n",
ox_id);
/* Send a BA_RJT */
efct_bls_send_rjt(io, hdr);
}
return 0;
}
int
efct_node_recv_abts_frame(struct efct_node *node, struct efc_hw_sequence *seq)
{
struct efct *efct = node->efct;
struct fc_frame_header *hdr = seq->header->dma.virt;
struct efct_io *io = NULL;
node->abort_cnt++;
io = efct_scsi_io_alloc(node);
if (io) {
io->hw_priv = seq->hw_priv;
/* If we got this far, SIT=1 */
io->seq_init = 1;
/* fill out generic fields */
io->efct = efct;
io->node = node;
io->cmd_tgt = true;
efct_process_abts(io, seq->header->dma.virt);
} else {
efc_log_err(efct,
"SCSI IO allocation failed for ABTS received ");
efc_log_err(efct, "s_id %06x d_id %06x ox_id %04x rx_id %04x\n",
ntoh24(hdr->fh_s_id), ntoh24(hdr->fh_d_id),
be16_to_cpu(hdr->fh_ox_id),
be16_to_cpu(hdr->fh_rx_id));
}
return 0;
}
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
#if !defined(__OSC_UNSOL_H__)
#define __OSC_UNSOL_H__
int
efct_unsolicited_cb(void *arg, struct efc_hw_sequence *seq);
int
efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq);
int
efct_node_recv_abts_frame(struct efct_node *node, struct efc_hw_sequence *seq);
#endif /* __OSC_UNSOL_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,186 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 Broadcom. All Rights Reserved. The term
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*/
#if !defined(__EFCT_XPORT_H__)
#define __EFCT_XPORT_H__
enum efct_xport_ctrl {
EFCT_XPORT_PORT_ONLINE = 1,
EFCT_XPORT_PORT_OFFLINE,
EFCT_XPORT_SHUTDOWN,
EFCT_XPORT_POST_NODE_EVENT,
EFCT_XPORT_WWNN_SET,
EFCT_XPORT_WWPN_SET,
};
enum efct_xport_status {
EFCT_XPORT_PORT_STATUS,
EFCT_XPORT_CONFIG_PORT_STATUS,
EFCT_XPORT_LINK_SPEED,
EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,
EFCT_XPORT_LINK_STATISTICS,
EFCT_XPORT_LINK_STAT_RESET,
EFCT_XPORT_IS_QUIESCED
};
struct efct_xport_link_stats {
bool rec;
bool gec;
bool w02of;
bool w03of;
bool w04of;
bool w05of;
bool w06of;
bool w07of;
bool w08of;
bool w09of;
bool w10of;
bool w11of;
bool w12of;
bool w13of;
bool w14of;
bool w15of;
bool w16of;
bool w17of;
bool w18of;
bool w19of;
bool w20of;
bool w21of;
bool clrc;
bool clof1;
u32 link_failure_error_count;
u32 loss_of_sync_error_count;
u32 loss_of_signal_error_count;
u32 primitive_sequence_error_count;
u32 invalid_transmission_word_error_count;
u32 crc_error_count;
u32 primitive_sequence_event_timeout_count;
u32 elastic_buffer_overrun_error_count;
u32 arbitration_fc_al_timeout_count;
u32 advertised_receive_bufftor_to_buffer_credit;
u32 current_receive_buffer_to_buffer_credit;
u32 advertised_transmit_buffer_to_buffer_credit;
u32 current_transmit_buffer_to_buffer_credit;
u32 received_eofa_count;
u32 received_eofdti_count;
u32 received_eofni_count;
u32 received_soff_count;
u32 received_dropped_no_aer_count;
u32 received_dropped_no_available_rpi_resources_count;
u32 received_dropped_no_available_xri_resources_count;
};
struct efct_xport_host_stats {
bool cc;
u32 transmit_kbyte_count;
u32 receive_kbyte_count;
u32 transmit_frame_count;
u32 receive_frame_count;
u32 transmit_sequence_count;
u32 receive_sequence_count;
u32 total_exchanges_originator;
u32 total_exchanges_responder;
u32 receive_p_bsy_count;
u32 receive_f_bsy_count;
u32 dropped_frames_due_to_no_rq_buffer_count;
u32 empty_rq_timeout_count;
u32 dropped_frames_due_to_no_xri_count;
u32 empty_xri_pool_count;
};
struct efct_xport_host_statistics {
struct completion done;
struct efct_xport_link_stats link_stats;
struct efct_xport_host_stats host_stats;
};
union efct_xport_stats_u {
u32 value;
struct efct_xport_host_statistics stats;
};
struct efct_xport_fcp_stats {
u64 input_bytes;
u64 output_bytes;
u64 input_requests;
u64 output_requests;
u64 control_requests;
};
struct efct_xport {
struct efct *efct;
/* wwpn requested by user for primary nport */
u64 req_wwpn;
/* wwnn requested by user for primary nport */
u64 req_wwnn;
/* Nodes */
/* number of allocated nodes */
u32 nodes_count;
/* used to track how often IO pool is empty */
atomic_t io_alloc_failed_count;
/* array of pointers to nodes */
struct efc_node **nodes;
/* Io pool and counts */
/* pointer to IO pool */
struct efct_io_pool *io_pool;
/* lock for io_pending_list */
spinlock_t io_pending_lock;
/* list of IOs waiting for HW resources
* lock: xport->io_pending_lock
* link: efct_io_s->io_pending_link
*/
struct list_head io_pending_list;
/* count of totals IOS allocated */
atomic_t io_total_alloc;
/* count of totals IOS free'd */
atomic_t io_total_free;
/* count of totals IOS that were pended */
atomic_t io_total_pending;
/* count of active IOS */
atomic_t io_active_count;
/* count of pending IOS */
atomic_t io_pending_count;
/* non-zero if efct_scsi_check_pending is executing */
atomic_t io_pending_recursing;
/* Port */
/* requested link state */
u32 configured_link_state;
/* Timer for Statistics */
struct timer_list stats_timer;
union efct_xport_stats_u fc_xport_stats;
struct efct_xport_fcp_stats fcp_stats;
};
struct efct_rport_data {
struct efc_node *node;
};
struct efct_xport *
efct_xport_alloc(struct efct *efct);
int
efct_xport_attach(struct efct_xport *xport);
int
efct_xport_initialize(struct efct_xport *xport);
void
efct_xport_detach(struct efct_xport *xport);
int
efct_xport_control(struct efct_xport *xport, enum efct_xport_ctrl cmd, ...);
int
efct_xport_status(struct efct_xport *xport, enum efct_xport_status cmd,
union efct_xport_stats_u *result);
void
efct_xport_free(struct efct_xport *xport);
struct scsi_transport_template *efct_attach_fc_transport(void);
struct scsi_transport_template *efct_attach_vport_fc_transport(void);
void
efct_release_fc_transport(struct scsi_transport_template *transport_template);
#endif /* __EFCT_XPORT_H__ */