cxgb4: Add capability to get/set SGE Doorbell Queue Timer Tick
This patch gets/sets SGE Doorbell Queue timer ticks via ethtool Original work by: Casey Leedom <leedom@chelsio.com> Signed-off-by: Vishal Kulkarni <vishal@chelsio.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d429005fdf
commit
543a1b85e7
|
@ -819,6 +819,7 @@ struct sge {
|
|||
u16 nqs_per_uld; /* # of Rx queues per ULD */
|
||||
u16 timer_val[SGE_NTIMERS];
|
||||
u8 counter_val[SGE_NCOUNTERS];
|
||||
u16 dbqtimer_tick;
|
||||
u16 dbqtimer_val[SGE_NDBQTIMERS];
|
||||
u32 fl_pg_order; /* large page allocation size */
|
||||
u32 stat_len; /* length of status page at ring end */
|
||||
|
|
|
@ -932,11 +932,190 @@ static int get_adaptive_rx_setting(struct net_device *dev)
|
|||
return q->rspq.adaptive_rx;
|
||||
}
|
||||
|
||||
static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
|
||||
/* Return the current global Adapter SGE Doorbell Queue Timer Tick for all
|
||||
* Ethernet TX Queues.
|
||||
*/
|
||||
static int get_dbqtimer_tick(struct net_device *dev)
|
||||
{
|
||||
set_adaptive_rx_setting(dev, c->use_adaptive_rx_coalesce);
|
||||
return set_rx_intr_params(dev, c->rx_coalesce_usecs,
|
||||
c->rx_max_coalesced_frames);
|
||||
struct port_info *pi = netdev_priv(dev);
|
||||
struct adapter *adap = pi->adapter;
|
||||
|
||||
if (!(adap->flags & SGE_DBQ_TIMER))
|
||||
return 0;
|
||||
|
||||
return adap->sge.dbqtimer_tick;
|
||||
}
|
||||
|
||||
/* Return the SGE Doorbell Queue Timer Value for the Ethernet TX Queues
|
||||
* associated with a Network Device.
|
||||
*/
|
||||
static int get_dbqtimer(struct net_device *dev)
|
||||
{
|
||||
struct port_info *pi = netdev_priv(dev);
|
||||
struct adapter *adap = pi->adapter;
|
||||
struct sge_eth_txq *txq;
|
||||
|
||||
txq = &adap->sge.ethtxq[pi->first_qset];
|
||||
|
||||
if (!(adap->flags & SGE_DBQ_TIMER))
|
||||
return 0;
|
||||
|
||||
/* all of the TX Queues use the same Timer Index */
|
||||
return adap->sge.dbqtimer_val[txq->dbqtimerix];
|
||||
}
|
||||
|
||||
/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX
|
||||
* Queues. This is the fundamental "Tick" that sets the scale of values which
|
||||
* can be used. Individual Ethernet TX Queues index into a relatively small
|
||||
* array of Tick Multipliers. Changing the base Tick will thus change all of
|
||||
* the resulting Timer Values associated with those multipliers for all
|
||||
* Ethernet TX Queues.
|
||||
*/
|
||||
static int set_dbqtimer_tick(struct net_device *dev, int usecs)
|
||||
{
|
||||
struct port_info *pi = netdev_priv(dev);
|
||||
struct adapter *adap = pi->adapter;
|
||||
struct sge *s = &adap->sge;
|
||||
u32 param, val;
|
||||
int ret;
|
||||
|
||||
if (!(adap->flags & SGE_DBQ_TIMER))
|
||||
return 0;
|
||||
|
||||
/* return early if it's the same Timer Tick we're already using */
|
||||
if (s->dbqtimer_tick == usecs)
|
||||
return 0;
|
||||
|
||||
/* attempt to set the new Timer Tick value */
|
||||
param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
|
||||
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DBQ_TIMERTICK));
|
||||
val = usecs;
|
||||
ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1, ¶m, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
s->dbqtimer_tick = usecs;
|
||||
|
||||
/* if successful, reread resulting dependent Timer values */
|
||||
ret = t4_read_sge_dbqtimers(adap, ARRAY_SIZE(s->dbqtimer_val),
|
||||
s->dbqtimer_val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set the SGE Doorbell Queue Timer Value for the Ethernet TX Queues
|
||||
* associated with a Network Device. There is a relatively small array of
|
||||
* possible Timer Values so we need to pick the closest value available.
|
||||
*/
|
||||
static int set_dbqtimer(struct net_device *dev, int usecs)
|
||||
{
|
||||
int qix, timerix, min_timerix, delta, min_delta;
|
||||
struct port_info *pi = netdev_priv(dev);
|
||||
struct adapter *adap = pi->adapter;
|
||||
struct sge *s = &adap->sge;
|
||||
struct sge_eth_txq *txq;
|
||||
u32 param, val;
|
||||
int ret;
|
||||
|
||||
if (!(adap->flags & SGE_DBQ_TIMER))
|
||||
return 0;
|
||||
|
||||
/* Find the SGE Doorbell Timer Value that's closest to the requested
|
||||
* value.
|
||||
*/
|
||||
min_delta = INT_MAX;
|
||||
min_timerix = 0;
|
||||
for (timerix = 0; timerix < ARRAY_SIZE(s->dbqtimer_val); timerix++) {
|
||||
delta = s->dbqtimer_val[timerix] - usecs;
|
||||
if (delta < 0)
|
||||
delta = -delta;
|
||||
if (delta < min_delta) {
|
||||
min_delta = delta;
|
||||
min_timerix = timerix;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return early if it's the same Timer Index we're already using.
|
||||
* We use the same Timer Index for all of the TX Queues for an
|
||||
* interface so it's only necessary to check the first one.
|
||||
*/
|
||||
txq = &s->ethtxq[pi->first_qset];
|
||||
if (txq->dbqtimerix == min_timerix)
|
||||
return 0;
|
||||
|
||||
for (qix = 0; qix < pi->nqsets; qix++, txq++) {
|
||||
if (adap->flags & FULL_INIT_DONE) {
|
||||
param =
|
||||
(FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
|
||||
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DMAQ_EQ_TIMERIX) |
|
||||
FW_PARAMS_PARAM_YZ_V(txq->q.cntxt_id));
|
||||
val = min_timerix;
|
||||
ret = t4_set_params(adap, adap->mbox, adap->pf, 0,
|
||||
1, ¶m, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
txq->dbqtimerix = min_timerix;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX
|
||||
* Queues and the Timer Value for the Ethernet TX Queues associated with a
|
||||
* Network Device. Since changing the global Tick changes all of the
|
||||
* available Timer Values, we need to do this first before selecting the
|
||||
* resulting closest Timer Value. Moreover, since the Tick is global,
|
||||
* changing it affects the Timer Values for all Network Devices on the
|
||||
* adapter. So, before changing the Tick, we grab all of the current Timer
|
||||
* Values for other Network Devices on this Adapter and then attempt to select
|
||||
* new Timer Values which are close to the old values ...
|
||||
*/
|
||||
static int set_dbqtimer_tickval(struct net_device *dev,
|
||||
int tick_usecs, int timer_usecs)
|
||||
{
|
||||
struct port_info *pi = netdev_priv(dev);
|
||||
struct adapter *adap = pi->adapter;
|
||||
int timer[MAX_NPORTS];
|
||||
unsigned int port;
|
||||
int ret;
|
||||
|
||||
/* Grab the other adapter Network Interface current timers and fill in
|
||||
* the new one for this Network Interface.
|
||||
*/
|
||||
for_each_port(adap, port)
|
||||
if (port == pi->port_id)
|
||||
timer[port] = timer_usecs;
|
||||
else
|
||||
timer[port] = get_dbqtimer(adap->port[port]);
|
||||
|
||||
/* Change the global Tick first ... */
|
||||
ret = set_dbqtimer_tick(dev, tick_usecs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* ... and then set all of the Network Interface Timer Values ... */
|
||||
for_each_port(adap, port) {
|
||||
ret = set_dbqtimer(adap->port[port], timer[port]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_coalesce(struct net_device *dev,
|
||||
struct ethtool_coalesce *coalesce)
|
||||
{
|
||||
int ret;
|
||||
|
||||
set_adaptive_rx_setting(dev, coalesce->use_adaptive_rx_coalesce);
|
||||
|
||||
ret = set_rx_intr_params(dev, coalesce->rx_coalesce_usecs,
|
||||
coalesce->rx_max_coalesced_frames);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return set_dbqtimer_tickval(dev,
|
||||
coalesce->tx_coalesce_usecs_irq,
|
||||
coalesce->tx_coalesce_usecs);
|
||||
}
|
||||
|
||||
static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
|
||||
|
@ -949,6 +1128,8 @@ static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
|
|||
c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN_F) ?
|
||||
adap->sge.counter_val[rq->pktcnt_idx] : 0;
|
||||
c->use_adaptive_rx_coalesce = get_adaptive_rx_setting(dev);
|
||||
c->tx_coalesce_usecs_irq = get_dbqtimer_tick(dev);
|
||||
c->tx_coalesce_usecs = get_dbqtimer(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -4331,8 +4331,18 @@ static int adap_init0(struct adapter *adap)
|
|||
/* Grab the SGE Doorbell Queue Timer values. If successful, that
|
||||
* indicates that the Firmware and Hardware support this.
|
||||
*/
|
||||
ret = t4_read_sge_dbqtimers(adap, ARRAY_SIZE(adap->sge.dbqtimer_val),
|
||||
adap->sge.dbqtimer_val);
|
||||
params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
|
||||
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DBQ_TIMERTICK));
|
||||
ret = t4_query_params(adap, adap->mbox, adap->pf, 0,
|
||||
1, params, val);
|
||||
|
||||
if (!ret) {
|
||||
adap->sge.dbqtimer_tick = val[0];
|
||||
ret = t4_read_sge_dbqtimers(adap,
|
||||
ARRAY_SIZE(adap->sge.dbqtimer_val),
|
||||
adap->sge.dbqtimer_val);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
adap->flags |= SGE_DBQ_TIMER;
|
||||
|
||||
|
|
Loading…
Reference in New Issue