162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright 2014 Cisco Systems, Inc. All rights reserved. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/module.h> 562306a36Sopenharmony_ci#include <linux/mempool.h> 662306a36Sopenharmony_ci#include <linux/string.h> 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/errno.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/skbuff.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci#include <linux/workqueue.h> 1562306a36Sopenharmony_ci#include <scsi/scsi_host.h> 1662306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "snic.h" 1962306a36Sopenharmony_ci#include "snic_fwint.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PCI_DEVICE_ID_CISCO_SNIC 0x0046 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Supported devices by snic module */ 2462306a36Sopenharmony_cistatic struct pci_device_id snic_id_table[] = { 2562306a36Sopenharmony_ci {PCI_DEVICE(0x1137, PCI_DEVICE_ID_CISCO_SNIC) }, 2662306a36Sopenharmony_ci { 0, } /* end of table */ 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciunsigned int snic_log_level = 0x0; 3062306a36Sopenharmony_cimodule_param(snic_log_level, int, S_IRUGO|S_IWUSR); 3162306a36Sopenharmony_ciMODULE_PARM_DESC(snic_log_level, "bitmask for snic logging levels"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#ifdef CONFIG_SCSI_SNIC_DEBUG_FS 3462306a36Sopenharmony_ciunsigned int snic_trace_max_pages = 16; 3562306a36Sopenharmony_cimodule_param(snic_trace_max_pages, uint, S_IRUGO|S_IWUSR); 3662306a36Sopenharmony_ciMODULE_PARM_DESC(snic_trace_max_pages, 3762306a36Sopenharmony_ci "Total allocated memory pages for snic trace buffer"); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ciunsigned int snic_max_qdepth = SNIC_DFLT_QUEUE_DEPTH; 4162306a36Sopenharmony_cimodule_param(snic_max_qdepth, uint, S_IRUGO | S_IWUSR); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(snic_max_qdepth, "Queue depth to report for each LUN"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * snic_slave_alloc : callback function to SCSI Mid Layer, called on 4662306a36Sopenharmony_ci * scsi device initialization. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic int 4962306a36Sopenharmony_cisnic_slave_alloc(struct scsi_device *sdev) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct snic_tgt *tgt = starget_to_tgt(scsi_target(sdev)); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (!tgt || snic_tgt_chkready(tgt)) 5462306a36Sopenharmony_ci return -ENXIO; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * snic_slave_configure : callback function to SCSI Mid Layer, called on 6162306a36Sopenharmony_ci * scsi device initialization. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistatic int 6462306a36Sopenharmony_cisnic_slave_configure(struct scsi_device *sdev) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct snic *snic = shost_priv(sdev->host); 6762306a36Sopenharmony_ci u32 qdepth = 0, max_ios = 0; 6862306a36Sopenharmony_ci int tmo = SNIC_DFLT_CMD_TIMEOUT * HZ; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Set Queue Depth */ 7162306a36Sopenharmony_ci max_ios = snic_max_qdepth; 7262306a36Sopenharmony_ci qdepth = min_t(u32, max_ios, SNIC_MAX_QUEUE_DEPTH); 7362306a36Sopenharmony_ci scsi_change_queue_depth(sdev, qdepth); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (snic->fwinfo.io_tmo > 1) 7662306a36Sopenharmony_ci tmo = snic->fwinfo.io_tmo * HZ; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* FW requires extended timeouts */ 7962306a36Sopenharmony_ci blk_queue_rq_timeout(sdev->request_queue, tmo); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int 8562306a36Sopenharmony_cisnic_change_queue_depth(struct scsi_device *sdev, int qdepth) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct snic *snic = shost_priv(sdev->host); 8862306a36Sopenharmony_ci int qsz = 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci qsz = min_t(u32, qdepth, SNIC_MAX_QUEUE_DEPTH); 9162306a36Sopenharmony_ci if (qsz < sdev->queue_depth) 9262306a36Sopenharmony_ci atomic64_inc(&snic->s_stats.misc.qsz_rampdown); 9362306a36Sopenharmony_ci else if (qsz > sdev->queue_depth) 9462306a36Sopenharmony_ci atomic64_inc(&snic->s_stats.misc.qsz_rampup); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci atomic64_set(&snic->s_stats.misc.last_qsz, sdev->queue_depth); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci scsi_change_queue_depth(sdev, qsz); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return sdev->queue_depth; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic const struct scsi_host_template snic_host_template = { 10462306a36Sopenharmony_ci .module = THIS_MODULE, 10562306a36Sopenharmony_ci .name = SNIC_DRV_NAME, 10662306a36Sopenharmony_ci .queuecommand = snic_queuecommand, 10762306a36Sopenharmony_ci .eh_abort_handler = snic_abort_cmd, 10862306a36Sopenharmony_ci .eh_device_reset_handler = snic_device_reset, 10962306a36Sopenharmony_ci .eh_host_reset_handler = snic_host_reset, 11062306a36Sopenharmony_ci .slave_alloc = snic_slave_alloc, 11162306a36Sopenharmony_ci .slave_configure = snic_slave_configure, 11262306a36Sopenharmony_ci .change_queue_depth = snic_change_queue_depth, 11362306a36Sopenharmony_ci .this_id = -1, 11462306a36Sopenharmony_ci .cmd_per_lun = SNIC_DFLT_QUEUE_DEPTH, 11562306a36Sopenharmony_ci .can_queue = SNIC_MAX_IO_REQ, 11662306a36Sopenharmony_ci .sg_tablesize = SNIC_MAX_SG_DESC_CNT, 11762306a36Sopenharmony_ci .max_sectors = 0x800, 11862306a36Sopenharmony_ci .shost_groups = snic_host_groups, 11962306a36Sopenharmony_ci .track_queue_depth = 1, 12062306a36Sopenharmony_ci .cmd_size = sizeof(struct snic_internal_io_state), 12162306a36Sopenharmony_ci .proc_name = "snic_scsi", 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * snic_handle_link_event : Handles link events such as link up/down/error 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_civoid 12862306a36Sopenharmony_cisnic_handle_link_event(struct snic *snic) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci unsigned long flags; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci spin_lock_irqsave(&snic->snic_lock, flags); 13362306a36Sopenharmony_ci if (snic->stop_link_events) { 13462306a36Sopenharmony_ci spin_unlock_irqrestore(&snic->snic_lock, flags); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci spin_unlock_irqrestore(&snic->snic_lock, flags); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci queue_work(snic_glob->event_q, &snic->link_work); 14162306a36Sopenharmony_ci} /* end of snic_handle_link_event */ 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* 14462306a36Sopenharmony_ci * snic_notify_set : sets notification area 14562306a36Sopenharmony_ci * This notification area is to receive events from fw 14662306a36Sopenharmony_ci * Note: snic supports only MSIX interrupts, in which we can just call 14762306a36Sopenharmony_ci * svnic_dev_notify_set directly 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistatic int 15062306a36Sopenharmony_cisnic_notify_set(struct snic *snic) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci int ret = 0; 15362306a36Sopenharmony_ci enum vnic_dev_intr_mode intr_mode; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci intr_mode = svnic_dev_get_intr_mode(snic->vdev); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (intr_mode == VNIC_DEV_INTR_MODE_MSIX) { 15862306a36Sopenharmony_ci ret = svnic_dev_notify_set(snic->vdev, SNIC_MSIX_ERR_NOTIFY); 15962306a36Sopenharmony_ci } else { 16062306a36Sopenharmony_ci SNIC_HOST_ERR(snic->shost, 16162306a36Sopenharmony_ci "Interrupt mode should be setup before devcmd notify set %d\n", 16262306a36Sopenharmony_ci intr_mode); 16362306a36Sopenharmony_ci ret = -1; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci} /* end of snic_notify_set */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * snic_dev_wait : polls vnic open status. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cistatic int 17362306a36Sopenharmony_cisnic_dev_wait(struct vnic_dev *vdev, 17462306a36Sopenharmony_ci int (*start)(struct vnic_dev *, int), 17562306a36Sopenharmony_ci int (*finished)(struct vnic_dev *, int *), 17662306a36Sopenharmony_ci int arg) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci unsigned long time; 17962306a36Sopenharmony_ci int ret, done; 18062306a36Sopenharmony_ci int retry_cnt = 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ret = start(vdev, arg); 18362306a36Sopenharmony_ci if (ret) 18462306a36Sopenharmony_ci return ret; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* 18762306a36Sopenharmony_ci * Wait for func to complete...2 seconds max. 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * Sometimes schedule_timeout_uninterruptible take long time 19062306a36Sopenharmony_ci * to wakeup, which results skipping retry. The retry counter 19162306a36Sopenharmony_ci * ensures to retry at least two times. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci time = jiffies + (HZ * 2); 19462306a36Sopenharmony_ci do { 19562306a36Sopenharmony_ci ret = finished(vdev, &done); 19662306a36Sopenharmony_ci if (ret) 19762306a36Sopenharmony_ci return ret; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (done) 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci schedule_timeout_uninterruptible(HZ/10); 20262306a36Sopenharmony_ci ++retry_cnt; 20362306a36Sopenharmony_ci } while (time_after(time, jiffies) || (retry_cnt < 3)); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return -ETIMEDOUT; 20662306a36Sopenharmony_ci} /* end of snic_dev_wait */ 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* 20962306a36Sopenharmony_ci * snic_cleanup: called by snic_remove 21062306a36Sopenharmony_ci * Stops the snic device, masks all interrupts, Completed CQ entries are 21162306a36Sopenharmony_ci * drained. Posted WQ/RQ/Copy-WQ entries are cleanup 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic int 21462306a36Sopenharmony_cisnic_cleanup(struct snic *snic) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci unsigned int i; 21762306a36Sopenharmony_ci int ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci svnic_dev_disable(snic->vdev); 22062306a36Sopenharmony_ci for (i = 0; i < snic->intr_count; i++) 22162306a36Sopenharmony_ci svnic_intr_mask(&snic->intr[i]); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci for (i = 0; i < snic->wq_count; i++) { 22462306a36Sopenharmony_ci ret = svnic_wq_disable(&snic->wq[i]); 22562306a36Sopenharmony_ci if (ret) 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Clean up completed IOs */ 23062306a36Sopenharmony_ci snic_fwcq_cmpl_handler(snic, -1); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci snic_wq_cmpl_handler(snic, -1); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Clean up the IOs that have not completed */ 23562306a36Sopenharmony_ci for (i = 0; i < snic->wq_count; i++) 23662306a36Sopenharmony_ci svnic_wq_clean(&snic->wq[i], snic_free_wq_buf); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci for (i = 0; i < snic->cq_count; i++) 23962306a36Sopenharmony_ci svnic_cq_clean(&snic->cq[i]); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci for (i = 0; i < snic->intr_count; i++) 24262306a36Sopenharmony_ci svnic_intr_clean(&snic->intr[i]); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Cleanup snic specific requests */ 24562306a36Sopenharmony_ci snic_free_all_untagged_reqs(snic); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Cleanup Pending SCSI commands */ 24862306a36Sopenharmony_ci snic_shutdown_scsi_cleanup(snic); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci for (i = 0; i < SNIC_REQ_MAX_CACHES; i++) 25162306a36Sopenharmony_ci mempool_destroy(snic->req_pool[i]); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci} /* end of snic_cleanup */ 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void 25862306a36Sopenharmony_cisnic_iounmap(struct snic *snic) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci if (snic->bar0.vaddr) 26162306a36Sopenharmony_ci iounmap(snic->bar0.vaddr); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* 26562306a36Sopenharmony_ci * snic_vdev_open_done : polls for svnic_dev_open cmd completion. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_cistatic int 26862306a36Sopenharmony_cisnic_vdev_open_done(struct vnic_dev *vdev, int *done) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct snic *snic = svnic_dev_priv(vdev); 27162306a36Sopenharmony_ci int ret; 27262306a36Sopenharmony_ci int nretries = 5; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci do { 27562306a36Sopenharmony_ci ret = svnic_dev_open_done(vdev, done); 27662306a36Sopenharmony_ci if (ret == 0) 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci SNIC_HOST_INFO(snic->shost, "VNIC_DEV_OPEN Timedout.\n"); 28062306a36Sopenharmony_ci } while (nretries--); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci} /* end of snic_vdev_open_done */ 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* 28662306a36Sopenharmony_ci * snic_add_host : registers scsi host with ML 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_cistatic int 28962306a36Sopenharmony_cisnic_add_host(struct Scsi_Host *shost, struct pci_dev *pdev) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci int ret = 0; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ret = scsi_add_host(shost, &pdev->dev); 29462306a36Sopenharmony_ci if (ret) { 29562306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 29662306a36Sopenharmony_ci "snic: scsi_add_host failed. %d\n", 29762306a36Sopenharmony_ci ret); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci SNIC_BUG_ON(shost->work_q != NULL); 30362306a36Sopenharmony_ci snprintf(shost->work_q_name, sizeof(shost->work_q_name), "scsi_wq_%d", 30462306a36Sopenharmony_ci shost->host_no); 30562306a36Sopenharmony_ci shost->work_q = create_singlethread_workqueue(shost->work_q_name); 30662306a36Sopenharmony_ci if (!shost->work_q) { 30762306a36Sopenharmony_ci SNIC_HOST_ERR(shost, "Failed to Create ScsiHost wq.\n"); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ret = -ENOMEM; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci} /* end of snic_add_host */ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic void 31662306a36Sopenharmony_cisnic_del_host(struct Scsi_Host *shost) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci if (!shost->work_q) 31962306a36Sopenharmony_ci return; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci destroy_workqueue(shost->work_q); 32262306a36Sopenharmony_ci shost->work_q = NULL; 32362306a36Sopenharmony_ci scsi_remove_host(shost); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ciint 32762306a36Sopenharmony_cisnic_get_state(struct snic *snic) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci return atomic_read(&snic->state); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_civoid 33362306a36Sopenharmony_cisnic_set_state(struct snic *snic, enum snic_state state) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci SNIC_HOST_INFO(snic->shost, "snic state change from %s to %s\n", 33662306a36Sopenharmony_ci snic_state_to_str(snic_get_state(snic)), 33762306a36Sopenharmony_ci snic_state_to_str(state)); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci atomic_set(&snic->state, state); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/* 34362306a36Sopenharmony_ci * snic_probe : Initialize the snic interface. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_cistatic int 34662306a36Sopenharmony_cisnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct Scsi_Host *shost; 34962306a36Sopenharmony_ci struct snic *snic; 35062306a36Sopenharmony_ci mempool_t *pool; 35162306a36Sopenharmony_ci unsigned long flags; 35262306a36Sopenharmony_ci u32 max_ios = 0; 35362306a36Sopenharmony_ci int ret, i; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* Device Information */ 35662306a36Sopenharmony_ci SNIC_INFO("snic device %4x:%4x:%4x:%4x: ", 35762306a36Sopenharmony_ci pdev->vendor, pdev->device, pdev->subsystem_vendor, 35862306a36Sopenharmony_ci pdev->subsystem_device); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci SNIC_INFO("snic device bus %x: slot %x: fn %x\n", 36162306a36Sopenharmony_ci pdev->bus->number, PCI_SLOT(pdev->devfn), 36262306a36Sopenharmony_ci PCI_FUNC(pdev->devfn)); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* 36562306a36Sopenharmony_ci * Allocate SCSI Host and setup association between host, and snic 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci shost = scsi_host_alloc(&snic_host_template, sizeof(struct snic)); 36862306a36Sopenharmony_ci if (!shost) { 36962306a36Sopenharmony_ci SNIC_ERR("Unable to alloc scsi_host\n"); 37062306a36Sopenharmony_ci ret = -ENOMEM; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci goto prob_end; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci snic = shost_priv(shost); 37562306a36Sopenharmony_ci snic->shost = shost; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci snprintf(snic->name, sizeof(snic->name) - 1, "%s%d", SNIC_DRV_NAME, 37862306a36Sopenharmony_ci shost->host_no); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci SNIC_HOST_INFO(shost, 38162306a36Sopenharmony_ci "snic%d = %p shost = %p device bus %x: slot %x: fn %x\n", 38262306a36Sopenharmony_ci shost->host_no, snic, shost, pdev->bus->number, 38362306a36Sopenharmony_ci PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); 38462306a36Sopenharmony_ci#ifdef CONFIG_SCSI_SNIC_DEBUG_FS 38562306a36Sopenharmony_ci /* Per snic debugfs init */ 38662306a36Sopenharmony_ci snic_stats_debugfs_init(snic); 38762306a36Sopenharmony_ci#endif 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Setup PCI Resources */ 39062306a36Sopenharmony_ci pci_set_drvdata(pdev, snic); 39162306a36Sopenharmony_ci snic->pdev = pdev; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci ret = pci_enable_device(pdev); 39462306a36Sopenharmony_ci if (ret) { 39562306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 39662306a36Sopenharmony_ci "Cannot enable PCI Resources, aborting : %d\n", 39762306a36Sopenharmony_ci ret); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci goto err_free_snic; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci ret = pci_request_regions(pdev, SNIC_DRV_NAME); 40362306a36Sopenharmony_ci if (ret) { 40462306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 40562306a36Sopenharmony_ci "Cannot obtain PCI Resources, aborting : %d\n", 40662306a36Sopenharmony_ci ret); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci goto err_pci_disable; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci pci_set_master(pdev); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * Query PCI Controller on system for DMA addressing 41562306a36Sopenharmony_ci * limitation for the device. Try 43-bit first, and 41662306a36Sopenharmony_ci * fail to 32-bit. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(43)); 41962306a36Sopenharmony_ci if (ret) { 42062306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 42162306a36Sopenharmony_ci if (ret) { 42262306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 42362306a36Sopenharmony_ci "No Usable DMA Configuration, aborting %d\n", 42462306a36Sopenharmony_ci ret); 42562306a36Sopenharmony_ci goto err_rel_regions; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Map vNIC resources from BAR0 */ 43062306a36Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { 43162306a36Sopenharmony_ci SNIC_HOST_ERR(shost, "BAR0 not memory mappable aborting.\n"); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci ret = -ENODEV; 43462306a36Sopenharmony_ci goto err_rel_regions; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci snic->bar0.vaddr = pci_iomap(pdev, 0, 0); 43862306a36Sopenharmony_ci if (!snic->bar0.vaddr) { 43962306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 44062306a36Sopenharmony_ci "Cannot memory map BAR0 res hdr aborting.\n"); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ret = -ENODEV; 44362306a36Sopenharmony_ci goto err_rel_regions; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci snic->bar0.bus_addr = pci_resource_start(pdev, 0); 44762306a36Sopenharmony_ci snic->bar0.len = pci_resource_len(pdev, 0); 44862306a36Sopenharmony_ci SNIC_BUG_ON(snic->bar0.bus_addr == 0); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Devcmd2 Resource Allocation and Initialization */ 45162306a36Sopenharmony_ci snic->vdev = svnic_dev_alloc_discover(NULL, snic, pdev, &snic->bar0, 1); 45262306a36Sopenharmony_ci if (!snic->vdev) { 45362306a36Sopenharmony_ci SNIC_HOST_ERR(shost, "vNIC Resource Discovery Failed.\n"); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ret = -ENODEV; 45662306a36Sopenharmony_ci goto err_iounmap; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ret = svnic_dev_cmd_init(snic->vdev, 0); 46062306a36Sopenharmony_ci if (ret) { 46162306a36Sopenharmony_ci SNIC_HOST_INFO(shost, "Devcmd2 Init Failed. err = %d\n", ret); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci goto err_vnic_unreg; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci ret = snic_dev_wait(snic->vdev, svnic_dev_open, snic_vdev_open_done, 0); 46762306a36Sopenharmony_ci if (ret) { 46862306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 46962306a36Sopenharmony_ci "vNIC dev open failed, aborting. %d\n", 47062306a36Sopenharmony_ci ret); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci goto err_vnic_unreg; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = svnic_dev_init(snic->vdev, 0); 47662306a36Sopenharmony_ci if (ret) { 47762306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 47862306a36Sopenharmony_ci "vNIC dev init failed. aborting. %d\n", 47962306a36Sopenharmony_ci ret); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci goto err_dev_close; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* Get vNIC information */ 48562306a36Sopenharmony_ci ret = snic_get_vnic_config(snic); 48662306a36Sopenharmony_ci if (ret) { 48762306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 48862306a36Sopenharmony_ci "Get vNIC configuration failed, aborting. %d\n", 48962306a36Sopenharmony_ci ret); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci goto err_dev_close; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Configure Maximum Outstanding IO reqs */ 49562306a36Sopenharmony_ci max_ios = snic->config.io_throttle_count; 49662306a36Sopenharmony_ci if (max_ios != SNIC_UCSM_DFLT_THROTTLE_CNT_BLD) 49762306a36Sopenharmony_ci shost->can_queue = min_t(u32, SNIC_MAX_IO_REQ, 49862306a36Sopenharmony_ci max_t(u32, SNIC_MIN_IO_REQ, max_ios)); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci snic->max_tag_id = shost->can_queue; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci shost->max_lun = snic->config.luns_per_tgt; 50362306a36Sopenharmony_ci shost->max_id = SNIC_MAX_TARGET; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci shost->max_cmd_len = MAX_COMMAND_SIZE; /*defined in scsi_cmnd.h*/ 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci snic_get_res_counts(snic); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * Assumption: Only MSIx is supported 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci ret = snic_set_intr_mode(snic); 51362306a36Sopenharmony_ci if (ret) { 51462306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 51562306a36Sopenharmony_ci "Failed to set intr mode aborting. %d\n", 51662306a36Sopenharmony_ci ret); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci goto err_dev_close; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci ret = snic_alloc_vnic_res(snic); 52262306a36Sopenharmony_ci if (ret) { 52362306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 52462306a36Sopenharmony_ci "Failed to alloc vNIC resources aborting. %d\n", 52562306a36Sopenharmony_ci ret); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci goto err_clear_intr; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Initialize specific lists */ 53162306a36Sopenharmony_ci INIT_LIST_HEAD(&snic->list); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * spl_cmd_list for maintaining snic specific cmds 53562306a36Sopenharmony_ci * such as EXCH_VER_REQ, REPORT_TARGETS etc 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci INIT_LIST_HEAD(&snic->spl_cmd_list); 53862306a36Sopenharmony_ci spin_lock_init(&snic->spl_cmd_lock); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* initialize all snic locks */ 54162306a36Sopenharmony_ci spin_lock_init(&snic->snic_lock); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci for (i = 0; i < SNIC_WQ_MAX; i++) 54462306a36Sopenharmony_ci spin_lock_init(&snic->wq_lock[i]); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci for (i = 0; i < SNIC_IO_LOCKS; i++) 54762306a36Sopenharmony_ci spin_lock_init(&snic->io_req_lock[i]); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci pool = mempool_create_slab_pool(2, 55062306a36Sopenharmony_ci snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]); 55162306a36Sopenharmony_ci if (!pool) { 55262306a36Sopenharmony_ci SNIC_HOST_ERR(shost, "dflt sgl pool creation failed\n"); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ret = -ENOMEM; 55562306a36Sopenharmony_ci goto err_free_res; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci snic->req_pool[SNIC_REQ_CACHE_DFLT_SGL] = pool; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci pool = mempool_create_slab_pool(2, 56162306a36Sopenharmony_ci snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]); 56262306a36Sopenharmony_ci if (!pool) { 56362306a36Sopenharmony_ci SNIC_HOST_ERR(shost, "max sgl pool creation failed\n"); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci ret = -ENOMEM; 56662306a36Sopenharmony_ci goto err_free_dflt_sgl_pool; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci snic->req_pool[SNIC_REQ_CACHE_MAX_SGL] = pool; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci pool = mempool_create_slab_pool(2, 57262306a36Sopenharmony_ci snic_glob->req_cache[SNIC_REQ_TM_CACHE]); 57362306a36Sopenharmony_ci if (!pool) { 57462306a36Sopenharmony_ci SNIC_HOST_ERR(shost, "snic tmreq info pool creation failed.\n"); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ret = -ENOMEM; 57762306a36Sopenharmony_ci goto err_free_max_sgl_pool; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci snic->req_pool[SNIC_REQ_TM_CACHE] = pool; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Initialize snic state */ 58362306a36Sopenharmony_ci atomic_set(&snic->state, SNIC_INIT); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci atomic_set(&snic->ios_inflight, 0); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* Setup notification buffer area */ 58862306a36Sopenharmony_ci ret = snic_notify_set(snic); 58962306a36Sopenharmony_ci if (ret) { 59062306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 59162306a36Sopenharmony_ci "Failed to alloc notify buffer aborting. %d\n", 59262306a36Sopenharmony_ci ret); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci goto err_free_tmreq_pool; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci spin_lock_irqsave(&snic_glob->snic_list_lock, flags); 59862306a36Sopenharmony_ci list_add_tail(&snic->list, &snic_glob->snic_list); 59962306a36Sopenharmony_ci spin_unlock_irqrestore(&snic_glob->snic_list_lock, flags); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci snic_disc_init(&snic->disc); 60262306a36Sopenharmony_ci INIT_WORK(&snic->tgt_work, snic_handle_tgt_disc); 60362306a36Sopenharmony_ci INIT_WORK(&snic->disc_work, snic_handle_disc); 60462306a36Sopenharmony_ci INIT_WORK(&snic->link_work, snic_handle_link); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* Enable all queues */ 60762306a36Sopenharmony_ci for (i = 0; i < snic->wq_count; i++) 60862306a36Sopenharmony_ci svnic_wq_enable(&snic->wq[i]); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci ret = svnic_dev_enable_wait(snic->vdev); 61162306a36Sopenharmony_ci if (ret) { 61262306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 61362306a36Sopenharmony_ci "vNIC dev enable failed w/ error %d\n", 61462306a36Sopenharmony_ci ret); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci goto err_vdev_enable; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci ret = snic_request_intr(snic); 62062306a36Sopenharmony_ci if (ret) { 62162306a36Sopenharmony_ci SNIC_HOST_ERR(shost, "Unable to request irq. %d\n", ret); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci goto err_req_intr; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci for (i = 0; i < snic->intr_count; i++) 62762306a36Sopenharmony_ci svnic_intr_unmask(&snic->intr[i]); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* Get snic params */ 63062306a36Sopenharmony_ci ret = snic_get_conf(snic); 63162306a36Sopenharmony_ci if (ret) { 63262306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 63362306a36Sopenharmony_ci "Failed to get snic io config from FW w err %d\n", 63462306a36Sopenharmony_ci ret); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci goto err_get_conf; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* 64062306a36Sopenharmony_ci * Initialization done with PCI system, hardware, firmware. 64162306a36Sopenharmony_ci * Add shost to SCSI 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_ci ret = snic_add_host(shost, pdev); 64462306a36Sopenharmony_ci if (ret) { 64562306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 64662306a36Sopenharmony_ci "Adding scsi host Failed ... exiting. %d\n", 64762306a36Sopenharmony_ci ret); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci goto err_get_conf; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci snic_set_state(snic, SNIC_ONLINE); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci ret = snic_disc_start(snic); 65562306a36Sopenharmony_ci if (ret) { 65662306a36Sopenharmony_ci SNIC_HOST_ERR(shost, "snic_probe:Discovery Failed w err = %d\n", 65762306a36Sopenharmony_ci ret); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci goto err_get_conf; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci SNIC_HOST_INFO(shost, "SNIC Device Probe Successful.\n"); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cierr_get_conf: 66762306a36Sopenharmony_ci snic_free_all_untagged_reqs(snic); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci for (i = 0; i < snic->intr_count; i++) 67062306a36Sopenharmony_ci svnic_intr_mask(&snic->intr[i]); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci snic_free_intr(snic); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cierr_req_intr: 67562306a36Sopenharmony_ci svnic_dev_disable(snic->vdev); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cierr_vdev_enable: 67862306a36Sopenharmony_ci svnic_dev_notify_unset(snic->vdev); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci for (i = 0; i < snic->wq_count; i++) { 68162306a36Sopenharmony_ci int rc = 0; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci rc = svnic_wq_disable(&snic->wq[i]); 68462306a36Sopenharmony_ci if (rc) { 68562306a36Sopenharmony_ci SNIC_HOST_ERR(shost, 68662306a36Sopenharmony_ci "WQ Disable Failed w/ err = %d\n", rc); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci snic_del_host(snic->shost); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cierr_free_tmreq_pool: 69462306a36Sopenharmony_ci mempool_destroy(snic->req_pool[SNIC_REQ_TM_CACHE]); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cierr_free_max_sgl_pool: 69762306a36Sopenharmony_ci mempool_destroy(snic->req_pool[SNIC_REQ_CACHE_MAX_SGL]); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cierr_free_dflt_sgl_pool: 70062306a36Sopenharmony_ci mempool_destroy(snic->req_pool[SNIC_REQ_CACHE_DFLT_SGL]); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cierr_free_res: 70362306a36Sopenharmony_ci snic_free_vnic_res(snic); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cierr_clear_intr: 70662306a36Sopenharmony_ci snic_clear_intr_mode(snic); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cierr_dev_close: 70962306a36Sopenharmony_ci svnic_dev_close(snic->vdev); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cierr_vnic_unreg: 71262306a36Sopenharmony_ci svnic_dev_unregister(snic->vdev); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cierr_iounmap: 71562306a36Sopenharmony_ci snic_iounmap(snic); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cierr_rel_regions: 71862306a36Sopenharmony_ci pci_release_regions(pdev); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cierr_pci_disable: 72162306a36Sopenharmony_ci pci_disable_device(pdev); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cierr_free_snic: 72462306a36Sopenharmony_ci#ifdef CONFIG_SCSI_SNIC_DEBUG_FS 72562306a36Sopenharmony_ci snic_stats_debugfs_remove(snic); 72662306a36Sopenharmony_ci#endif 72762306a36Sopenharmony_ci scsi_host_put(shost); 72862306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ciprob_end: 73162306a36Sopenharmony_ci SNIC_INFO("sNIC device : bus %d: slot %d: fn %d Registration Failed.\n", 73262306a36Sopenharmony_ci pdev->bus->number, PCI_SLOT(pdev->devfn), 73362306a36Sopenharmony_ci PCI_FUNC(pdev->devfn)); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return ret; 73662306a36Sopenharmony_ci} /* end of snic_probe */ 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci/* 74062306a36Sopenharmony_ci * snic_remove : invoked on unbinding the interface to cleanup the 74162306a36Sopenharmony_ci * resources allocated in snic_probe on initialization. 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_cistatic void 74462306a36Sopenharmony_cisnic_remove(struct pci_dev *pdev) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct snic *snic = pci_get_drvdata(pdev); 74762306a36Sopenharmony_ci unsigned long flags; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (!snic) { 75062306a36Sopenharmony_ci SNIC_INFO("sNIC dev: bus %d slot %d fn %d snic inst is null.\n", 75162306a36Sopenharmony_ci pdev->bus->number, PCI_SLOT(pdev->devfn), 75262306a36Sopenharmony_ci PCI_FUNC(pdev->devfn)); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* 75862306a36Sopenharmony_ci * Mark state so that the workqueue thread stops forwarding 75962306a36Sopenharmony_ci * received frames and link events. ISR and other threads 76062306a36Sopenharmony_ci * that can queue work items will also stop creating work 76162306a36Sopenharmony_ci * items on the snic workqueue 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_ci snic_set_state(snic, SNIC_OFFLINE); 76462306a36Sopenharmony_ci spin_lock_irqsave(&snic->snic_lock, flags); 76562306a36Sopenharmony_ci snic->stop_link_events = 1; 76662306a36Sopenharmony_ci spin_unlock_irqrestore(&snic->snic_lock, flags); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci flush_workqueue(snic_glob->event_q); 76962306a36Sopenharmony_ci snic_disc_term(snic); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci spin_lock_irqsave(&snic->snic_lock, flags); 77262306a36Sopenharmony_ci snic->in_remove = 1; 77362306a36Sopenharmony_ci spin_unlock_irqrestore(&snic->snic_lock, flags); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* 77662306a36Sopenharmony_ci * This stops the snic device, masks all interrupts, Completed 77762306a36Sopenharmony_ci * CQ entries are drained. Posted WQ/RQ/Copy-WQ entries are 77862306a36Sopenharmony_ci * cleanup 77962306a36Sopenharmony_ci */ 78062306a36Sopenharmony_ci snic_cleanup(snic); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci spin_lock_irqsave(&snic_glob->snic_list_lock, flags); 78362306a36Sopenharmony_ci list_del(&snic->list); 78462306a36Sopenharmony_ci spin_unlock_irqrestore(&snic_glob->snic_list_lock, flags); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci snic_tgt_del_all(snic); 78762306a36Sopenharmony_ci#ifdef CONFIG_SCSI_SNIC_DEBUG_FS 78862306a36Sopenharmony_ci snic_stats_debugfs_remove(snic); 78962306a36Sopenharmony_ci#endif 79062306a36Sopenharmony_ci snic_del_host(snic->shost); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci svnic_dev_notify_unset(snic->vdev); 79362306a36Sopenharmony_ci snic_free_intr(snic); 79462306a36Sopenharmony_ci snic_free_vnic_res(snic); 79562306a36Sopenharmony_ci snic_clear_intr_mode(snic); 79662306a36Sopenharmony_ci svnic_dev_close(snic->vdev); 79762306a36Sopenharmony_ci svnic_dev_unregister(snic->vdev); 79862306a36Sopenharmony_ci snic_iounmap(snic); 79962306a36Sopenharmony_ci pci_release_regions(pdev); 80062306a36Sopenharmony_ci pci_disable_device(pdev); 80162306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* this frees Scsi_Host and snic memory (continuous chunk) */ 80462306a36Sopenharmony_ci scsi_host_put(snic->shost); 80562306a36Sopenharmony_ci} /* end of snic_remove */ 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistruct snic_global *snic_glob; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci/* 81162306a36Sopenharmony_ci * snic_global_data_init: Initialize SNIC Global Data 81262306a36Sopenharmony_ci * Notes: All the global lists, variables should be part of global data 81362306a36Sopenharmony_ci * this helps in debugging. 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_cistatic int 81662306a36Sopenharmony_cisnic_global_data_init(void) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci int ret = 0; 81962306a36Sopenharmony_ci struct kmem_cache *cachep; 82062306a36Sopenharmony_ci ssize_t len = 0; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci snic_glob = kzalloc(sizeof(*snic_glob), GFP_KERNEL); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (!snic_glob) { 82562306a36Sopenharmony_ci SNIC_ERR("Failed to allocate Global Context.\n"); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = -ENOMEM; 82862306a36Sopenharmony_ci goto gdi_end; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci#ifdef CONFIG_SCSI_SNIC_DEBUG_FS 83262306a36Sopenharmony_ci /* Debugfs related Initialization */ 83362306a36Sopenharmony_ci /* Create debugfs entries for snic */ 83462306a36Sopenharmony_ci snic_debugfs_init(); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* Trace related Initialization */ 83762306a36Sopenharmony_ci /* Allocate memory for trace buffer */ 83862306a36Sopenharmony_ci ret = snic_trc_init(); 83962306a36Sopenharmony_ci if (ret < 0) { 84062306a36Sopenharmony_ci SNIC_ERR("Trace buffer init failed, SNIC tracing disabled\n"); 84162306a36Sopenharmony_ci snic_trc_free(); 84262306a36Sopenharmony_ci /* continue even if it fails */ 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci#endif 84662306a36Sopenharmony_ci INIT_LIST_HEAD(&snic_glob->snic_list); 84762306a36Sopenharmony_ci spin_lock_init(&snic_glob->snic_list_lock); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* Create a cache for allocation of snic_host_req+default size ESGLs */ 85062306a36Sopenharmony_ci len = sizeof(struct snic_req_info); 85162306a36Sopenharmony_ci len += sizeof(struct snic_host_req) + sizeof(struct snic_dflt_sgl); 85262306a36Sopenharmony_ci cachep = kmem_cache_create("snic_req_dfltsgl", len, SNIC_SG_DESC_ALIGN, 85362306a36Sopenharmony_ci SLAB_HWCACHE_ALIGN, NULL); 85462306a36Sopenharmony_ci if (!cachep) { 85562306a36Sopenharmony_ci SNIC_ERR("Failed to create snic default sgl slab\n"); 85662306a36Sopenharmony_ci ret = -ENOMEM; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci goto err_dflt_req_slab; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL] = cachep; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* Create a cache for allocation of max size Extended SGLs */ 86362306a36Sopenharmony_ci len = sizeof(struct snic_req_info); 86462306a36Sopenharmony_ci len += sizeof(struct snic_host_req) + sizeof(struct snic_max_sgl); 86562306a36Sopenharmony_ci cachep = kmem_cache_create("snic_req_maxsgl", len, SNIC_SG_DESC_ALIGN, 86662306a36Sopenharmony_ci SLAB_HWCACHE_ALIGN, NULL); 86762306a36Sopenharmony_ci if (!cachep) { 86862306a36Sopenharmony_ci SNIC_ERR("Failed to create snic max sgl slab\n"); 86962306a36Sopenharmony_ci ret = -ENOMEM; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci goto err_max_req_slab; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL] = cachep; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci len = sizeof(struct snic_host_req); 87662306a36Sopenharmony_ci cachep = kmem_cache_create("snic_req_maxsgl", len, SNIC_SG_DESC_ALIGN, 87762306a36Sopenharmony_ci SLAB_HWCACHE_ALIGN, NULL); 87862306a36Sopenharmony_ci if (!cachep) { 87962306a36Sopenharmony_ci SNIC_ERR("Failed to create snic tm req slab\n"); 88062306a36Sopenharmony_ci ret = -ENOMEM; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci goto err_tmreq_slab; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci snic_glob->req_cache[SNIC_REQ_TM_CACHE] = cachep; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* snic_event queue */ 88762306a36Sopenharmony_ci snic_glob->event_q = create_singlethread_workqueue("snic_event_wq"); 88862306a36Sopenharmony_ci if (!snic_glob->event_q) { 88962306a36Sopenharmony_ci SNIC_ERR("snic event queue create failed\n"); 89062306a36Sopenharmony_ci ret = -ENOMEM; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci goto err_eventq; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return ret; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cierr_eventq: 89862306a36Sopenharmony_ci kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_TM_CACHE]); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cierr_tmreq_slab: 90162306a36Sopenharmony_ci kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cierr_max_req_slab: 90462306a36Sopenharmony_ci kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cierr_dflt_req_slab: 90762306a36Sopenharmony_ci#ifdef CONFIG_SCSI_SNIC_DEBUG_FS 90862306a36Sopenharmony_ci snic_trc_free(); 90962306a36Sopenharmony_ci snic_debugfs_term(); 91062306a36Sopenharmony_ci#endif 91162306a36Sopenharmony_ci kfree(snic_glob); 91262306a36Sopenharmony_ci snic_glob = NULL; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cigdi_end: 91562306a36Sopenharmony_ci return ret; 91662306a36Sopenharmony_ci} /* end of snic_glob_init */ 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci/* 91962306a36Sopenharmony_ci * snic_global_data_cleanup : Frees SNIC Global Data 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_cistatic void 92262306a36Sopenharmony_cisnic_global_data_cleanup(void) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci SNIC_BUG_ON(snic_glob == NULL); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci destroy_workqueue(snic_glob->event_q); 92762306a36Sopenharmony_ci kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_TM_CACHE]); 92862306a36Sopenharmony_ci kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]); 92962306a36Sopenharmony_ci kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci#ifdef CONFIG_SCSI_SNIC_DEBUG_FS 93262306a36Sopenharmony_ci /* Freeing Trace Resources */ 93362306a36Sopenharmony_ci snic_trc_free(); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* Freeing Debugfs Resources */ 93662306a36Sopenharmony_ci snic_debugfs_term(); 93762306a36Sopenharmony_ci#endif 93862306a36Sopenharmony_ci kfree(snic_glob); 93962306a36Sopenharmony_ci snic_glob = NULL; 94062306a36Sopenharmony_ci} /* end of snic_glob_cleanup */ 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic struct pci_driver snic_driver = { 94362306a36Sopenharmony_ci .name = SNIC_DRV_NAME, 94462306a36Sopenharmony_ci .id_table = snic_id_table, 94562306a36Sopenharmony_ci .probe = snic_probe, 94662306a36Sopenharmony_ci .remove = snic_remove, 94762306a36Sopenharmony_ci}; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic int __init 95062306a36Sopenharmony_cisnic_init_module(void) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci int ret = 0; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci#ifndef __x86_64__ 95562306a36Sopenharmony_ci SNIC_INFO("SNIC Driver is supported only for x86_64 platforms!\n"); 95662306a36Sopenharmony_ci add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); 95762306a36Sopenharmony_ci#endif 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci SNIC_INFO("%s, ver %s\n", SNIC_DRV_DESCRIPTION, SNIC_DRV_VERSION); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci ret = snic_global_data_init(); 96262306a36Sopenharmony_ci if (ret) { 96362306a36Sopenharmony_ci SNIC_ERR("Failed to Initialize Global Data.\n"); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci return ret; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci ret = pci_register_driver(&snic_driver); 96962306a36Sopenharmony_ci if (ret < 0) { 97062306a36Sopenharmony_ci SNIC_ERR("PCI driver register error\n"); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci goto err_pci_reg; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return ret; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cierr_pci_reg: 97862306a36Sopenharmony_ci snic_global_data_cleanup(); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci return ret; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic void __exit 98462306a36Sopenharmony_cisnic_cleanup_module(void) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci pci_unregister_driver(&snic_driver); 98762306a36Sopenharmony_ci snic_global_data_cleanup(); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cimodule_init(snic_init_module); 99162306a36Sopenharmony_cimodule_exit(snic_cleanup_module); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 99462306a36Sopenharmony_ciMODULE_DESCRIPTION(SNIC_DRV_DESCRIPTION); 99562306a36Sopenharmony_ciMODULE_VERSION(SNIC_DRV_VERSION); 99662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snic_id_table); 99762306a36Sopenharmony_ciMODULE_AUTHOR("Narsimhulu Musini <nmusini@cisco.com>, " 99862306a36Sopenharmony_ci "Sesidhar Baddela <sebaddel@cisco.com>"); 999