Commit 4f75da36 authored by Tal Gilboa's avatar Tal Gilboa Committed by Saeed Mahameed

linux/dim: Move implementation to .c files

Moved all logic from dim.h and net_dim.h to dim.c and net_dim.c.
This is both more structurally appealing and would allow to only
expose externally used functions.
Signed-off-by: default avatarTal Gilboa <talgi@mellanox.com>
Signed-off-by: default avatarSaeed Mahameed <saeedm@mellanox.com>
parent 8960b389
...@@ -5588,8 +5588,8 @@ F: include/linux/dynamic_debug.h ...@@ -5588,8 +5588,8 @@ F: include/linux/dynamic_debug.h
DYNAMIC INTERRUPT MODERATION DYNAMIC INTERRUPT MODERATION
M: Tal Gilboa <talgi@mellanox.com> M: Tal Gilboa <talgi@mellanox.com>
S: Maintained S: Maintained
F: include/linux/net_dim.h
F: include/linux/dim.h F: include/linux/dim.h
F: lib/dim/
DZ DECSTATION DZ11 SERIAL DRIVER DZ DECSTATION DZ11 SERIAL DRIVER
M: "Maciej W. Rozycki" <macro@linux-mips.org> M: "Maciej W. Rozycki" <macro@linux-mips.org>
......
...@@ -8,6 +8,7 @@ config NET_VENDOR_BROADCOM ...@@ -8,6 +8,7 @@ config NET_VENDOR_BROADCOM
default y default y
depends on (SSB_POSSIBLE && HAS_DMA) || PCI || BCM63XX || \ depends on (SSB_POSSIBLE && HAS_DMA) || PCI || BCM63XX || \
SIBYTE_SB1xxx_SOC SIBYTE_SB1xxx_SOC
select DIMLIB
---help--- ---help---
If you have a network (Ethernet) chipset belonging to this class, If you have a network (Ethernet) chipset belonging to this class,
say Y. say Y.
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/net_dim.h> #include <linux/dim.h>
/* Receive/transmit descriptor format */ /* Receive/transmit descriptor format */
#define DESC_ADDR_HI_STATUS_LEN 0x00 #define DESC_ADDR_HI_STATUS_LEN 0x00
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#include <net/devlink.h> #include <net/devlink.h>
#include <net/dst_metadata.h> #include <net/dst_metadata.h>
#include <net/xdp.h> #include <net/xdp.h>
#include <linux/net_dim.h> #include <linux/dim.h>
struct tx_bd { struct tx_bd {
__le32 tx_bd_len_flags_type; __le32 tx_bd_len_flags_type;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include "bnxt_hsi.h" #include "bnxt_hsi.h"
#include <linux/net_dim.h> #include <linux/dim.h>
#include "bnxt.h" #include "bnxt.h"
#include "bnxt_debugfs.h" #include "bnxt_debugfs.h"
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* the Free Software Foundation. * the Free Software Foundation.
*/ */
#include <linux/net_dim.h> #include <linux/dim.h>
#include "bnxt_hsi.h" #include "bnxt_hsi.h"
#include "bnxt.h" #include "bnxt.h"
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/net_dim.h> #include <linux/dim.h>
/* total number of Buffer Descriptors, same for Rx/Tx */ /* total number of Buffer Descriptors, same for Rx/Tx */
#define TOTAL_DESC 256 #define TOTAL_DESC 256
......
...@@ -34,6 +34,7 @@ config MLX5_CORE_EN ...@@ -34,6 +34,7 @@ config MLX5_CORE_EN
depends on NETDEVICES && ETHERNET && INET && PCI && MLX5_CORE depends on NETDEVICES && ETHERNET && INET && PCI && MLX5_CORE
depends on IPV6=y || IPV6=n || MLX5_CORE=m depends on IPV6=y || IPV6=n || MLX5_CORE=m
select PAGE_POOL select PAGE_POOL
select DIMLIB
default n default n
---help--- ---help---
Ethernet support in Mellanox Technologies ConnectX-4 NIC. Ethernet support in Mellanox Technologies ConnectX-4 NIC.
......
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
#include <linux/rhashtable.h> #include <linux/rhashtable.h>
#include <net/switchdev.h> #include <net/switchdev.h>
#include <net/xdp.h> #include <net/xdp.h>
#include <linux/net_dim.h> #include <linux/dim.h>
#include <linux/bits.h> #include <linux/bits.h>
#include "wq.h" #include "wq.h"
#include "mlx5_core.h" #include "mlx5_core.h"
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include <linux/net_dim.h> #include <linux/dim.h>
#include "en.h" #include "en.h"
static void static void
......
...@@ -6,20 +6,49 @@ ...@@ -6,20 +6,49 @@
#include <linux/module.h> #include <linux/module.h>
/**
* Number of events between DIM iterations.
* Causes a moderation of the algorithm run.
*/
#define DIM_NEVENTS 64 #define DIM_NEVENTS 64
/* more than 10% difference */ /**
* Is a difference between values justifies taking an action.
* We consider 10% difference as significant.
*/
#define IS_SIGNIFICANT_DIFF(val, ref) \ #define IS_SIGNIFICANT_DIFF(val, ref) \
(((100UL * abs((val) - (ref))) / (ref)) > 10) (((100UL * abs((val) - (ref))) / (ref)) > 10)
/**
* Calculate the gap between two values.
* Take wrap-around and variable size into consideration.
*/
#define BIT_GAP(bits, end, start) ((((end) - (start)) + BIT_ULL(bits)) \ #define BIT_GAP(bits, end, start) ((((end) - (start)) + BIT_ULL(bits)) \
& (BIT_ULL(bits) - 1)) & (BIT_ULL(bits) - 1))
/**
* Structure for CQ moderation values.
* Used for communications between DIM and its consumer.
*
* @usec: CQ timer suggestion (by DIM)
* @pkts: CQ packet counter suggestion (by DIM)
* @cq_period_mode: CQ priod count mode (from CQE/EQE)
*/
struct dim_cq_moder { struct dim_cq_moder {
u16 usec; u16 usec;
u16 pkts; u16 pkts;
u8 cq_period_mode; u8 cq_period_mode;
}; };
/**
* Structure for DIM sample data.
* Used for communications between DIM and its consumer.
*
* @time: Sample timestamp
* @pkt_ctr: Number of packets
* @byte_ctr: Number of bytes
* @event_ctr: Number of events
*/
struct dim_sample { struct dim_sample {
ktime_t time; ktime_t time;
u32 pkt_ctr; u32 pkt_ctr;
...@@ -27,13 +56,36 @@ struct dim_sample { ...@@ -27,13 +56,36 @@ struct dim_sample {
u16 event_ctr; u16 event_ctr;
}; };
/**
* Structure for DIM stats.
* Used for holding current measured rates.
*
* @ppms: Packets per msec
* @bpms: Bytes per msec
* @epms: Events per msec
*/
struct dim_stats { struct dim_stats {
int ppms; /* packets per msec */ int ppms;
int bpms; /* bytes per msec */ int bpms;
int epms; /* events per msec */ int epms;
}; };
struct dim { /* Dynamic Interrupt Moderation */ /**
* Main structure for dynamic interrupt moderation (DIM).
* Used for holding all information about a specific DIM instance.
*
* @state: Algorithm state (see below)
* @prev_stats: Measured rates from previous iteration (for comparison)
* @start_sample: Sampled data at start of current iteration
* @work: Work to perform on action required
* @profile_ix: Current moderation profile
* @mode: CQ period count mode
* @tune_state: Algorithm tuning state (see below)
* @steps_right: Number of steps taken towards higher moderation
* @steps_left: Number of steps taken towards lower moderation
* @tired: Parking depth counter
*/
struct dim {
u8 state; u8 state;
struct dim_stats prev_stats; struct dim_stats prev_stats;
struct dim_sample start_sample; struct dim_sample start_sample;
...@@ -46,18 +98,49 @@ struct dim { /* Dynamic Interrupt Moderation */ ...@@ -46,18 +98,49 @@ struct dim { /* Dynamic Interrupt Moderation */
u8 tired; u8 tired;
}; };
/**
* enum dim_cq_period_mode
*
* These are the modes for CQ period count.
*
* @DIM_CQ_PERIOD_MODE_START_FROM_EQE: Start counting from EQE
* @DIM_CQ_PERIOD_MODE_START_FROM_CQE: Start counting from CQE (implies timer reset)
* @DIM_CQ_PERIOD_NUM_MODES: Number of modes
*/
enum { enum {
DIM_CQ_PERIOD_MODE_START_FROM_EQE = 0x0, DIM_CQ_PERIOD_MODE_START_FROM_EQE = 0x0,
DIM_CQ_PERIOD_MODE_START_FROM_CQE = 0x1, DIM_CQ_PERIOD_MODE_START_FROM_CQE = 0x1,
DIM_CQ_PERIOD_NUM_MODES DIM_CQ_PERIOD_NUM_MODES
}; };
/**
* enum dim_state
*
* These are the DIM algorithm states.
* These will determine if the algorithm is in a valid state to start an iteration.
*
* @DIM_START_MEASURE: This is the first iteration (also after applying a new profile)
* @DIM_MEASURE_IN_PROGRESS: Algorithm is already in progress - check if
* need to perform an action
* @DIM_APPLY_NEW_PROFILE: DIM consumer is currently applying a profile - no need to measure
*/
enum { enum {
DIM_START_MEASURE, DIM_START_MEASURE,
DIM_MEASURE_IN_PROGRESS, DIM_MEASURE_IN_PROGRESS,
DIM_APPLY_NEW_PROFILE, DIM_APPLY_NEW_PROFILE,
}; };
/**
* enum dim_tune_state
*
* These are the DIM algorithm tune states.
* These will determine which action the algorithm should perform.
*
* @DIM_PARKING_ON_TOP: Algorithm found a local top point - exit on significant difference
* @DIM_PARKING_TIRED: Algorithm found a deep top point - don't exit if tired > 0
* @DIM_GOING_RIGHT: Algorithm is currently trying higher moderation levels
* @DIM_GOING_LEFT: Algorithm is currently trying lower moderation levels
*/
enum { enum {
DIM_PARKING_ON_TOP, DIM_PARKING_ON_TOP,
DIM_PARKING_TIRED, DIM_PARKING_TIRED,
...@@ -65,63 +148,95 @@ enum { ...@@ -65,63 +148,95 @@ enum {
DIM_GOING_LEFT, DIM_GOING_LEFT,
}; };
/**
* enum dim_stats_state
*
* These are the DIM algorithm statistics states.
* These will determine the verdict of current iteration.
*
* @DIM_STATS_WORSE: Current iteration shows worse performance than before
* @DIM_STATS_WORSE: Current iteration shows same performance than before
* @DIM_STATS_WORSE: Current iteration shows better performance than before
*/
enum { enum {
DIM_STATS_WORSE, DIM_STATS_WORSE,
DIM_STATS_SAME, DIM_STATS_SAME,
DIM_STATS_BETTER, DIM_STATS_BETTER,
}; };
/**
* enum dim_step_result
*
* These are the DIM algorithm step results.
* These describe the result of a step.
*
* @DIM_STEPPED: Performed a regular step
* @DIM_TOO_TIRED: Same kind of step was done multiple times - should go to
* tired parking
* @DIM_ON_EDGE: Stepped to the most left/right profile
*/
enum { enum {
DIM_STEPPED, DIM_STEPPED,
DIM_TOO_TIRED, DIM_TOO_TIRED,
DIM_ON_EDGE, DIM_ON_EDGE,
}; };
static inline bool dim_on_top(struct dim *dim) /**
{ * dim_on_top - check if current state is a good place to stop (top location)
switch (dim->tune_state) { * @dim: DIM context
case DIM_PARKING_ON_TOP: *
case DIM_PARKING_TIRED: * Check if current profile is a good place to park at.
return true; * This will result in reducing the DIM checks frequency as we assume we
case DIM_GOING_RIGHT: * shouldn't probably change profiles, unless traffic pattern wasn't changed.
return (dim->steps_left > 1) && (dim->steps_right == 1); */
default: /* DIM_GOING_LEFT */ bool dim_on_top(struct dim *dim);
return (dim->steps_right > 1) && (dim->steps_left == 1);
}
}
static inline void dim_turn(struct dim *dim) /**
{ * dim_turn - change profile alterning direction
switch (dim->tune_state) { * @dim: DIM context
case DIM_PARKING_ON_TOP: *
case DIM_PARKING_TIRED: * Go left if we were going right and vice-versa.
break; * Do nothing if currently parking.
case DIM_GOING_RIGHT: */
dim->tune_state = DIM_GOING_LEFT; void dim_turn(struct dim *dim);
dim->steps_left = 0;
break;
case DIM_GOING_LEFT:
dim->tune_state = DIM_GOING_RIGHT;
dim->steps_right = 0;
break;
}
}
static inline void dim_park_on_top(struct dim *dim) /**
{ * dim_park_on_top - enter a parking state on a top location
dim->steps_right = 0; * @dim: DIM context
dim->steps_left = 0; *
dim->tired = 0; * Enter parking state.
dim->tune_state = DIM_PARKING_ON_TOP; * Clear all movement history.
} */
void dim_park_on_top(struct dim *dim);
static inline void dim_park_tired(struct dim *dim) /**
{ * dim_park_tired - enter a tired parking state
dim->steps_right = 0; * @dim: DIM context
dim->steps_left = 0; *
dim->tune_state = DIM_PARKING_TIRED; * Enter parking state.
} * Clear all movement history and cause DIM checks frequency to reduce.
*/
void dim_park_tired(struct dim *dim);
/**
* dim_calc_stats - calculate the difference between two samples
* @start: start sample
* @end: end sample
* @curr_stats: delta between samples
*
* Calculate the delta between two samples (in data rates).
* Takes into consideration counter wrap-around.
*/
void dim_calc_stats(struct dim_sample *start, struct dim_sample *end,
struct dim_stats *curr_stats);
/**
* dim_update_sample - set a sample's fields with give values
* @event_ctr: number of events to set
* @packets: number of packets to set
* @bytes: number of bytes to set
* @s: DIM sample
*/
static inline void static inline void
dim_update_sample(u16 event_ctr, u64 packets, u64 bytes, struct dim_sample *s) dim_update_sample(u16 event_ctr, u64 packets, u64 bytes, struct dim_sample *s)
{ {
...@@ -131,23 +246,99 @@ dim_update_sample(u16 event_ctr, u64 packets, u64 bytes, struct dim_sample *s) ...@@ -131,23 +246,99 @@ dim_update_sample(u16 event_ctr, u64 packets, u64 bytes, struct dim_sample *s)
s->event_ctr = event_ctr; s->event_ctr = event_ctr;
} }
static inline void /* Net DIM */
dim_calc_stats(struct dim_sample *start, struct dim_sample *end,
struct dim_stats *curr_stats) /*
{ * Net DIM profiles:
/* u32 holds up to 71 minutes, should be enough */ * There are different set of profiles for each CQ period mode.
u32 delta_us = ktime_us_delta(end->time, start->time); * There are different set of profiles for RX/TX CQs.
u32 npkts = BIT_GAP(BITS_PER_TYPE(u32), end->pkt_ctr, start->pkt_ctr); * Each profile size must be of NET_DIM_PARAMS_NUM_PROFILES
u32 nbytes = BIT_GAP(BITS_PER_TYPE(u32), end->byte_ctr, */
start->byte_ctr); #define NET_DIM_PARAMS_NUM_PROFILES 5
#define NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE 256
if (!delta_us) #define NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE 128
return; #define NET_DIM_DEF_PROFILE_CQE 1
#define NET_DIM_DEF_PROFILE_EQE 1
curr_stats->ppms = DIV_ROUND_UP(npkts * USEC_PER_MSEC, delta_us);
curr_stats->bpms = DIV_ROUND_UP(nbytes * USEC_PER_MSEC, delta_us); #define NET_DIM_RX_EQE_PROFILES { \
curr_stats->epms = DIV_ROUND_UP(DIM_NEVENTS * USEC_PER_MSEC, {1, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
delta_us); {8, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
{64, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
{128, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
{256, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
} }
#define NET_DIM_RX_CQE_PROFILES { \
{2, 256}, \
{8, 128}, \
{16, 64}, \
{32, 64}, \
{64, 64} \
}
#define NET_DIM_TX_EQE_PROFILES { \
{1, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
{8, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
{32, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
{64, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
{128, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE} \
}
#define NET_DIM_TX_CQE_PROFILES { \
{5, 128}, \
{8, 64}, \
{16, 32}, \
{32, 32}, \
{64, 32} \
}
static const struct dim_cq_moder
rx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
NET_DIM_RX_EQE_PROFILES,
NET_DIM_RX_CQE_PROFILES,
};
static const struct dim_cq_moder
tx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
NET_DIM_TX_EQE_PROFILES,
NET_DIM_TX_CQE_PROFILES,
};
/**
* net_dim_get_rx_moderation - provide a CQ moderation object for the given RX profile
* @cq_period_mode: CQ period mode
* @ix: Profile index
*/
struct dim_cq_moder net_dim_get_rx_moderation(u8 cq_period_mode, int ix);
/**
* net_dim_get_def_rx_moderation - provide the default RX moderation
* @cq_period_mode: CQ period mode
*/
struct dim_cq_moder net_dim_get_def_rx_moderation(u8 cq_period_mode);
/**
* net_dim_get_tx_moderation - provide a CQ moderation object for the given TX profile
* @cq_period_mode: CQ period mode
* @ix: Profile index
*/
struct dim_cq_moder net_dim_get_tx_moderation(u8 cq_period_mode, int ix);
/**
* net_dim_get_def_tx_moderation - provide the default TX moderation
* @cq_period_mode: CQ period mode
*/
struct dim_cq_moder net_dim_get_def_tx_moderation(u8 cq_period_mode);
/**
* net_dim - main DIM algorithm entry point
* @dim: DIM instance information
* @end_sample: Current data measurement
*
* Called by the consumer.
* This is the main logic of the algorithm, where data is processed in order to decide on next
* required action.
*/
void net_dim(struct dim *dim, struct dim_sample end_sample);
#endif /* DIM_H */ #endif /* DIM_H */
...@@ -562,6 +562,14 @@ config SIGNATURE ...@@ -562,6 +562,14 @@ config SIGNATURE
Digital signature verification. Currently only RSA is supported. Digital signature verification. Currently only RSA is supported.
Implementation is done using GnuPG MPI library Implementation is done using GnuPG MPI library
config DIMLIB
bool "DIM library"
default y
help
Dynamic Interrupt Moderation library.
Implements an algorithm for dynamically change CQ modertion values
according to run time performance.
# #
# libfdt files, only selected if needed. # libfdt files, only selected if needed.
# #
......
...@@ -202,6 +202,7 @@ obj-$(CONFIG_GLOB) += glob.o ...@@ -202,6 +202,7 @@ obj-$(CONFIG_GLOB) += glob.o
obj-$(CONFIG_GLOB_SELFTEST) += globtest.o obj-$(CONFIG_GLOB_SELFTEST) += globtest.o
obj-$(CONFIG_MPILIB) += mpi/ obj-$(CONFIG_MPILIB) += mpi/
obj-$(CONFIG_DIMLIB) += dim/
obj-$(CONFIG_SIGNATURE) += digsig.o obj-$(CONFIG_SIGNATURE) += digsig.o
lib-$(CONFIG_CLZ_TAB) += clz_tab.o lib-$(CONFIG_CLZ_TAB) += clz_tab.o
......
#
# DIM Dynamic Interrupt Moderation library
#
obj-$(CONFIG_DIMLIB) = net_dim.o
net_dim-y = \
dim.o \
net_dim.o
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2019, Mellanox Technologies inc. All rights reserved.
*/
#include <linux/dim.h>
bool dim_on_top(struct dim *dim)
{
switch (dim->tune_state) {
case DIM_PARKING_ON_TOP:
case DIM_PARKING_TIRED:
return true;
case DIM_GOING_RIGHT:
return (dim->steps_left > 1) && (dim->steps_right == 1);
default: /* DIM_GOING_LEFT */
return (dim->steps_right > 1) && (dim->steps_left == 1);
}
}
EXPORT_SYMBOL(dim_on_top);
void dim_turn(struct dim *dim)
{
switch (dim->tune_state) {
case DIM_PARKING_ON_TOP:
case DIM_PARKING_TIRED:
break;
case DIM_GOING_RIGHT:
dim->tune_state = DIM_GOING_LEFT;
dim->steps_left = 0;
break;
case DIM_GOING_LEFT:
dim->tune_state = DIM_GOING_RIGHT;
dim->steps_right = 0;
break;
}
}
EXPORT_SYMBOL(dim_turn);
void dim_park_on_top(struct dim *dim)
{
dim->steps_right = 0;
dim->steps_left = 0;
dim->tired = 0;
dim->tune_state = DIM_PARKING_ON_TOP;
}
EXPORT_SYMBOL(dim_park_on_top);
void dim_park_tired(struct dim *dim)
{
dim->steps_right = 0;
dim->steps_left = 0;
dim->tune_state = DIM_PARKING_TIRED;
}
EXPORT_SYMBOL(dim_park_tired);
void dim_calc_stats(struct dim_sample *start, struct dim_sample *end,
struct dim_stats *curr_stats)
{
/* u32 holds up to 71 minutes, should be enough */
u32 delta_us = ktime_us_delta(end->time, start->time);
u32 npkts = BIT_GAP(BITS_PER_TYPE(u32), end->pkt_ctr, start->pkt_ctr);
u32 nbytes = BIT_GAP(BITS_PER_TYPE(u32), end->byte_ctr,
start->byte_ctr);
if (!delta_us)
return;
curr_stats->ppms = DIV_ROUND_UP(npkts * USEC_PER_MSEC, delta_us);
curr_stats->bpms = DIV_ROUND_UP(nbytes * USEC_PER_MSEC, delta_us);
curr_stats->epms = DIV_ROUND_UP(DIM_NEVENTS * USEC_PER_MSEC,
delta_us);
}
EXPORT_SYMBOL(dim_calc_stats);
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* /*
* Copyright (c) 2016, Mellanox Technologies. All rights reserved. * Copyright (c) 2018, Mellanox Technologies inc. All rights reserved.
* Copyright (c) 2017-2018, Broadcom Limited. 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.
*/ */
#ifndef NET_DIM_H
#define NET_DIM_H
#include <linux/module.h>
#include <linux/dim.h> #include <linux/dim.h>
#define NET_DIM_PARAMS_NUM_PROFILES 5 struct dim_cq_moder
/* Netdev dynamic interrupt moderation profiles */
#define NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE 256
#define NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE 128
#define NET_DIM_DEF_PROFILE_CQE 1
#define NET_DIM_DEF_PROFILE_EQE 1
/* All profiles sizes must be NET_PARAMS_DIM_NUM_PROFILES */
#define NET_DIM_RX_EQE_PROFILES { \
{1, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
{8, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
{64, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
{128, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
{256, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
}
#define NET_DIM_RX_CQE_PROFILES { \
{2, 256}, \
{8, 128}, \
{16, 64}, \
{32, 64}, \
{64, 64} \
}
#define NET_DIM_TX_EQE_PROFILES { \
{1, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
{8, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
{32, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
{64, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
{128, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE} \
}
#define NET_DIM_TX_CQE_PROFILES { \
{5, 128}, \
{8, 64}, \
{16, 32}, \
{32, 32}, \
{64, 32} \
}
static const struct dim_cq_moder
rx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
NET_DIM_RX_EQE_PROFILES,
NET_DIM_RX_CQE_PROFILES,
};
static const struct dim_cq_moder
tx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
NET_DIM_TX_EQE_PROFILES,
NET_DIM_TX_CQE_PROFILES,
};
static inline struct dim_cq_moder
net_dim_get_rx_moderation(u8 cq_period_mode, int ix) net_dim_get_rx_moderation(u8 cq_period_mode, int ix)
{ {
struct dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix]; struct dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix];
...@@ -97,8 +13,9 @@ net_dim_get_rx_moderation(u8 cq_period_mode, int ix) ...@@ -97,8 +13,9 @@ net_dim_get_rx_moderation(u8 cq_period_mode, int ix)
cq_moder.cq_period_mode = cq_period_mode; cq_moder.cq_period_mode = cq_period_mode;
return cq_moder; return cq_moder;
} }
EXPORT_SYMBOL(net_dim_get_rx_moderation);
static inline struct dim_cq_moder struct dim_cq_moder
net_dim_get_def_rx_moderation(u8 cq_period_mode) net_dim_get_def_rx_moderation(u8 cq_period_mode)
{ {
u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ? u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
...@@ -106,8 +23,9 @@ net_dim_get_def_rx_moderation(u8 cq_period_mode) ...@@ -106,8 +23,9 @@ net_dim_get_def_rx_moderation(u8 cq_period_mode)
return net_dim_get_rx_moderation(cq_period_mode, profile_ix); return net_dim_get_rx_moderation(cq_period_mode, profile_ix);
} }
EXPORT_SYMBOL(net_dim_get_def_rx_moderation);
static inline struct dim_cq_moder struct dim_cq_moder
net_dim_get_tx_moderation(u8 cq_period_mode, int ix) net_dim_get_tx_moderation(u8 cq_period_mode, int ix)
{ {
struct dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix]; struct dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix];
...@@ -115,8 +33,9 @@ net_dim_get_tx_moderation(u8 cq_period_mode, int ix) ...@@ -115,8 +33,9 @@ net_dim_get_tx_moderation(u8 cq_period_mode, int ix)
cq_moder.cq_period_mode = cq_period_mode; cq_moder.cq_period_mode = cq_period_mode;
return cq_moder; return cq_moder;
} }
EXPORT_SYMBOL(net_dim_get_tx_moderation);
static inline struct dim_cq_moder struct dim_cq_moder
net_dim_get_def_tx_moderation(u8 cq_period_mode) net_dim_get_def_tx_moderation(u8 cq_period_mode)
{ {
u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ? u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
...@@ -124,8 +43,9 @@ net_dim_get_def_tx_moderation(u8 cq_period_mode) ...@@ -124,8 +43,9 @@ net_dim_get_def_tx_moderation(u8 cq_period_mode)
return net_dim_get_tx_moderation(cq_period_mode, profile_ix); return net_dim_get_tx_moderation(cq_period_mode, profile_ix);
} }
EXPORT_SYMBOL(net_dim_get_def_tx_moderation);
static inline int net_dim_step(struct dim *dim) static int net_dim_step(struct dim *dim)
{ {
if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2)) if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2))
return DIM_TOO_TIRED; return DIM_TOO_TIRED;
...@@ -152,19 +72,17 @@ static inline int net_dim_step(struct dim *dim) ...@@ -152,19 +72,17 @@ static inline int net_dim_step(struct dim *dim)
return DIM_STEPPED; return DIM_STEPPED;
} }
static inline void net_dim_exit_parking(struct dim *dim) static void net_dim_exit_parking(struct dim *dim)
{ {
dim->tune_state = dim->profile_ix ? DIM_GOING_LEFT : dim->tune_state = dim->profile_ix ? DIM_GOING_LEFT : DIM_GOING_RIGHT;
DIM_GOING_RIGHT;
net_dim_step(dim); net_dim_step(dim);
} }
static inline int net_dim_stats_compare(struct dim_stats *curr, static int net_dim_stats_compare(struct dim_stats *curr,
struct dim_stats *prev) struct dim_stats *prev)
{ {
if (!prev->bpms) if (!prev->bpms)
return curr->bpms ? DIM_STATS_BETTER : return curr->bpms ? DIM_STATS_BETTER : DIM_STATS_SAME;
DIM_STATS_SAME;
if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms)) if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms))
return (curr->bpms > prev->bpms) ? DIM_STATS_BETTER : return (curr->bpms > prev->bpms) ? DIM_STATS_BETTER :
...@@ -188,8 +106,7 @@ static inline int net_dim_stats_compare(struct dim_stats *curr, ...@@ -188,8 +106,7 @@ static inline int net_dim_stats_compare(struct dim_stats *curr,
return DIM_STATS_SAME; return DIM_STATS_SAME;
} }
static inline bool net_dim_decision(struct dim_stats *curr_stats, static bool net_dim_decision(struct dim_stats *curr_stats, struct dim *dim)
struct dim *dim)
{ {
int prev_state = dim->tune_state; int prev_state = dim->tune_state;
int prev_ix = dim->profile_ix; int prev_ix = dim->profile_ix;
...@@ -198,7 +115,8 @@ static inline bool net_dim_decision(struct dim_stats *curr_stats, ...@@ -198,7 +115,8 @@ static inline bool net_dim_decision(struct dim_stats *curr_stats,
switch (dim->tune_state) { switch (dim->tune_state) {
case DIM_PARKING_ON_TOP: case DIM_PARKING_ON_TOP:
stats_res = net_dim_stats_compare(curr_stats, &dim->prev_stats); stats_res = net_dim_stats_compare(curr_stats,
&dim->prev_stats);
if (stats_res != DIM_STATS_SAME) if (stats_res != DIM_STATS_SAME)
net_dim_exit_parking(dim); net_dim_exit_parking(dim);
break; break;
...@@ -211,7 +129,8 @@ static inline bool net_dim_decision(struct dim_stats *curr_stats, ...@@ -211,7 +129,8 @@ static inline bool net_dim_decision(struct dim_stats *curr_stats,
case DIM_GOING_RIGHT: case DIM_GOING_RIGHT:
case DIM_GOING_LEFT: case DIM_GOING_LEFT:
stats_res = net_dim_stats_compare(curr_stats, &dim->prev_stats); stats_res = net_dim_stats_compare(curr_stats,
&dim->prev_stats);
if (stats_res != DIM_STATS_BETTER) if (stats_res != DIM_STATS_BETTER)
dim_turn(dim); dim_turn(dim);
...@@ -240,8 +159,7 @@ static inline bool net_dim_decision(struct dim_stats *curr_stats, ...@@ -240,8 +159,7 @@ static inline bool net_dim_decision(struct dim_stats *curr_stats,
return dim->profile_ix != prev_ix; return dim->profile_ix != prev_ix;
} }
static inline void net_dim(struct dim *dim, void net_dim(struct dim *dim, struct dim_sample end_sample)
struct dim_sample end_sample)
{ {
struct dim_stats curr_stats; struct dim_stats curr_stats;
u16 nevents; u16 nevents;
...@@ -269,5 +187,4 @@ static inline void net_dim(struct dim *dim, ...@@ -269,5 +187,4 @@ static inline void net_dim(struct dim *dim,
break; break;
} }
} }
EXPORT_SYMBOL(net_dim);
#endif /* NET_DIM_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment