/*
* This file is part of the Chelsio FCoE driver for Linux .
*
* Copyright ( c ) 2008 - 2012 Chelsio Communications , Inc . All rights reserved .
*
* This software is available to you under a choice of one of two
* licenses . You may choose to be licensed under the terms of the GNU
* General Public License ( GPL ) Version 2 , available from the file
* COPYING in the main directory of this source tree , or the
* OpenIB . org BSD license below :
*
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* - Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* - Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/cpumask.h>
#include <linux/string.h>
#include "csio_init.h"
#include "csio_hw.h"
static irqreturn_t
csio_nondata_isr(int irq, void *dev_id)
{
struct csio_hw *hw = (struct csio_hw *) dev_id;
int rv;
unsigned long flags;
if (unlikely(!hw))
return IRQ_NONE;
if (unlikely(pci_channel_offline(hw->pdev))) {
CSIO_INC_STATS(hw, n_pcich_offline);
return IRQ_NONE;
}
spin_lock_irqsave(&hw->lock, flags);
csio_hw_slow_intr_handler(hw);
rv = csio_mb_isr_handler(hw);
if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
hw->flags |= CSIO_HWF_FWEVT_PENDING;
spin_unlock_irqrestore(&hw->lock, flags);
schedule_work(&hw->evtq_work);
return IRQ_HANDLED;
}
spin_unlock_irqrestore(&hw->lock, flags);
return IRQ_HANDLED;
}
/*
* csio_fwevt_handler - Common FW event handler routine .
* @ hw : HW module .
*
* This is the ISR for FW events . It is shared b / w MSIX
* and INTx handlers .
*/
static void
csio_fwevt_handler(struct csio_hw *hw)
{
int rv;
unsigned long flags;
rv = csio_fwevtq_handler(hw);
spin_lock_irqsave(&hw->lock, flags);
if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
hw->flags |= CSIO_HWF_FWEVT_PENDING;
spin_unlock_irqrestore(&hw->lock, flags);
schedule_work(&hw->evtq_work);
return ;
}
spin_unlock_irqrestore(&hw->lock, flags);
} /* csio_fwevt_handler */
/*
* csio_fwevt_isr ( ) - FW events MSIX ISR
* @ irq :
* @ dev_id :
*
* Process WRs on the FW event queue .
*
*/
static irqreturn_t
csio_fwevt_isr(int irq, void *dev_id)
{
struct csio_hw *hw = (struct csio_hw *) dev_id;
if (unlikely(!hw))
return IRQ_NONE;
if (unlikely(pci_channel_offline(hw->pdev))) {
CSIO_INC_STATS(hw, n_pcich_offline);
return IRQ_NONE;
}
csio_fwevt_handler(hw);
return IRQ_HANDLED;
}
/*
* csio_fwevt_isr ( ) - INTx wrapper for handling FW events .
* @ irq :
* @ dev_id :
*/
void
csio_fwevt_intx_handler(struct csio_hw *hw, void *wr, uint32_t len,
struct csio_fl_dma_buf *flb, void *priv)
{
csio_fwevt_handler(hw);
} /* csio_fwevt_intx_handler */
/*
* csio_process_scsi_cmpl - Process a SCSI WR completion .
* @ hw : HW module .
* @ wr : The completed WR from the ingress queue .
* @ len : Length of the WR .
* @ flb : Freelist buffer array .
*
*/
static void
csio_process_scsi_cmpl(struct csio_hw *hw, void *wr, uint32_t len,
struct csio_fl_dma_buf *flb, void *cbfn_q)
{
struct csio_ioreq *ioreq;
uint8_t *scsiwr;
uint8_t subop;
void *cmnd;
unsigned long flags;
ioreq = csio_scsi_cmpl_handler(hw, wr, len, flb, NULL, &scsiwr);
if (likely(ioreq)) {
if (unlikely(*scsiwr == FW_SCSI_ABRT_CLS_WR)) {
subop = FW_SCSI_ABRT_CLS_WR_SUB_OPCODE_GET(
((struct fw_scsi_abrt_cls_wr *)
scsiwr)->sub_opcode_to_chk_all_io);
csio_dbg(hw, "%s cmpl recvd ioreq:%p status:%d\n" ,
subop ? "Close" : "Abort" ,
ioreq, ioreq->wr_status);
spin_lock_irqsave(&hw->lock, flags);
if (subop)
csio_scsi_closed(ioreq,
(struct list_head *)cbfn_q);
else
csio_scsi_aborted(ioreq,
(struct list_head *)cbfn_q);
/*
* We call scsi_done for I / Os that driver thinks aborts
* have timed out . If there is a race caused by FW
* completing abort at the exact same time that the
* driver has deteced the abort timeout , the following
* check prevents calling of scsi_done twice for the
* same command : once from the eh_abort_handler , another
* from csio_scsi_isr_handler ( ) . This also avoids the
* need to check if csio_scsi_cmnd ( req ) is NULL in the
* fast path .
*/
cmnd = csio_scsi_cmnd(ioreq);
if (unlikely(cmnd == NULL))
list_del_init(&ioreq->sm.sm_list);
spin_unlock_irqrestore(&hw->lock, flags);
if (unlikely(cmnd == NULL))
csio_put_scsi_ioreq_lock(hw,
csio_hw_to_scsim(hw), ioreq);
} else {
spin_lock_irqsave(&hw->lock, flags);
csio_scsi_completed(ioreq, (struct list_head *)cbfn_q);
spin_unlock_irqrestore(&hw->lock, flags);
}
}
}
/*
* csio_scsi_isr_handler ( ) - Common SCSI ISR handler .
* @ iq : Ingress queue pointer .
*
* Processes SCSI completions on the SCSI IQ indicated by scm - > iq_idx
* by calling csio_wr_process_iq_idx . If there are completions on the
* isr_cbfn_q , yank them out into a local queue and call their io_cbfns .
* Once done , add these completions onto the freelist .
* This routine is shared b / w MSIX and INTx .
*/
static inline irqreturn_t
csio_scsi_isr_handler(struct csio_q *iq)
{
struct csio_hw *hw = (struct csio_hw *)iq->owner;
LIST_HEAD(cbfn_q);
struct list_head *tmp;
struct csio_scsim *scm;
struct csio_ioreq *ioreq;
int isr_completions = 0 ;
scm = csio_hw_to_scsim(hw);
if (unlikely(csio_wr_process_iq(hw, iq, csio_process_scsi_cmpl,
&cbfn_q) != 0 ))
return IRQ_NONE;
/* Call back the completion routines */
list_for_each(tmp, &cbfn_q) {
ioreq = (struct csio_ioreq *)tmp;
isr_completions++;
ioreq->io_cbfn(hw, ioreq);
/* Release ddp buffer if used for this req */
if (unlikely(ioreq->dcopy))
csio_put_scsi_ddp_list_lock(hw, scm, &ioreq->gen_list,
ioreq->nsge);
}
if (isr_completions) {
/* Return the ioreqs back to ioreq->freelist */
csio_put_scsi_ioreq_list_lock(hw, scm, &cbfn_q,
isr_completions);
}
return IRQ_HANDLED;
}
/*
* csio_scsi_isr ( ) - SCSI MSIX handler
* @ irq :
* @ dev_id :
*
* This is the top level SCSI MSIX handler . Calls csio_scsi_isr_handler ( )
* for handling SCSI completions .
*/
static irqreturn_t
csio_scsi_isr(int irq, void *dev_id)
{
struct csio_q *iq = (struct csio_q *) dev_id;
struct csio_hw *hw;
if (unlikely(!iq))
return IRQ_NONE;
hw = (struct csio_hw *)iq->owner;
if (unlikely(pci_channel_offline(hw->pdev))) {
CSIO_INC_STATS(hw, n_pcich_offline);
return IRQ_NONE;
}
csio_scsi_isr_handler(iq);
return IRQ_HANDLED;
}
/*
* csio_scsi_intx_handler ( ) - SCSI INTx handler
* @ irq :
* @ dev_id :
*
* This is the top level SCSI INTx handler . Calls csio_scsi_isr_handler ( )
* for handling SCSI completions .
*/
void
csio_scsi_intx_handler(struct csio_hw *hw, void *wr, uint32_t len,
struct csio_fl_dma_buf *flb, void *priv)
{
struct csio_q *iq = priv;
csio_scsi_isr_handler(iq);
} /* csio_scsi_intx_handler */
/*
* csio_fcoe_isr ( ) - INTx / MSI interrupt service routine for FCoE .
* @ irq :
* @ dev_id :
*
*
*/
static irqreturn_t
csio_fcoe_isr(int irq, void *dev_id)
{
struct csio_hw *hw = (struct csio_hw *) dev_id;
struct csio_q *intx_q = NULL;
int rv;
irqreturn_t ret = IRQ_NONE;
unsigned long flags;
if (unlikely(!hw))
return IRQ_NONE;
if (unlikely(pci_channel_offline(hw->pdev))) {
CSIO_INC_STATS(hw, n_pcich_offline);
return IRQ_NONE;
}
/* Disable the interrupt for this PCI function. */
if (hw->intr_mode == CSIO_IM_INTX)
csio_wr_reg32(hw, 0 , MYPF_REG(PCIE_PF_CLI_A));
/*
* The read in the following function will flush the
* above write .
*/
if (csio_hw_slow_intr_handler(hw))
ret = IRQ_HANDLED;
/* Get the INTx Forward interrupt IQ. */
intx_q = csio_get_q(hw, hw->intr_iq_idx);
CSIO_DB_ASSERT(intx_q);
/* IQ handler is not possible for intx_q, hence pass in NULL */
if (likely(csio_wr_process_iq(hw, intx_q, NULL, NULL) == 0 ))
ret = IRQ_HANDLED;
spin_lock_irqsave(&hw->lock, flags);
rv = csio_mb_isr_handler(hw);
if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
hw->flags |= CSIO_HWF_FWEVT_PENDING;
spin_unlock_irqrestore(&hw->lock, flags);
schedule_work(&hw->evtq_work);
return IRQ_HANDLED;
}
spin_unlock_irqrestore(&hw->lock, flags);
return ret;
}
static void
csio_add_msix_desc(struct csio_hw *hw)
{
int i;
struct csio_msix_entries *entryp = &hw->msix_entries[0 ];
int k = CSIO_EXTRA_VECS;
int len = sizeof (entryp->desc) - 1 ;
int cnt = hw->num_sqsets + k;
/* Non-data vector */
memset(entryp->desc, 0 , len + 1 );
snprintf(entryp->desc, len, "csio-%02x:%02x:%x-nondata" ,
CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), CSIO_PCI_FUNC(hw));
entryp++;
memset(entryp->desc, 0 , len + 1 );
snprintf(entryp->desc, len, "csio-%02x:%02x:%x-fwevt" ,
CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), CSIO_PCI_FUNC(hw));
entryp++;
/* Name SCSI vecs */
for (i = k; i < cnt; i++, entryp++) {
memset(entryp->desc, 0 , len + 1 );
snprintf(entryp->desc, len, "csio-%02x:%02x:%x-scsi%d" ,
CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw),
CSIO_PCI_FUNC(hw), i - CSIO_EXTRA_VECS);
}
}
int
csio_request_irqs(struct csio_hw *hw)
{
int rv, i, j, k = 0 ;
struct csio_msix_entries *entryp = &hw->msix_entries[0 ];
struct csio_scsi_cpu_info *info;
struct pci_dev *pdev = hw->pdev;
if (hw->intr_mode != CSIO_IM_MSIX) {
rv = request_irq(pci_irq_vector(pdev, 0 ), csio_fcoe_isr,
hw->intr_mode == CSIO_IM_MSI ? 0 : IRQF_SHARED,
KBUILD_MODNAME, hw);
if (rv) {
csio_err(hw, "Failed to allocate interrupt line.\n" );
goto out_free_irqs;
}
goto out;
}
/* Add the MSIX vector descriptions */
csio_add_msix_desc(hw);
rv = request_irq(pci_irq_vector(pdev, k), csio_nondata_isr, 0 ,
entryp[k].desc, hw);
if (rv) {
csio_err(hw, "IRQ request failed for vec %d err:%d\n" ,
pci_irq_vector(pdev, k), rv);
goto out_free_irqs;
}
entryp[k++].dev_id = hw;
rv = request_irq(pci_irq_vector(pdev, k), csio_fwevt_isr, 0 ,
entryp[k].desc, hw);
if (rv) {
csio_err(hw, "IRQ request failed for vec %d err:%d\n" ,
pci_irq_vector(pdev, k), rv);
goto out_free_irqs;
}
entryp[k++].dev_id = (void *)hw;
/* Allocate IRQs for SCSI */
for (i = 0 ; i < hw->num_pports; i++) {
info = &hw->scsi_cpu_info[i];
for (j = 0 ; j < info->max_cpus; j++, k++) {
struct csio_scsi_qset *sqset = &hw->sqset[i][j];
struct csio_q *q = hw->wrm.q_arr[sqset->iq_idx];
rv = request_irq(pci_irq_vector(pdev, k), csio_scsi_isr, 0 ,
entryp[k].desc, q);
if (rv) {
csio_err(hw,
"IRQ request failed for vec %d err:%d\n" ,
pci_irq_vector(pdev, k), rv);
goto out_free_irqs;
}
entryp[k].dev_id = q;
} /* for all scsi cpus */
} /* for all ports */
out:
hw->flags |= CSIO_HWF_HOST_INTR_ENABLED;
return 0 ;
out_free_irqs:
for (i = 0 ; i < k; i++)
free_irq(pci_irq_vector(pdev, i), hw->msix_entries[i].dev_id);
pci_free_irq_vectors(hw->pdev);
return -EINVAL;
}
/* Reduce per-port max possible CPUs */
static void
csio_reduce_sqsets(struct csio_hw *hw, int cnt)
{
int i;
struct csio_scsi_cpu_info *info;
while (cnt < hw->num_sqsets) {
for (i = 0 ; i < hw->num_pports; i++) {
info = &hw->scsi_cpu_info[i];
if (info->max_cpus > 1 ) {
info->max_cpus--;
hw->num_sqsets--;
if (hw->num_sqsets <= cnt)
break ;
}
}
}
csio_dbg(hw, "Reduced sqsets to %d\n" , hw->num_sqsets);
}
static void csio_calc_sets(struct irq_affinity *affd, unsigned int nvecs)
{
struct csio_hw *hw = affd->priv;
u8 i;
if (!nvecs)
return ;
if (nvecs < hw->num_pports) {
affd->nr_sets = 1 ;
affd->set_size[0 ] = nvecs;
return ;
}
affd->nr_sets = hw->num_pports;
for (i = 0 ; i < hw->num_pports; i++)
affd->set_size[i] = nvecs / hw->num_pports;
}
static int
csio_enable_msix(struct csio_hw *hw)
{
int i, j, k, n, min, cnt;
int extra = CSIO_EXTRA_VECS;
struct csio_scsi_cpu_info *info;
struct irq_affinity desc = {
.pre_vectors = CSIO_EXTRA_VECS,
.calc_sets = csio_calc_sets,
.priv = hw,
};
if (hw->num_pports > IRQ_AFFINITY_MAX_SETS)
return -ENOSPC;
min = hw->num_pports + extra;
cnt = hw->num_sqsets + extra;
/* Max vectors required based on #niqs configured in fw */
if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS || !csio_is_hw_master(hw))
cnt = min_t(uint8_t, hw->cfg_niq, cnt);
csio_dbg(hw, "FW supp #niq:%d, trying %d msix's\n" , hw->cfg_niq, cnt);
cnt = pci_alloc_irq_vectors_affinity(hw->pdev, min, cnt,
PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, &desc);
if (cnt < 0 )
return cnt;
if (cnt < (hw->num_sqsets + extra)) {
csio_dbg(hw, "Reducing sqsets to %d\n" , cnt - extra);
csio_reduce_sqsets(hw, cnt - extra);
}
/* Distribute vectors */
k = 0 ;
csio_set_nondata_intr_idx(hw, k);
csio_set_mb_intr_idx(csio_hw_to_mbm(hw), k++);
csio_set_fwevt_intr_idx(hw, k++);
for (i = 0 ; i < hw->num_pports; i++) {
info = &hw->scsi_cpu_info[i];
for (j = 0 ; j < hw->num_scsi_msix_cpus; j++) {
n = (j % info->max_cpus) + k;
hw->sqset[i][j].intr_idx = n;
}
k += info->max_cpus;
}
return 0 ;
}
void
csio_intr_enable(struct csio_hw *hw)
{
hw->intr_mode = CSIO_IM_NONE;
hw->flags &= ~CSIO_HWF_HOST_INTR_ENABLED;
/* Try MSIX, then MSI or fall back to INTx */
if ((csio_msi == 2 ) && !csio_enable_msix(hw))
hw->intr_mode = CSIO_IM_MSIX;
else {
/* Max iqs required based on #niqs configured in fw */
if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS ||
!csio_is_hw_master(hw)) {
int extra = CSIO_EXTRA_MSI_IQS;
if (hw->cfg_niq < (hw->num_sqsets + extra)) {
csio_dbg(hw, "Reducing sqsets to %d\n" ,
hw->cfg_niq - extra);
csio_reduce_sqsets(hw, hw->cfg_niq - extra);
}
}
if ((csio_msi == 1 ) && !pci_enable_msi(hw->pdev))
hw->intr_mode = CSIO_IM_MSI;
else
hw->intr_mode = CSIO_IM_INTX;
}
csio_dbg(hw, "Using %s interrupt mode.\n" ,
(hw->intr_mode == CSIO_IM_MSIX) ? "MSIX" :
((hw->intr_mode == CSIO_IM_MSI) ? "MSI" : "INTx" ));
}
void
csio_intr_disable(struct csio_hw *hw, bool free)
{
csio_hw_intr_disable(hw);
if (free) {
int i;
switch (hw->intr_mode) {
case CSIO_IM_MSIX:
for (i = 0 ; i < hw->num_sqsets + CSIO_EXTRA_VECS; i++) {
free_irq(pci_irq_vector(hw->pdev, i),
hw->msix_entries[i].dev_id);
}
break ;
case CSIO_IM_MSI:
case CSIO_IM_INTX:
free_irq(pci_irq_vector(hw->pdev, 0 ), hw);
break ;
default :
break ;
}
}
pci_free_irq_vectors(hw->pdev);
hw->intr_mode = CSIO_IM_NONE;
hw->flags &= ~CSIO_HWF_HOST_INTR_ENABLED;
}
Messung V0.5 in Prozent C=96 H=86 G=90
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.17Bemerkung:
(vorverarbeitet am 2026-10-01)
¤
*Bot Zugriff