2017-01-23 22:09:14 -08:00
/*
* aQuantia Corporation Network Driver
* Copyright ( C ) 2014 - 2017 aQuantia Corporation . All rights reserved
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*/
/* File aq_vec.c: Definition of common structure for vector of Rx and Tx rings.
* Definition of functions for Rx and Tx rings . Friendly module for aq_nic .
*/
# include "aq_vec.h"
# include "aq_nic.h"
# include "aq_ring.h"
# include "aq_hw.h"
# include <linux/netdevice.h>
struct aq_vec_s {
struct aq_hw_ops * aq_hw_ops ;
struct aq_hw_s * aq_hw ;
struct aq_nic_s * aq_nic ;
unsigned int tx_rings ;
unsigned int rx_rings ;
struct aq_ring_param_s aq_ring_param ;
struct napi_struct napi ;
struct aq_ring_s ring [ AQ_CFG_TCS_MAX ] [ 2 ] ;
} ;
# define AQ_VEC_TX_ID 0
# define AQ_VEC_RX_ID 1
static int aq_vec_poll ( struct napi_struct * napi , int budget )
{
struct aq_vec_s * self = container_of ( napi , struct aq_vec_s , napi ) ;
struct aq_ring_s * ring = NULL ;
int work_done = 0 ;
int err = 0 ;
unsigned int i = 0U ;
unsigned int sw_tail_old = 0U ;
bool was_tx_cleaned = false ;
if ( ! self ) {
err = - EINVAL ;
2017-08-28 21:52:08 +03:00
} else {
2017-01-23 22:09:14 -08:00
for ( i = 0U , ring = self - > ring [ 0 ] ;
self - > tx_rings > i ; + + i , ring = self - > ring [ i ] ) {
if ( self - > aq_hw_ops - > hw_ring_tx_head_update ) {
err = self - > aq_hw_ops - > hw_ring_tx_head_update (
self - > aq_hw ,
& ring [ AQ_VEC_TX_ID ] ) ;
if ( err < 0 )
goto err_exit ;
}
if ( ring [ AQ_VEC_TX_ID ] . sw_head ! =
2017-02-18 12:27:12 +01:00
ring [ AQ_VEC_TX_ID ] . hw_head ) {
aq_ring_tx_clean ( & ring [ AQ_VEC_TX_ID ] ) ;
2017-09-25 10:48:48 +03:00
aq_ring_update_queue_state ( & ring [ AQ_VEC_TX_ID ] ) ;
2017-01-23 22:09:14 -08:00
was_tx_cleaned = true ;
}
err = self - > aq_hw_ops - > hw_ring_rx_receive ( self - > aq_hw ,
& ring [ AQ_VEC_RX_ID ] ) ;
if ( err < 0 )
goto err_exit ;
if ( ring [ AQ_VEC_RX_ID ] . sw_head ! =
ring [ AQ_VEC_RX_ID ] . hw_head ) {
err = aq_ring_rx_clean ( & ring [ AQ_VEC_RX_ID ] ,
2017-08-03 18:15:32 +03:00
napi ,
2017-01-23 22:09:14 -08:00
& work_done ,
budget - work_done ) ;
if ( err < 0 )
goto err_exit ;
sw_tail_old = ring [ AQ_VEC_RX_ID ] . sw_tail ;
err = aq_ring_rx_fill ( & ring [ AQ_VEC_RX_ID ] ) ;
if ( err < 0 )
goto err_exit ;
err = self - > aq_hw_ops - > hw_ring_rx_fill (
self - > aq_hw ,
& ring [ AQ_VEC_RX_ID ] , sw_tail_old ) ;
if ( err < 0 )
goto err_exit ;
}
}
if ( was_tx_cleaned )
work_done = budget ;
if ( work_done < budget ) {
2017-01-30 08:22:01 -08:00
napi_complete_done ( napi , work_done ) ;
2017-01-23 22:09:14 -08:00
self - > aq_hw_ops - > hw_irq_enable ( self - > aq_hw ,
1U < < self - > aq_ring_param . vec_idx ) ;
}
}
2017-08-28 21:52:08 +03:00
err_exit :
2017-01-23 22:09:14 -08:00
return work_done ;
}
struct aq_vec_s * aq_vec_alloc ( struct aq_nic_s * aq_nic , unsigned int idx ,
struct aq_nic_cfg_s * aq_nic_cfg )
{
struct aq_vec_s * self = NULL ;
struct aq_ring_s * ring = NULL ;
unsigned int i = 0U ;
int err = 0 ;
self = kzalloc ( sizeof ( * self ) , GFP_KERNEL ) ;
if ( ! self ) {
err = - ENOMEM ;
goto err_exit ;
}
self - > aq_nic = aq_nic ;
self - > aq_ring_param . vec_idx = idx ;
self - > aq_ring_param . cpu =
idx + aq_nic_cfg - > aq_rss . base_cpu_number ;
cpumask_set_cpu ( self - > aq_ring_param . cpu ,
& self - > aq_ring_param . affinity_mask ) ;
self - > tx_rings = 0 ;
self - > rx_rings = 0 ;
netif_napi_add ( aq_nic_get_ndev ( aq_nic ) , & self - > napi ,
aq_vec_poll , AQ_CFG_NAPI_WEIGHT ) ;
for ( i = 0 ; i < aq_nic_cfg - > tcs ; + + i ) {
unsigned int idx_ring = AQ_NIC_TCVEC2RING ( self - > nic ,
self - > tx_rings ,
self - > aq_ring_param . vec_idx ) ;
ring = aq_ring_tx_alloc ( & self - > ring [ i ] [ AQ_VEC_TX_ID ] , aq_nic ,
idx_ring , aq_nic_cfg ) ;
if ( ! ring ) {
err = - ENOMEM ;
goto err_exit ;
}
+ + self - > tx_rings ;
aq_nic_set_tx_ring ( aq_nic , idx_ring , ring ) ;
ring = aq_ring_rx_alloc ( & self - > ring [ i ] [ AQ_VEC_RX_ID ] , aq_nic ,
idx_ring , aq_nic_cfg ) ;
if ( ! ring ) {
err = - ENOMEM ;
goto err_exit ;
}
+ + self - > rx_rings ;
}
err_exit :
if ( err < 0 ) {
aq_vec_free ( self ) ;
self = NULL ;
}
return self ;
}
int aq_vec_init ( struct aq_vec_s * self , struct aq_hw_ops * aq_hw_ops ,
struct aq_hw_s * aq_hw )
{
struct aq_ring_s * ring = NULL ;
unsigned int i = 0U ;
int err = 0 ;
self - > aq_hw_ops = aq_hw_ops ;
self - > aq_hw = aq_hw ;
for ( i = 0U , ring = self - > ring [ 0 ] ;
self - > tx_rings > i ; + + i , ring = self - > ring [ i ] ) {
err = aq_ring_init ( & ring [ AQ_VEC_TX_ID ] ) ;
if ( err < 0 )
goto err_exit ;
err = self - > aq_hw_ops - > hw_ring_tx_init ( self - > aq_hw ,
& ring [ AQ_VEC_TX_ID ] ,
& self - > aq_ring_param ) ;
if ( err < 0 )
goto err_exit ;
err = aq_ring_init ( & ring [ AQ_VEC_RX_ID ] ) ;
if ( err < 0 )
goto err_exit ;
err = self - > aq_hw_ops - > hw_ring_rx_init ( self - > aq_hw ,
& ring [ AQ_VEC_RX_ID ] ,
& self - > aq_ring_param ) ;
if ( err < 0 )
goto err_exit ;
err = aq_ring_rx_fill ( & ring [ AQ_VEC_RX_ID ] ) ;
if ( err < 0 )
goto err_exit ;
err = self - > aq_hw_ops - > hw_ring_rx_fill ( self - > aq_hw ,
& ring [ AQ_VEC_RX_ID ] , 0U ) ;
if ( err < 0 )
goto err_exit ;
}
err_exit :
return err ;
}
int aq_vec_start ( struct aq_vec_s * self )
{
struct aq_ring_s * ring = NULL ;
unsigned int i = 0U ;
int err = 0 ;
for ( i = 0U , ring = self - > ring [ 0 ] ;
self - > tx_rings > i ; + + i , ring = self - > ring [ i ] ) {
err = self - > aq_hw_ops - > hw_ring_tx_start ( self - > aq_hw ,
& ring [ AQ_VEC_TX_ID ] ) ;
if ( err < 0 )
goto err_exit ;
err = self - > aq_hw_ops - > hw_ring_rx_start ( self - > aq_hw ,
& ring [ AQ_VEC_RX_ID ] ) ;
if ( err < 0 )
goto err_exit ;
}
napi_enable ( & self - > napi ) ;
err_exit :
return err ;
}
void aq_vec_stop ( struct aq_vec_s * self )
{
struct aq_ring_s * ring = NULL ;
unsigned int i = 0U ;
for ( i = 0U , ring = self - > ring [ 0 ] ;
self - > tx_rings > i ; + + i , ring = self - > ring [ i ] ) {
self - > aq_hw_ops - > hw_ring_tx_stop ( self - > aq_hw ,
& ring [ AQ_VEC_TX_ID ] ) ;
self - > aq_hw_ops - > hw_ring_rx_stop ( self - > aq_hw ,
& ring [ AQ_VEC_RX_ID ] ) ;
}
napi_disable ( & self - > napi ) ;
}
void aq_vec_deinit ( struct aq_vec_s * self )
{
struct aq_ring_s * ring = NULL ;
unsigned int i = 0U ;
if ( ! self )
goto err_exit ;
for ( i = 0U , ring = self - > ring [ 0 ] ;
self - > tx_rings > i ; + + i , ring = self - > ring [ i ] ) {
2017-02-18 12:27:12 +01:00
aq_ring_tx_clean ( & ring [ AQ_VEC_TX_ID ] ) ;
2017-01-23 22:09:14 -08:00
aq_ring_rx_deinit ( & ring [ AQ_VEC_RX_ID ] ) ;
}
err_exit : ;
}
void aq_vec_free ( struct aq_vec_s * self )
{
struct aq_ring_s * ring = NULL ;
unsigned int i = 0U ;
if ( ! self )
goto err_exit ;
for ( i = 0U , ring = self - > ring [ 0 ] ;
self - > tx_rings > i ; + + i , ring = self - > ring [ i ] ) {
aq_ring_free ( & ring [ AQ_VEC_TX_ID ] ) ;
aq_ring_free ( & ring [ AQ_VEC_RX_ID ] ) ;
}
netif_napi_del ( & self - > napi ) ;
kfree ( self ) ;
err_exit : ;
}
irqreturn_t aq_vec_isr ( int irq , void * private )
{
struct aq_vec_s * self = private ;
int err = 0 ;
if ( ! self ) {
err = - EINVAL ;
goto err_exit ;
}
napi_schedule ( & self - > napi ) ;
err_exit :
return err > = 0 ? IRQ_HANDLED : IRQ_NONE ;
}
irqreturn_t aq_vec_isr_legacy ( int irq , void * private )
{
struct aq_vec_s * self = private ;
u64 irq_mask = 0U ;
irqreturn_t err = 0 ;
if ( ! self ) {
err = - EINVAL ;
goto err_exit ;
}
err = self - > aq_hw_ops - > hw_irq_read ( self - > aq_hw , & irq_mask ) ;
if ( err < 0 )
goto err_exit ;
if ( irq_mask ) {
self - > aq_hw_ops - > hw_irq_disable ( self - > aq_hw ,
1U < < self - > aq_ring_param . vec_idx ) ;
napi_schedule ( & self - > napi ) ;
} else {
self - > aq_hw_ops - > hw_irq_enable ( self - > aq_hw , 1U ) ;
err = IRQ_NONE ;
}
err_exit :
return err > = 0 ? IRQ_HANDLED : IRQ_NONE ;
}
cpumask_t * aq_vec_get_affinity_mask ( struct aq_vec_s * self )
{
return & self - > aq_ring_param . affinity_mask ;
}
void aq_vec_add_stats ( struct aq_vec_s * self ,
struct aq_ring_stats_rx_s * stats_rx ,
struct aq_ring_stats_tx_s * stats_tx )
{
struct aq_ring_s * ring = NULL ;
unsigned int r = 0U ;
for ( r = 0U , ring = self - > ring [ 0 ] ;
self - > tx_rings > r ; + + r , ring = self - > ring [ r ] ) {
struct aq_ring_stats_tx_s * tx = & ring [ AQ_VEC_TX_ID ] . stats . tx ;
struct aq_ring_stats_rx_s * rx = & ring [ AQ_VEC_RX_ID ] . stats . rx ;
stats_rx - > packets + = rx - > packets ;
stats_rx - > bytes + = rx - > bytes ;
stats_rx - > errors + = rx - > errors ;
stats_rx - > jumbo_packets + = rx - > jumbo_packets ;
stats_rx - > lro_packets + = rx - > lro_packets ;
stats_tx - > packets + = tx - > packets ;
stats_tx - > bytes + = tx - > bytes ;
stats_tx - > errors + = tx - > errors ;
2017-09-25 10:48:48 +03:00
stats_tx - > queue_restarts + = tx - > queue_restarts ;
2017-01-23 22:09:14 -08:00
}
}
int aq_vec_get_sw_stats ( struct aq_vec_s * self , u64 * data , unsigned int * p_count )
{
unsigned int count = 0U ;
struct aq_ring_stats_rx_s stats_rx ;
struct aq_ring_stats_tx_s stats_tx ;
memset ( & stats_rx , 0U , sizeof ( struct aq_ring_stats_rx_s ) ) ;
memset ( & stats_tx , 0U , sizeof ( struct aq_ring_stats_tx_s ) ) ;
aq_vec_add_stats ( self , & stats_rx , & stats_tx ) ;
2017-10-19 18:23:54 +03:00
/* This data should mimic aq_ethtool_queue_stat_names structure
*/
2017-01-23 22:09:14 -08:00
data [ count ] + = stats_rx . packets ;
data [ + + count ] + = stats_tx . packets ;
2017-10-19 18:23:54 +03:00
data [ + + count ] + = stats_tx . queue_restarts ;
2017-01-23 22:09:14 -08:00
data [ + + count ] + = stats_rx . jumbo_packets ;
data [ + + count ] + = stats_rx . lro_packets ;
data [ + + count ] + = stats_rx . errors ;
if ( p_count )
* p_count = + + count ;
return 0 ;
}