2022-06-07 16:11:24 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2009-04-17 18:33:26 -07:00
/*
* Copyright 2008 Cisco Systems , Inc . All rights reserved .
* Copyright 2007 Nuova Systems , Inc . All rights reserved .
*/
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <scsi/libfc.h>
# include <scsi/fc_frame.h>
# include "vnic_dev.h"
# include "vnic_intr.h"
# include "vnic_stats.h"
# include "fnic_io.h"
# include "fnic.h"
static irqreturn_t fnic_isr_legacy ( int irq , void * data )
{
struct fnic * fnic = data ;
u32 pba ;
unsigned long work_done = 0 ;
pba = vnic_intr_legacy_pba ( fnic - > legacy_pba ) ;
if ( ! pba )
return IRQ_NONE ;
2013-09-12 17:45:42 -07:00
fnic - > fnic_stats . misc_stats . last_isr_time = jiffies ;
atomic64_inc ( & fnic - > fnic_stats . misc_stats . isr_count ) ;
2009-04-17 18:33:26 -07:00
if ( pba & ( 1 < < FNIC_INTX_NOTIFY ) ) {
vnic_intr_return_all_credits ( & fnic - > intr [ FNIC_INTX_NOTIFY ] ) ;
fnic_handle_link_event ( fnic ) ;
}
if ( pba & ( 1 < < FNIC_INTX_ERR ) ) {
vnic_intr_return_all_credits ( & fnic - > intr [ FNIC_INTX_ERR ] ) ;
fnic_log_q_error ( fnic ) ;
}
2023-12-11 09:36:11 -08:00
if ( pba & ( 1 < < FNIC_INTX_DUMMY ) ) {
atomic64_inc ( & fnic - > fnic_stats . misc_stats . intx_dummy ) ;
vnic_intr_return_all_credits ( & fnic - > intr [ FNIC_INTX_DUMMY ] ) ;
}
2009-04-17 18:33:26 -07:00
if ( pba & ( 1 < < FNIC_INTX_WQ_RQ_COPYWQ ) ) {
2023-12-11 09:36:11 -08:00
work_done + = fnic_wq_copy_cmpl_handler ( fnic , io_completions , FNIC_MQ_CQ_INDEX ) ;
2009-10-21 16:28:14 -07:00
work_done + = fnic_wq_cmpl_handler ( fnic , - 1 ) ;
work_done + = fnic_rq_cmpl_handler ( fnic , - 1 ) ;
2009-04-17 18:33:26 -07:00
vnic_intr_return_credits ( & fnic - > intr [ FNIC_INTX_WQ_RQ_COPYWQ ] ,
work_done ,
1 /* unmask intr */ ,
1 /* reset intr timer */ ) ;
}
return IRQ_HANDLED ;
}
static irqreturn_t fnic_isr_msi ( int irq , void * data )
{
struct fnic * fnic = data ;
unsigned long work_done = 0 ;
2013-09-12 17:45:42 -07:00
fnic - > fnic_stats . misc_stats . last_isr_time = jiffies ;
atomic64_inc ( & fnic - > fnic_stats . misc_stats . isr_count ) ;
2023-12-11 09:36:11 -08:00
work_done + = fnic_wq_copy_cmpl_handler ( fnic , io_completions , FNIC_MQ_CQ_INDEX ) ;
2009-10-21 16:28:14 -07:00
work_done + = fnic_wq_cmpl_handler ( fnic , - 1 ) ;
work_done + = fnic_rq_cmpl_handler ( fnic , - 1 ) ;
2009-04-17 18:33:26 -07:00
vnic_intr_return_credits ( & fnic - > intr [ 0 ] ,
work_done ,
1 /* unmask intr */ ,
1 /* reset intr timer */ ) ;
return IRQ_HANDLED ;
}
static irqreturn_t fnic_isr_msix_rq ( int irq , void * data )
{
struct fnic * fnic = data ;
unsigned long rq_work_done = 0 ;
2013-09-12 17:45:42 -07:00
fnic - > fnic_stats . misc_stats . last_isr_time = jiffies ;
atomic64_inc ( & fnic - > fnic_stats . misc_stats . isr_count ) ;
2009-10-21 16:28:14 -07:00
rq_work_done = fnic_rq_cmpl_handler ( fnic , - 1 ) ;
2009-04-17 18:33:26 -07:00
vnic_intr_return_credits ( & fnic - > intr [ FNIC_MSIX_RQ ] ,
rq_work_done ,
1 /* unmask intr */ ,
1 /* reset intr timer */ ) ;
return IRQ_HANDLED ;
}
static irqreturn_t fnic_isr_msix_wq ( int irq , void * data )
{
struct fnic * fnic = data ;
unsigned long wq_work_done = 0 ;
2013-09-12 17:45:42 -07:00
fnic - > fnic_stats . misc_stats . last_isr_time = jiffies ;
atomic64_inc ( & fnic - > fnic_stats . misc_stats . isr_count ) ;
2009-10-21 16:28:14 -07:00
wq_work_done = fnic_wq_cmpl_handler ( fnic , - 1 ) ;
2009-04-17 18:33:26 -07:00
vnic_intr_return_credits ( & fnic - > intr [ FNIC_MSIX_WQ ] ,
wq_work_done ,
1 /* unmask intr */ ,
1 /* reset intr timer */ ) ;
return IRQ_HANDLED ;
}
static irqreturn_t fnic_isr_msix_wq_copy ( int irq , void * data )
{
struct fnic * fnic = data ;
unsigned long wq_copy_work_done = 0 ;
2023-12-11 09:36:11 -08:00
int i ;
2009-04-17 18:33:26 -07:00
2013-09-12 17:45:42 -07:00
fnic - > fnic_stats . misc_stats . last_isr_time = jiffies ;
atomic64_inc ( & fnic - > fnic_stats . misc_stats . isr_count ) ;
2023-12-11 09:36:11 -08:00
i = irq - fnic - > msix [ 0 ] . irq_num ;
if ( i > = fnic - > wq_copy_count + fnic - > copy_wq_base | |
i < 0 | | fnic - > msix [ i ] . irq_num ! = irq ) {
for ( i = fnic - > copy_wq_base ; i < fnic - > wq_copy_count + fnic - > copy_wq_base ; i + + ) {
if ( fnic - > msix [ i ] . irq_num = = irq )
break ;
}
}
wq_copy_work_done = fnic_wq_copy_cmpl_handler ( fnic , io_completions , i ) ;
vnic_intr_return_credits ( & fnic - > intr [ i ] ,
2009-04-17 18:33:26 -07:00
wq_copy_work_done ,
1 /* unmask intr */ ,
1 /* reset intr timer */ ) ;
return IRQ_HANDLED ;
}
static irqreturn_t fnic_isr_msix_err_notify ( int irq , void * data )
{
struct fnic * fnic = data ;
2013-09-12 17:45:42 -07:00
fnic - > fnic_stats . misc_stats . last_isr_time = jiffies ;
atomic64_inc ( & fnic - > fnic_stats . misc_stats . isr_count ) ;
2023-12-11 09:36:11 -08:00
vnic_intr_return_all_credits ( & fnic - > intr [ fnic - > err_intr_offset ] ) ;
2009-04-17 18:33:26 -07:00
fnic_log_q_error ( fnic ) ;
fnic_handle_link_event ( fnic ) ;
return IRQ_HANDLED ;
}
void fnic_free_intr ( struct fnic * fnic )
{
int i ;
switch ( vnic_dev_get_intr_mode ( fnic - > vdev ) ) {
case VNIC_DEV_INTR_MODE_INTX :
case VNIC_DEV_INTR_MODE_MSI :
2017-02-01 15:02:53 +01:00
free_irq ( pci_irq_vector ( fnic - > pdev , 0 ) , fnic ) ;
2009-04-17 18:33:26 -07:00
break ;
case VNIC_DEV_INTR_MODE_MSIX :
for ( i = 0 ; i < ARRAY_SIZE ( fnic - > msix ) ; i + + )
if ( fnic - > msix [ i ] . requested )
2017-02-01 15:02:53 +01:00
free_irq ( pci_irq_vector ( fnic - > pdev , i ) ,
2009-04-17 18:33:26 -07:00
fnic - > msix [ i ] . devid ) ;
break ;
default :
break ;
}
}
int fnic_request_intr ( struct fnic * fnic )
{
int err = 0 ;
int i ;
switch ( vnic_dev_get_intr_mode ( fnic - > vdev ) ) {
case VNIC_DEV_INTR_MODE_INTX :
2017-02-01 15:02:53 +01:00
err = request_irq ( pci_irq_vector ( fnic - > pdev , 0 ) ,
& fnic_isr_legacy , IRQF_SHARED , DRV_NAME , fnic ) ;
2009-04-17 18:33:26 -07:00
break ;
case VNIC_DEV_INTR_MODE_MSI :
2017-02-01 15:02:53 +01:00
err = request_irq ( pci_irq_vector ( fnic - > pdev , 0 ) , & fnic_isr_msi ,
2009-04-17 18:33:26 -07:00
0 , fnic - > name , fnic ) ;
break ;
case VNIC_DEV_INTR_MODE_MSIX :
sprintf ( fnic - > msix [ FNIC_MSIX_RQ ] . devname ,
" %.11s-fcs-rq " , fnic - > name ) ;
fnic - > msix [ FNIC_MSIX_RQ ] . isr = fnic_isr_msix_rq ;
fnic - > msix [ FNIC_MSIX_RQ ] . devid = fnic ;
sprintf ( fnic - > msix [ FNIC_MSIX_WQ ] . devname ,
" %.11s-fcs-wq " , fnic - > name ) ;
fnic - > msix [ FNIC_MSIX_WQ ] . isr = fnic_isr_msix_wq ;
fnic - > msix [ FNIC_MSIX_WQ ] . devid = fnic ;
2023-12-11 09:36:11 -08:00
for ( i = fnic - > copy_wq_base ; i < fnic - > wq_copy_count + fnic - > copy_wq_base ; i + + ) {
sprintf ( fnic - > msix [ i ] . devname ,
" %.11s-scsi-wq-%d " , fnic - > name , i - FNIC_MSIX_WQ_COPY ) ;
fnic - > msix [ i ] . isr = fnic_isr_msix_wq_copy ;
fnic - > msix [ i ] . devid = fnic ;
}
2009-04-17 18:33:26 -07:00
2023-12-11 09:36:11 -08:00
sprintf ( fnic - > msix [ fnic - > err_intr_offset ] . devname ,
2009-04-17 18:33:26 -07:00
" %.11s-err-notify " , fnic - > name ) ;
2023-12-11 09:36:11 -08:00
fnic - > msix [ fnic - > err_intr_offset ] . isr =
2009-04-17 18:33:26 -07:00
fnic_isr_msix_err_notify ;
2023-12-11 09:36:11 -08:00
fnic - > msix [ fnic - > err_intr_offset ] . devid = fnic ;
2009-04-17 18:33:26 -07:00
2023-12-11 09:36:11 -08:00
for ( i = 0 ; i < fnic - > intr_count ; i + + ) {
fnic - > msix [ i ] . irq_num = pci_irq_vector ( fnic - > pdev , i ) ;
err = request_irq ( fnic - > msix [ i ] . irq_num ,
fnic - > msix [ i ] . isr , 0 ,
fnic - > msix [ i ] . devname ,
fnic - > msix [ i ] . devid ) ;
2009-04-17 18:33:26 -07:00
if ( err ) {
2023-12-11 09:36:11 -08:00
FNIC_ISR_DBG ( KERN_ERR , fnic - > lport - > host , fnic - > fnic_num ,
" request_irq failed with error: %d \n " ,
err ) ;
2009-04-17 18:33:26 -07:00
fnic_free_intr ( fnic ) ;
break ;
}
fnic - > msix [ i ] . requested = 1 ;
}
break ;
default :
break ;
}
return err ;
}
2023-12-11 09:36:11 -08:00
int fnic_set_intr_mode_msix ( struct fnic * fnic )
2009-04-17 18:33:26 -07:00
{
unsigned int n = ARRAY_SIZE ( fnic - > rq ) ;
unsigned int m = ARRAY_SIZE ( fnic - > wq ) ;
2023-12-11 09:36:08 -08:00
unsigned int o = ARRAY_SIZE ( fnic - > hw_copy_wq ) ;
2023-12-11 09:36:11 -08:00
unsigned int min_irqs = n + m + 1 + 1 ; /*rq, raw wq, wq, err*/
2009-04-17 18:33:26 -07:00
/*
* We need n RQs , m WQs , o Copy WQs , n + m + o CQs , and n + m + o + 1 INTRs
* ( last INTR is used for WQ / RQ errors and notification area )
*/
2023-12-11 09:36:11 -08:00
FNIC_ISR_DBG ( KERN_INFO , fnic - > lport - > host , fnic - > fnic_num ,
" rq-array size: %d wq-array size: %d copy-wq array size: %d \n " ,
n , m , o ) ;
FNIC_ISR_DBG ( KERN_INFO , fnic - > lport - > host , fnic - > fnic_num ,
" rq_count: %d raw_wq_count: %d wq_copy_count: %d cq_count: %d \n " ,
fnic - > rq_count , fnic - > raw_wq_count ,
fnic - > wq_copy_count , fnic - > cq_count ) ;
if ( fnic - > rq_count < = n & & fnic - > raw_wq_count < = m & &
fnic - > wq_copy_count < = o ) {
int vec_count = 0 ;
int vecs = fnic - > rq_count + fnic - > raw_wq_count + fnic - > wq_copy_count + 1 ;
vec_count = pci_alloc_irq_vectors ( fnic - > pdev , min_irqs , vecs ,
PCI_IRQ_MSIX | PCI_IRQ_AFFINITY ) ;
FNIC_ISR_DBG ( KERN_INFO , fnic - > lport - > host , fnic - > fnic_num ,
" allocated %d MSI-X vectors \n " ,
vec_count ) ;
if ( vec_count > 0 ) {
if ( vec_count < vecs ) {
FNIC_ISR_DBG ( KERN_ERR , fnic - > lport - > host , fnic - > fnic_num ,
" interrupts number mismatch: vec_count: %d vecs: %d \n " ,
vec_count , vecs ) ;
if ( vec_count < min_irqs ) {
FNIC_ISR_DBG ( KERN_ERR , fnic - > lport - > host , fnic - > fnic_num ,
" no interrupts for copy wq \n " ) ;
return 1 ;
}
}
2009-04-17 18:33:26 -07:00
fnic - > rq_count = n ;
fnic - > raw_wq_count = m ;
2023-12-11 09:36:11 -08:00
fnic - > copy_wq_base = fnic - > rq_count + fnic - > raw_wq_count ;
fnic - > wq_copy_count = vec_count - n - m - 1 ;
fnic - > wq_count = fnic - > raw_wq_count + fnic - > wq_copy_count ;
if ( fnic - > cq_count ! = vec_count - 1 ) {
FNIC_ISR_DBG ( KERN_ERR , fnic - > lport - > host , fnic - > fnic_num ,
" CQ count: %d does not match MSI-X vector count: %d \n " ,
fnic - > cq_count , vec_count ) ;
fnic - > cq_count = vec_count - 1 ;
}
fnic - > intr_count = vec_count ;
fnic - > err_intr_offset = fnic - > rq_count + fnic - > wq_count ;
FNIC_ISR_DBG ( KERN_INFO , fnic - > lport - > host , fnic - > fnic_num ,
" rq_count: %d raw_wq_count: %d copy_wq_base: %d \n " ,
fnic - > rq_count ,
fnic - > raw_wq_count , fnic - > copy_wq_base ) ;
FNIC_ISR_DBG ( KERN_INFO , fnic - > lport - > host , fnic - > fnic_num ,
" wq_copy_count: %d wq_count: %d cq_count: %d \n " ,
fnic - > wq_copy_count ,
fnic - > wq_count , fnic - > cq_count ) ;
FNIC_ISR_DBG ( KERN_INFO , fnic - > lport - > host , fnic - > fnic_num ,
" intr_count: %d err_intr_offset: %u " ,
fnic - > intr_count ,
fnic - > err_intr_offset ) ;
vnic_dev_set_intr_mode ( fnic - > vdev , VNIC_DEV_INTR_MODE_MSIX ) ;
FNIC_ISR_DBG ( KERN_INFO , fnic - > lport - > host , fnic - > fnic_num ,
" fnic using MSI-X \n " ) ;
2009-04-17 18:33:26 -07:00
return 0 ;
}
}
2023-12-11 09:36:11 -08:00
return 1 ;
}
int fnic_set_intr_mode ( struct fnic * fnic )
{
int ret_status = 0 ;
/*
* Set interrupt mode ( INTx , MSI , MSI - X ) depending
* system capabilities .
*
* Try MSI - X first
*/
ret_status = fnic_set_intr_mode_msix ( fnic ) ;
if ( ret_status = = 0 )
return ret_status ;
2009-04-17 18:33:26 -07:00
/*
* Next try MSI
* We need 1 RQ , 1 WQ , 1 WQ_COPY , 3 CQs , and 1 INTR
*/
if ( fnic - > rq_count > = 1 & &
fnic - > raw_wq_count > = 1 & &
fnic - > wq_copy_count > = 1 & &
fnic - > cq_count > = 3 & &
fnic - > intr_count > = 1 & &
2019-08-27 14:13:40 -07:00
pci_alloc_irq_vectors ( fnic - > pdev , 1 , 1 , PCI_IRQ_MSI ) = = 1 ) {
2009-04-17 18:33:26 -07:00
fnic - > rq_count = 1 ;
fnic - > raw_wq_count = 1 ;
fnic - > wq_copy_count = 1 ;
fnic - > wq_count = 2 ;
fnic - > cq_count = 3 ;
fnic - > intr_count = 1 ;
fnic - > err_intr_offset = 0 ;
2023-12-11 09:36:11 -08:00
FNIC_ISR_DBG ( KERN_DEBUG , fnic - > lport - > host , fnic - > fnic_num ,
2009-04-17 18:33:26 -07:00
" Using MSI Interrupts \n " ) ;
vnic_dev_set_intr_mode ( fnic - > vdev , VNIC_DEV_INTR_MODE_MSI ) ;
return 0 ;
}
/*
* Next try INTx
* We need 1 RQ , 1 WQ , 1 WQ_COPY , 3 CQs , and 3 INTRs
* 1 INTR is used for all 3 queues , 1 INTR for queue errors
* 1 INTR for notification area
*/
if ( fnic - > rq_count > = 1 & &
fnic - > raw_wq_count > = 1 & &
fnic - > wq_copy_count > = 1 & &
fnic - > cq_count > = 3 & &
fnic - > intr_count > = 3 ) {
fnic - > rq_count = 1 ;
fnic - > raw_wq_count = 1 ;
fnic - > wq_copy_count = 1 ;
fnic - > cq_count = 3 ;
fnic - > intr_count = 3 ;
2023-12-11 09:36:11 -08:00
FNIC_ISR_DBG ( KERN_DEBUG , fnic - > lport - > host , fnic - > fnic_num ,
2009-04-17 18:33:26 -07:00
" Using Legacy Interrupts \n " ) ;
vnic_dev_set_intr_mode ( fnic - > vdev , VNIC_DEV_INTR_MODE_INTX ) ;
return 0 ;
}
vnic_dev_set_intr_mode ( fnic - > vdev , VNIC_DEV_INTR_MODE_UNKNOWN ) ;
return - EINVAL ;
}
void fnic_clear_intr_mode ( struct fnic * fnic )
{
2017-02-01 15:02:53 +01:00
pci_free_irq_vectors ( fnic - > pdev ) ;
2009-04-17 18:33:26 -07:00
vnic_dev_set_intr_mode ( fnic - > vdev , VNIC_DEV_INTR_MODE_INTX ) ;
}