// SPDX-License-Identifier: GPL-2.0-only
// Copyright 2014 Cisco Systems, Inc. All rights reserved.
#include <linux/module.h>
#include <linux/mempool.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include "snic.h"
#include "snic_fwint.h"
#define PCI_DEVICE_ID_CISCO_SNIC 0x0046
/* Supported devices by snic module */
static const struct pci_device_id snic_id_table[] = {
{PCI_DEVICE(0x1137, PCI_DEVICE_ID_CISCO_SNIC) },
{ 0, } /* end of table */
};
unsigned int snic_log_level = 0x0;
module_param(snic_log_level, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(snic_log_level, "bitmask for snic logging levels");
#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
unsigned int snic_trace_max_pages = 16;
module_param(snic_trace_max_pages, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(snic_trace_max_pages,
"Total allocated memory pages for snic trace buffer");
#endif
unsigned int snic_max_qdepth = SNIC_DFLT_QUEUE_DEPTH;
module_param(snic_max_qdepth, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(snic_max_qdepth, "Queue depth to report for each LUN");
/*
* snic_sdev_init : callback function to SCSI Mid Layer, called on
* scsi device initialization.
*/
static int
snic_sdev_init(struct scsi_device *sdev)
{
struct snic_tgt *tgt = starget_to_tgt(scsi_target(sdev));
if (!tgt || snic_tgt_chkready(tgt))
return -ENXIO;
return 0;
}
/*
* snic_sdev_configure : callback function to SCSI Mid Layer, called on
* scsi device initialization.
*/
static int
snic_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{
struct snic *snic = shost_priv(sdev->host);
u32 qdepth = 0, max_ios = 0;
int tmo = SNIC_DFLT_CMD_TIMEOUT * HZ;
/* Set Queue Depth */
max_ios = snic_max_qdepth;
qdepth = min_t(u32, max_ios, SNIC_MAX_QUEUE_DEPTH);
scsi_change_queue_depth(sdev, qdepth);
if (snic->fwinfo.io_tmo > 1)
tmo = snic->fwinfo.io_tmo * HZ;
/* FW requires extended timeouts */
blk_queue_rq_timeout(sdev->request_queue, tmo);
return 0;
}
static int
snic_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
struct snic *snic = shost_priv(sdev->host);
int qsz = 0;
qsz = min_t(u32, qdepth, SNIC_MAX_QUEUE_DEPTH);
if (qsz < sdev->queue_depth)
atomic64_inc(&snic->s_stats.misc.qsz_rampdown);
else if (qsz > sdev->queue_depth)
atomic64_inc(&snic->s_stats.misc.qsz_rampup);
atomic64_set(&snic->s_stats.misc.last_qsz, sdev->queue_depth);
scsi_change_queue_depth(sdev, qsz);
return sdev->queue_depth;
}
static const struct scsi_host_template snic_host_template = {
.module = THIS_MODULE,
.name = SNIC_DRV_NAME,
.queuecommand = snic_queuecommand,
.eh_abort_handler = snic_abort_cmd,
.eh_device_reset_handler = snic_device_reset,
.eh_host_reset_handler = snic_host_reset,
.sdev_init = snic_sdev_init,
.sdev_configure = snic_sdev_configure,
.change_queue_depth = snic_change_queue_depth,
.this_id = -1,
.cmd_per_lun = SNIC_DFLT_QUEUE_DEPTH,
.can_queue = SNIC_MAX_IO_REQ,
.sg_tablesize = SNIC_MAX_SG_DESC_CNT,
.max_sectors = 0x800,
.shost_groups = snic_host_groups,
.track_queue_depth = 1,
.cmd_size = sizeof(struct snic_internal_io_state),
.proc_name = "snic_scsi",
};
/*
* snic_handle_link_event : Handles link events such as link up/down/error
*/
void
snic_handle_link_event(struct snic *snic)
{
unsigned long flags;
spin_lock_irqsave(&snic->snic_lock, flags);
if (snic->stop_link_events) {
spin_unlock_irqrestore(&snic->snic_lock, flags);
return;
}
spin_unlock_irqrestore(&snic->snic_lock, flags);
queue_work(snic_glob->event_q, &snic->link_work);
} /* end of snic_handle_link_event */
/*
* snic_notify_set : sets notification area
* This notification area is to receive events from fw
* Note: snic supports only MSIX interrupts, in which we can just call
* svnic_dev_notify_set directly
*/
static int
snic_notify_set(struct