365 lines
8.6 KiB
C
365 lines
8.6 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
/*
|
||
|
* Driver for Broadcom MPI3 Storage Controllers
|
||
|
*
|
||
|
* Copyright (C) 2017-2021 Broadcom Inc.
|
||
|
* (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "mpi3mr.h"
|
||
|
|
||
|
/* global driver scop variables */
|
||
|
LIST_HEAD(mrioc_list);
|
||
|
DEFINE_SPINLOCK(mrioc_list_lock);
|
||
|
static int mrioc_ids;
|
||
|
static int warn_non_secure_ctlr;
|
||
|
|
||
|
MODULE_AUTHOR(MPI3MR_DRIVER_AUTHOR);
|
||
|
MODULE_DESCRIPTION(MPI3MR_DRIVER_DESC);
|
||
|
MODULE_LICENSE(MPI3MR_DRIVER_LICENSE);
|
||
|
MODULE_VERSION(MPI3MR_DRIVER_VERSION);
|
||
|
|
||
|
/* Module parameters*/
|
||
|
int logging_level;
|
||
|
module_param(logging_level, int, 0);
|
||
|
MODULE_PARM_DESC(logging_level,
|
||
|
" bits for enabling additional logging info (default=0)");
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_map_queues - Map queues callback handler
|
||
|
* @shost: SCSI host reference
|
||
|
*
|
||
|
* Call the blk_mq_pci_map_queues with from which operational
|
||
|
* queue the mapping has to be done
|
||
|
*
|
||
|
* Return: return of blk_mq_pci_map_queues
|
||
|
*/
|
||
|
static int mpi3mr_map_queues(struct Scsi_Host *shost)
|
||
|
{
|
||
|
struct mpi3mr_ioc *mrioc = shost_priv(shost);
|
||
|
|
||
|
return blk_mq_pci_map_queues(&shost->tag_set.map[HCTX_TYPE_DEFAULT],
|
||
|
mrioc->pdev, 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_slave_destroy - Slave destroy callback handler
|
||
|
* @sdev: SCSI device reference
|
||
|
*
|
||
|
* Cleanup and free per device(lun) private data.
|
||
|
*
|
||
|
* Return: Nothing.
|
||
|
*/
|
||
|
static void mpi3mr_slave_destroy(struct scsi_device *sdev)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_target_destroy - Target destroy callback handler
|
||
|
* @starget: SCSI target reference
|
||
|
*
|
||
|
* Cleanup and free per target private data.
|
||
|
*
|
||
|
* Return: Nothing.
|
||
|
*/
|
||
|
static void mpi3mr_target_destroy(struct scsi_target *starget)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_slave_configure - Slave configure callback handler
|
||
|
* @sdev: SCSI device reference
|
||
|
*
|
||
|
* Configure queue depth, max hardware sectors and virt boundary
|
||
|
* as required
|
||
|
*
|
||
|
* Return: 0 always.
|
||
|
*/
|
||
|
static int mpi3mr_slave_configure(struct scsi_device *sdev)
|
||
|
{
|
||
|
int retval = 0;
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_slave_alloc -Slave alloc callback handler
|
||
|
* @sdev: SCSI device reference
|
||
|
*
|
||
|
* Allocate per device(lun) private data and initialize it.
|
||
|
*
|
||
|
* Return: 0 on success -ENOMEM on memory allocation failure.
|
||
|
*/
|
||
|
static int mpi3mr_slave_alloc(struct scsi_device *sdev)
|
||
|
{
|
||
|
int retval = 0;
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_target_alloc - Target alloc callback handler
|
||
|
* @starget: SCSI target reference
|
||
|
*
|
||
|
* Allocate per target private data and initialize it.
|
||
|
*
|
||
|
* Return: 0 on success -ENOMEM on memory allocation failure.
|
||
|
*/
|
||
|
static int mpi3mr_target_alloc(struct scsi_target *starget)
|
||
|
{
|
||
|
int retval = -ENODEV;
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_qcmd - I/O request despatcher
|
||
|
* @shost: SCSI Host reference
|
||
|
* @scmd: SCSI Command reference
|
||
|
*
|
||
|
* Issues the SCSI Command as an MPI3 request.
|
||
|
*
|
||
|
* Return: 0 on successful queueing of the request or if the
|
||
|
* request is completed with failure.
|
||
|
* SCSI_MLQUEUE_DEVICE_BUSY when the device is busy.
|
||
|
* SCSI_MLQUEUE_HOST_BUSY when the host queue is full.
|
||
|
*/
|
||
|
static int mpi3mr_qcmd(struct Scsi_Host *shost,
|
||
|
struct scsi_cmnd *scmd)
|
||
|
{
|
||
|
int retval = 0;
|
||
|
|
||
|
scmd->result = DID_NO_CONNECT << 16;
|
||
|
scmd->scsi_done(scmd);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
static struct scsi_host_template mpi3mr_driver_template = {
|
||
|
.module = THIS_MODULE,
|
||
|
.name = "MPI3 Storage Controller",
|
||
|
.proc_name = MPI3MR_DRIVER_NAME,
|
||
|
.queuecommand = mpi3mr_qcmd,
|
||
|
.target_alloc = mpi3mr_target_alloc,
|
||
|
.slave_alloc = mpi3mr_slave_alloc,
|
||
|
.slave_configure = mpi3mr_slave_configure,
|
||
|
.target_destroy = mpi3mr_target_destroy,
|
||
|
.slave_destroy = mpi3mr_slave_destroy,
|
||
|
.map_queues = mpi3mr_map_queues,
|
||
|
.no_write_same = 1,
|
||
|
.can_queue = 1,
|
||
|
.this_id = -1,
|
||
|
.sg_tablesize = MPI3MR_SG_DEPTH,
|
||
|
/* max xfer supported is 1M (2K in 512 byte sized sectors)
|
||
|
*/
|
||
|
.max_sectors = 2048,
|
||
|
.cmd_per_lun = MPI3MR_MAX_CMDS_LUN,
|
||
|
.track_queue_depth = 1,
|
||
|
.cmd_size = sizeof(struct scmd_priv),
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_init_drv_cmd - Initialize internal command tracker
|
||
|
* @cmdptr: Internal command tracker
|
||
|
* @host_tag: Host tag used for the specific command
|
||
|
*
|
||
|
* Initialize the internal command tracker structure with
|
||
|
* specified host tag.
|
||
|
*
|
||
|
* Return: Nothing.
|
||
|
*/
|
||
|
static inline void mpi3mr_init_drv_cmd(struct mpi3mr_drv_cmd *cmdptr,
|
||
|
u16 host_tag)
|
||
|
{
|
||
|
mutex_init(&cmdptr->mutex);
|
||
|
cmdptr->reply = NULL;
|
||
|
cmdptr->state = MPI3MR_CMD_NOTUSED;
|
||
|
cmdptr->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
|
||
|
cmdptr->host_tag = host_tag;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_probe - PCI probe callback
|
||
|
* @pdev: PCI device instance
|
||
|
* @id: PCI device ID details
|
||
|
*
|
||
|
* controller initialization routine. Checks the security status
|
||
|
* of the controller and if it is invalid or tampered return the
|
||
|
* probe without initializing the controller. Otherwise,
|
||
|
* allocate per adapter instance through shost_priv and
|
||
|
* initialize controller specific data structures, initializae
|
||
|
* the controller hardware, add shost to the SCSI subsystem.
|
||
|
*
|
||
|
* Return: 0 on success, non-zero on failure.
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||
|
{
|
||
|
struct mpi3mr_ioc *mrioc = NULL;
|
||
|
struct Scsi_Host *shost = NULL;
|
||
|
int retval = 0;
|
||
|
|
||
|
shost = scsi_host_alloc(&mpi3mr_driver_template,
|
||
|
sizeof(struct mpi3mr_ioc));
|
||
|
if (!shost) {
|
||
|
retval = -ENODEV;
|
||
|
goto shost_failed;
|
||
|
}
|
||
|
|
||
|
mrioc = shost_priv(shost);
|
||
|
mrioc->id = mrioc_ids++;
|
||
|
sprintf(mrioc->driver_name, "%s", MPI3MR_DRIVER_NAME);
|
||
|
sprintf(mrioc->name, "%s%d", mrioc->driver_name, mrioc->id);
|
||
|
INIT_LIST_HEAD(&mrioc->list);
|
||
|
spin_lock(&mrioc_list_lock);
|
||
|
list_add_tail(&mrioc->list, &mrioc_list);
|
||
|
spin_unlock(&mrioc_list_lock);
|
||
|
|
||
|
spin_lock_init(&mrioc->admin_req_lock);
|
||
|
spin_lock_init(&mrioc->reply_free_queue_lock);
|
||
|
spin_lock_init(&mrioc->sbq_lock);
|
||
|
|
||
|
mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS);
|
||
|
|
||
|
mrioc->logging_level = logging_level;
|
||
|
mrioc->shost = shost;
|
||
|
mrioc->pdev = pdev;
|
||
|
|
||
|
/* init shost parameters */
|
||
|
shost->max_cmd_len = MPI3MR_MAX_CDB_LENGTH;
|
||
|
shost->max_lun = -1;
|
||
|
shost->unique_id = mrioc->id;
|
||
|
|
||
|
shost->max_channel = 1;
|
||
|
shost->max_id = 0xFFFFFFFF;
|
||
|
|
||
|
mrioc->is_driver_loading = 1;
|
||
|
if (mpi3mr_init_ioc(mrioc)) {
|
||
|
ioc_err(mrioc, "failure at %s:%d/%s()!\n",
|
||
|
__FILE__, __LINE__, __func__);
|
||
|
retval = -ENODEV;
|
||
|
goto out_iocinit_failed;
|
||
|
}
|
||
|
|
||
|
shost->nr_hw_queues = mrioc->num_op_reply_q;
|
||
|
shost->can_queue = mrioc->max_host_ios;
|
||
|
shost->sg_tablesize = MPI3MR_SG_DEPTH;
|
||
|
shost->max_id = mrioc->facts.max_perids;
|
||
|
|
||
|
retval = scsi_add_host(shost, &pdev->dev);
|
||
|
if (retval) {
|
||
|
ioc_err(mrioc, "failure at %s:%d/%s()!\n",
|
||
|
__FILE__, __LINE__, __func__);
|
||
|
goto addhost_failed;
|
||
|
}
|
||
|
|
||
|
scsi_scan_host(shost);
|
||
|
return retval;
|
||
|
|
||
|
addhost_failed:
|
||
|
mpi3mr_cleanup_ioc(mrioc);
|
||
|
out_iocinit_failed:
|
||
|
spin_lock(&mrioc_list_lock);
|
||
|
list_del(&mrioc->list);
|
||
|
spin_unlock(&mrioc_list_lock);
|
||
|
scsi_host_put(shost);
|
||
|
shost_failed:
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_remove - PCI remove callback
|
||
|
* @pdev: PCI device instance
|
||
|
*
|
||
|
* Free up all memory and resources associated with the
|
||
|
* controllerand target devices, unregister the shost.
|
||
|
*
|
||
|
* Return: Nothing.
|
||
|
*/
|
||
|
static void mpi3mr_remove(struct pci_dev *pdev)
|
||
|
{
|
||
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||
|
struct mpi3mr_ioc *mrioc;
|
||
|
|
||
|
mrioc = shost_priv(shost);
|
||
|
while (mrioc->reset_in_progress || mrioc->is_driver_loading)
|
||
|
ssleep(1);
|
||
|
|
||
|
scsi_remove_host(shost);
|
||
|
|
||
|
mpi3mr_cleanup_ioc(mrioc);
|
||
|
|
||
|
spin_lock(&mrioc_list_lock);
|
||
|
list_del(&mrioc->list);
|
||
|
spin_unlock(&mrioc_list_lock);
|
||
|
|
||
|
scsi_host_put(shost);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* mpi3mr_shutdown - PCI shutdown callback
|
||
|
* @pdev: PCI device instance
|
||
|
*
|
||
|
* Free up all memory and resources associated with the
|
||
|
* controller
|
||
|
*
|
||
|
* Return: Nothing.
|
||
|
*/
|
||
|
static void mpi3mr_shutdown(struct pci_dev *pdev)
|
||
|
{
|
||
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
||
|
struct mpi3mr_ioc *mrioc;
|
||
|
|
||
|
if (!shost)
|
||
|
return;
|
||
|
|
||
|
mrioc = shost_priv(shost);
|
||
|
while (mrioc->reset_in_progress || mrioc->is_driver_loading)
|
||
|
ssleep(1);
|
||
|
|
||
|
mpi3mr_cleanup_ioc(mrioc);
|
||
|
}
|
||
|
|
||
|
static const struct pci_device_id mpi3mr_pci_id_table[] = {
|
||
|
{
|
||
|
PCI_DEVICE_SUB(PCI_VENDOR_ID_LSI_LOGIC, 0x00A5,
|
||
|
PCI_ANY_ID, PCI_ANY_ID)
|
||
|
},
|
||
|
{ 0 }
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(pci, mpi3mr_pci_id_table);
|
||
|
|
||
|
static struct pci_driver mpi3mr_pci_driver = {
|
||
|
.name = MPI3MR_DRIVER_NAME,
|
||
|
.id_table = mpi3mr_pci_id_table,
|
||
|
.probe = mpi3mr_probe,
|
||
|
.remove = mpi3mr_remove,
|
||
|
.shutdown = mpi3mr_shutdown,
|
||
|
};
|
||
|
|
||
|
static int __init mpi3mr_init(void)
|
||
|
{
|
||
|
int ret_val;
|
||
|
|
||
|
pr_info("Loading %s version %s\n", MPI3MR_DRIVER_NAME,
|
||
|
MPI3MR_DRIVER_VERSION);
|
||
|
|
||
|
ret_val = pci_register_driver(&mpi3mr_pci_driver);
|
||
|
|
||
|
return ret_val;
|
||
|
}
|
||
|
|
||
|
static void __exit mpi3mr_exit(void)
|
||
|
{
|
||
|
if (warn_non_secure_ctlr)
|
||
|
pr_warn(
|
||
|
"Unloading %s version %s while managing a non secure controller\n",
|
||
|
MPI3MR_DRIVER_NAME, MPI3MR_DRIVER_VERSION);
|
||
|
else
|
||
|
pr_info("Unloading %s version %s\n", MPI3MR_DRIVER_NAME,
|
||
|
MPI3MR_DRIVER_VERSION);
|
||
|
|
||
|
pci_unregister_driver(&mpi3mr_pci_driver);
|
||
|
}
|
||
|
|
||
|
module_init(mpi3mr_init);
|
||
|
module_exit(mpi3mr_exit);
|