191 lines
4.1 KiB
C
191 lines
4.1 KiB
C
// 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(sizeof(*io_pool), GFP_KERNEL);
|
|
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(sizeof(*io), GFP_KERNEL);
|
|
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;
|
|
}
|