From: Andy Gospodarek <go...@broadcom.com> This takes the code that is not generically named to lib/.
This move allows drivers to add private structure elements to track the number of packets, bytes, and interrupts events per ring. A driver also defines a workqueue handler to act on this collected data once per poll and modify the coalescing parameters per ring. Signed-off-by: Andy Gospodarek <go...@broadcom.com> --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c | 1 + .../net/ethernet/mellanox/mlx5/core/net_rx_am.c | 303 -------------------- .../net/ethernet/mellanox/mlx5/core/net_rx_am.h | 107 ------- include/linux/net_rx_am.h | 109 ++++++++ lib/Makefile | 2 +- lib/net_rx_am.c | 306 +++++++++++++++++++++ 8 files changed, 419 insertions(+), 413 deletions(-) delete mode 100644 drivers/net/ethernet/mellanox/mlx5/core/net_rx_am.c delete mode 100644 drivers/net/ethernet/mellanox/mlx5/core/net_rx_am.h create mode 100644 include/linux/net_rx_am.h create mode 100644 lib/net_rx_am.c diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index d5d6d3d..19b21b4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -15,7 +15,7 @@ mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ en_tx.o en_rx.o en_rx_am.o en_txrx.o en_stats.o vxlan.o \ - en_arfs.o en_fs_ethtool.o en_selftest.o net_rx_am.o + en_arfs.o en_fs_ethtool.o en_selftest.o mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 203dc7b..04b36fe 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -49,7 +49,7 @@ #include "wq.h" #include "mlx5_core.h" #include "en_stats.h" -#include "net_rx_am.h" +#include <linux/net_rx_am.h> #define MLX5_SET_CFG(p, f, v) MLX5_SET(create_flow_group_in, p, f, v) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c index 1f8fda1..391f1ba 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c @@ -31,6 +31,7 @@ */ #include "en.h" +#include <linux/net_rx_am.h> void mlx5e_rx_am_work(struct work_struct *work) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/net_rx_am.c b/drivers/net/ethernet/mellanox/mlx5/core/net_rx_am.c deleted file mode 100644 index 37ea6d1..0000000 --- a/drivers/net/ethernet/mellanox/mlx5/core/net_rx_am.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2016, Mellanox Technologies. All rights reserved. - * Copyright (c) 2017, Broadcom Limiited. 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 "en.h" - -#define NET_PARAMS_AM_NUM_PROFILES 5 -/* Adaptive moderation profiles */ -#define NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE 256 -#define NET_RX_AM_DEF_PROFILE_CQE 1 -#define NET_RX_AM_DEF_PROFILE_EQE 1 - -/* All profiles sizes must be NET_PARAMS_AM_NUM_PROFILES */ -#define NET_AM_EQE_PROFILES { \ - {1, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ - {8, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ - {64, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ - {128, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ - {256, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ -} - -#define NET_AM_CQE_PROFILES { \ - {2, 256}, \ - {8, 128}, \ - {16, 64}, \ - {32, 64}, \ - {64, 64} \ -} - -static const struct net_cq_moder -profile[NET_CQ_PERIOD_NUM_MODES][NET_PARAMS_AM_NUM_PROFILES] = { - NET_AM_EQE_PROFILES, - NET_AM_CQE_PROFILES, -}; - -struct net_cq_moder net_am_get_profile(u8 cq_period_mode, int ix) -{ - return profile[cq_period_mode][ix]; -} - -struct net_cq_moder net_am_get_def_profile(u8 rx_cq_period_mode) -{ - int default_profile_ix; - - if (rx_cq_period_mode == NET_CQ_PERIOD_MODE_START_FROM_CQE) - default_profile_ix = NET_RX_AM_DEF_PROFILE_CQE; - else /* NET_CQ_PERIOD_MODE_START_FROM_EQE */ - default_profile_ix = NET_RX_AM_DEF_PROFILE_EQE; - - return profile[rx_cq_period_mode][default_profile_ix]; -} - -static bool net_am_on_top(struct net_rx_am *am) -{ - switch (am->tune_state) { - case NET_AM_PARKING_ON_TOP: - case NET_AM_PARKING_TIRED: - return true; - case NET_AM_GOING_RIGHT: - return (am->steps_left > 1) && (am->steps_right == 1); - default: /* NET_AM_GOING_LEFT */ - return (am->steps_right > 1) && (am->steps_left == 1); - } -} - -static void net_am_turn(struct net_rx_am *am) -{ - switch (am->tune_state) { - case NET_AM_PARKING_ON_TOP: - case NET_AM_PARKING_TIRED: - break; - case NET_AM_GOING_RIGHT: - am->tune_state = NET_AM_GOING_LEFT; - am->steps_left = 0; - break; - case NET_AM_GOING_LEFT: - am->tune_state = NET_AM_GOING_RIGHT; - am->steps_right = 0; - break; - } -} - -static int net_am_step(struct net_rx_am *am) -{ - if (am->tired == (NET_PARAMS_AM_NUM_PROFILES * 2)) - return NET_AM_TOO_TIRED; - - switch (am->tune_state) { - case NET_AM_PARKING_ON_TOP: - case NET_AM_PARKING_TIRED: - break; - case NET_AM_GOING_RIGHT: - if (am->profile_ix == (NET_PARAMS_AM_NUM_PROFILES - 1)) - return NET_AM_ON_EDGE; - am->profile_ix++; - am->steps_right++; - break; - case NET_AM_GOING_LEFT: - if (am->profile_ix == 0) - return NET_AM_ON_EDGE; - am->profile_ix--; - am->steps_left++; - break; - } - - am->tired++; - return NET_AM_STEPPED; -} - -static void net_am_park_on_top(struct net_rx_am *am) -{ - am->steps_right = 0; - am->steps_left = 0; - am->tired = 0; - am->tune_state = NET_AM_PARKING_ON_TOP; -} - -static void net_am_park_tired(struct net_rx_am *am) -{ - am->steps_right = 0; - am->steps_left = 0; - am->tune_state = NET_AM_PARKING_TIRED; -} - -static void net_am_exit_parking(struct net_rx_am *am) -{ - am->tune_state = am->profile_ix ? NET_AM_GOING_LEFT : - NET_AM_GOING_RIGHT; - net_am_step(am); -} - -#define IS_SIGNIFICANT_DIFF(val, ref) \ - (((100 * abs((val) - (ref))) / (ref)) > 10) /* more than 10% difference */ - -static int net_am_stats_compare(struct net_rx_am_stats *curr, - struct net_rx_am_stats *prev) -{ - if (!prev->bpms) - return curr->bpms ? NET_AM_STATS_BETTER : - NET_AM_STATS_SAME; - - if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms)) - return (curr->bpms > prev->bpms) ? NET_AM_STATS_BETTER : - NET_AM_STATS_WORSE; - - if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms)) - return (curr->ppms > prev->ppms) ? NET_AM_STATS_BETTER : - NET_AM_STATS_WORSE; - - if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms)) - return (curr->epms < prev->epms) ? NET_AM_STATS_BETTER : - NET_AM_STATS_WORSE; - - return NET_AM_STATS_SAME; -} - -static bool net_am_decision(struct net_rx_am_stats *curr_stats, - struct net_rx_am *am) -{ - int prev_state = am->tune_state; - int prev_ix = am->profile_ix; - int stats_res; - int step_res; - - switch (am->tune_state) { - case NET_AM_PARKING_ON_TOP: - stats_res = net_am_stats_compare(curr_stats, &am->prev_stats); - if (stats_res != NET_AM_STATS_SAME) - net_am_exit_parking(am); - break; - - case NET_AM_PARKING_TIRED: - am->tired--; - if (!am->tired) - net_am_exit_parking(am); - break; - - case NET_AM_GOING_RIGHT: - case NET_AM_GOING_LEFT: - stats_res = net_am_stats_compare(curr_stats, &am->prev_stats); - if (stats_res != NET_AM_STATS_BETTER) - net_am_turn(am); - - if (net_am_on_top(am)) { - net_am_park_on_top(am); - break; - } - - step_res = net_am_step(am); - switch (step_res) { - case NET_AM_ON_EDGE: - net_am_park_on_top(am); - break; - case NET_AM_TOO_TIRED: - net_am_park_tired(am); - break; - } - - break; - } - - if ((prev_state != NET_AM_PARKING_ON_TOP) || - (am->tune_state != NET_AM_PARKING_ON_TOP)) - am->prev_stats = *curr_stats; - - return am->profile_ix != prev_ix; -} - -static void net_am_sample(u16 event_ctr, - u64 packets, - u64 bytes, - struct net_rx_am_sample *s) -{ - s->time = ktime_get(); - s->pkt_ctr = packets; - s->byte_ctr = bytes; - s->event_ctr = event_ctr; -} - -#define NET_AM_NEVENTS 64 -#define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE) -#define BIT_GAP(bits, end, start) ((((end) - (start)) + BIT_ULL(bits)) & (BIT_ULL(bits) - 1)) - -static void net_am_calc_stats(struct net_rx_am_sample *start, - struct net_rx_am_sample *end, - struct net_rx_am_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(NET_AM_NEVENTS * USEC_PER_MSEC, - delta_us); -} - -void net_rx_am(struct net_rx_am *am, - u16 event_ctr, - u64 packets, - u64 bytes) -{ - struct net_rx_am_sample end_sample; - struct net_rx_am_stats curr_stats; - u16 nevents; - - switch (am->state) { - case NET_AM_MEASURE_IN_PROGRESS: - nevents = BIT_GAP(BITS_PER_TYPE(u16), event_ctr, - am->start_sample.event_ctr); - if (nevents < NET_AM_NEVENTS) - break; - net_am_sample(event_ctr, packets, bytes, &end_sample); - net_am_calc_stats(&am->start_sample, &end_sample, - &curr_stats); - if (net_am_decision(&curr_stats, am)) { - am->state = NET_AM_APPLY_NEW_PROFILE; - schedule_work(&am->work); - break; - } - /* fall through */ - case NET_AM_START_MEASURE: - net_am_sample(event_ctr, packets, bytes, &am->start_sample); - am->state = NET_AM_MEASURE_IN_PROGRESS; - break; - case NET_AM_APPLY_NEW_PROFILE: - break; - } -} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/net_rx_am.h b/drivers/net/ethernet/mellanox/mlx5/core/net_rx_am.h deleted file mode 100644 index aa65e79..0000000 --- a/drivers/net/ethernet/mellanox/mlx5/core/net_rx_am.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2013-2015, Mellanox Technologies, Ltd. All rights reserved. - * Copyright (c) 2017, Broadcom Limited - * - * 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_AM_H -#define NET_AM_H - -struct net_cq_moder { - u16 usec; - u16 pkts; -}; - -struct net_rx_am_sample { - ktime_t time; - u32 pkt_ctr; - u32 byte_ctr; - u16 event_ctr; -}; - -struct net_rx_am_stats { - int ppms; /* packets per msec */ - int bpms; /* bytes per msec */ - int epms; /* events per msec */ -}; - -struct net_rx_am { /* Adaptive Moderation */ - u8 state; - struct net_rx_am_stats prev_stats; - struct net_rx_am_sample start_sample; - struct work_struct work; - u8 profile_ix; - u8 mode; - u8 tune_state; - u8 steps_right; - u8 steps_left; - u8 tired; -}; - -enum { - NET_CQ_PERIOD_MODE_START_FROM_EQE = 0x0, - NET_CQ_PERIOD_MODE_START_FROM_CQE = 0x1, - NET_CQ_PERIOD_NUM_MODES -}; - -/* Adaptive moderation logic */ -enum { - NET_AM_START_MEASURE, - NET_AM_MEASURE_IN_PROGRESS, - NET_AM_APPLY_NEW_PROFILE, -}; - -enum { - NET_AM_PARKING_ON_TOP, - NET_AM_PARKING_TIRED, - NET_AM_GOING_RIGHT, - NET_AM_GOING_LEFT, -}; - -enum { - NET_AM_STATS_WORSE, - NET_AM_STATS_SAME, - NET_AM_STATS_BETTER, -}; - -enum { - NET_AM_STEPPED, - NET_AM_TOO_TIRED, - NET_AM_ON_EDGE, -}; - -void net_rx_am(struct net_rx_am *am, - u16 event_ctr, - u64 packets, - u64 bytes); -struct net_cq_moder net_am_get_def_profile(u8 rx_cq_period_mode); -struct net_cq_moder net_am_get_profile(u8 cq_period_mode, int ix); - -#endif /* NET_AM_H */ diff --git a/include/linux/net_rx_am.h b/include/linux/net_rx_am.h new file mode 100644 index 0000000..115cbde --- /dev/null +++ b/include/linux/net_rx_am.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013-2015, Mellanox Technologies, Ltd. All rights reserved. + * Copyright (c) 2017, Broadcom Limited + * + * 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_AM_H +#define NET_AM_H + +#include <linux/module.h> + +struct net_cq_moder { + u16 usec; + u16 pkts; +}; + +struct net_rx_am_sample { + ktime_t time; + u32 pkt_ctr; + u32 byte_ctr; + u16 event_ctr; +}; + +struct net_rx_am_stats { + int ppms; /* packets per msec */ + int bpms; /* bytes per msec */ + int epms; /* events per msec */ +}; + +struct net_rx_am { /* Adaptive Moderation */ + u8 state; + struct net_rx_am_stats prev_stats; + struct net_rx_am_sample start_sample; + struct work_struct work; + u8 profile_ix; + u8 mode; + u8 tune_state; + u8 steps_right; + u8 steps_left; + u8 tired; +}; + +enum { + NET_CQ_PERIOD_MODE_START_FROM_EQE = 0x0, + NET_CQ_PERIOD_MODE_START_FROM_CQE = 0x1, + NET_CQ_PERIOD_NUM_MODES +}; + +/* Adaptive moderation logic */ +enum { + NET_AM_START_MEASURE, + NET_AM_MEASURE_IN_PROGRESS, + NET_AM_APPLY_NEW_PROFILE, +}; + +enum { + NET_AM_PARKING_ON_TOP, + NET_AM_PARKING_TIRED, + NET_AM_GOING_RIGHT, + NET_AM_GOING_LEFT, +}; + +enum { + NET_AM_STATS_WORSE, + NET_AM_STATS_SAME, + NET_AM_STATS_BETTER, +}; + +enum { + NET_AM_STEPPED, + NET_AM_TOO_TIRED, + NET_AM_ON_EDGE, +}; + +void net_rx_am(struct net_rx_am *am, + u16 event_ctr, + u64 packets, + u64 bytes); +struct net_cq_moder net_am_get_def_profile(u8 rx_cq_period_mode); +struct net_cq_moder net_am_get_profile(u8 cq_period_mode, int ix); + +#endif /* NET_AM_H */ diff --git a/lib/Makefile b/lib/Makefile index b8f2c16..8f6bdc6 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -39,7 +39,7 @@ obj-y += bcd.o div64.o sort.o parser.o debug_locks.o random32.o \ gcd.o lcm.o list_sort.o uuid.o flex_array.o iov_iter.o clz_ctz.o \ bsearch.o find_bit.o llist.o memweight.o kfifo.o \ percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o \ - once.o refcount.o usercopy.o errseq.o + once.o refcount.o usercopy.o errseq.o net_rx_am.o obj-y += string_helpers.o obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o obj-y += hexdump.o diff --git a/lib/net_rx_am.c b/lib/net_rx_am.c new file mode 100644 index 0000000..3167ffa --- /dev/null +++ b/lib/net_rx_am.c @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2016, Mellanox Technologies. All rights reserved. + * Copyright (c) 2017, Broadcom Limiited. 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/net_rx_am.h> + +#define NET_PARAMS_AM_NUM_PROFILES 5 +/* Adaptive moderation profiles */ +#define NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE 256 +#define NET_RX_AM_DEF_PROFILE_CQE 1 +#define NET_RX_AM_DEF_PROFILE_EQE 1 + +/* All profiles sizes must be NET_PARAMS_AM_NUM_PROFILES */ +#define NET_AM_EQE_PROFILES { \ + {1, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ + {8, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ + {64, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ + {128, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ + {256, NET_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ +} + +#define NET_AM_CQE_PROFILES { \ + {2, 256}, \ + {8, 128}, \ + {16, 64}, \ + {32, 64}, \ + {64, 64} \ +} + +static const struct net_cq_moder +profile[NET_CQ_PERIOD_NUM_MODES][NET_PARAMS_AM_NUM_PROFILES] = { + NET_AM_EQE_PROFILES, + NET_AM_CQE_PROFILES, +}; + +struct net_cq_moder net_am_get_profile(u8 cq_period_mode, int ix) +{ + return profile[cq_period_mode][ix]; +} +EXPORT_SYMBOL_GPL(net_am_get_profile); + +struct net_cq_moder net_am_get_def_profile(u8 rx_cq_period_mode) +{ + int default_profile_ix; + + if (rx_cq_period_mode == NET_CQ_PERIOD_MODE_START_FROM_CQE) + default_profile_ix = NET_RX_AM_DEF_PROFILE_CQE; + else /* NET_CQ_PERIOD_MODE_START_FROM_EQE */ + default_profile_ix = NET_RX_AM_DEF_PROFILE_EQE; + + return profile[rx_cq_period_mode][default_profile_ix]; +} +EXPORT_SYMBOL_GPL(net_am_get_def_profile); + +static bool net_am_on_top(struct net_rx_am *am) +{ + switch (am->tune_state) { + case NET_AM_PARKING_ON_TOP: + case NET_AM_PARKING_TIRED: + return true; + case NET_AM_GOING_RIGHT: + return (am->steps_left > 1) && (am->steps_right == 1); + default: /* NET_AM_GOING_LEFT */ + return (am->steps_right > 1) && (am->steps_left == 1); + } +} + +static void net_am_turn(struct net_rx_am *am) +{ + switch (am->tune_state) { + case NET_AM_PARKING_ON_TOP: + case NET_AM_PARKING_TIRED: + break; + case NET_AM_GOING_RIGHT: + am->tune_state = NET_AM_GOING_LEFT; + am->steps_left = 0; + break; + case NET_AM_GOING_LEFT: + am->tune_state = NET_AM_GOING_RIGHT; + am->steps_right = 0; + break; + } +} + +static int net_am_step(struct net_rx_am *am) +{ + if (am->tired == (NET_PARAMS_AM_NUM_PROFILES * 2)) + return NET_AM_TOO_TIRED; + + switch (am->tune_state) { + case NET_AM_PARKING_ON_TOP: + case NET_AM_PARKING_TIRED: + break; + case NET_AM_GOING_RIGHT: + if (am->profile_ix == (NET_PARAMS_AM_NUM_PROFILES - 1)) + return NET_AM_ON_EDGE; + am->profile_ix++; + am->steps_right++; + break; + case NET_AM_GOING_LEFT: + if (am->profile_ix == 0) + return NET_AM_ON_EDGE; + am->profile_ix--; + am->steps_left++; + break; + } + + am->tired++; + return NET_AM_STEPPED; +} + +static void net_am_park_on_top(struct net_rx_am *am) +{ + am->steps_right = 0; + am->steps_left = 0; + am->tired = 0; + am->tune_state = NET_AM_PARKING_ON_TOP; +} + +static void net_am_park_tired(struct net_rx_am *am) +{ + am->steps_right = 0; + am->steps_left = 0; + am->tune_state = NET_AM_PARKING_TIRED; +} + +static void net_am_exit_parking(struct net_rx_am *am) +{ + am->tune_state = am->profile_ix ? NET_AM_GOING_LEFT : + NET_AM_GOING_RIGHT; + net_am_step(am); +} + +#define IS_SIGNIFICANT_DIFF(val, ref) \ + (((100 * abs((val) - (ref))) / (ref)) > 10) /* more than 10% difference */ + +static int net_am_stats_compare(struct net_rx_am_stats *curr, + struct net_rx_am_stats *prev) +{ + if (!prev->bpms) + return curr->bpms ? NET_AM_STATS_BETTER : + NET_AM_STATS_SAME; + + if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms)) + return (curr->bpms > prev->bpms) ? NET_AM_STATS_BETTER : + NET_AM_STATS_WORSE; + + if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms)) + return (curr->ppms > prev->ppms) ? NET_AM_STATS_BETTER : + NET_AM_STATS_WORSE; + + if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms)) + return (curr->epms < prev->epms) ? NET_AM_STATS_BETTER : + NET_AM_STATS_WORSE; + + return NET_AM_STATS_SAME; +} + +static bool net_am_decision(struct net_rx_am_stats *curr_stats, + struct net_rx_am *am) +{ + int prev_state = am->tune_state; + int prev_ix = am->profile_ix; + int stats_res; + int step_res; + + switch (am->tune_state) { + case NET_AM_PARKING_ON_TOP: + stats_res = net_am_stats_compare(curr_stats, &am->prev_stats); + if (stats_res != NET_AM_STATS_SAME) + net_am_exit_parking(am); + break; + + case NET_AM_PARKING_TIRED: + am->tired--; + if (!am->tired) + net_am_exit_parking(am); + break; + + case NET_AM_GOING_RIGHT: + case NET_AM_GOING_LEFT: + stats_res = net_am_stats_compare(curr_stats, &am->prev_stats); + if (stats_res != NET_AM_STATS_BETTER) + net_am_turn(am); + + if (net_am_on_top(am)) { + net_am_park_on_top(am); + break; + } + + step_res = net_am_step(am); + switch (step_res) { + case NET_AM_ON_EDGE: + net_am_park_on_top(am); + break; + case NET_AM_TOO_TIRED: + net_am_park_tired(am); + break; + } + + break; + } + + if ((prev_state != NET_AM_PARKING_ON_TOP) || + (am->tune_state != NET_AM_PARKING_ON_TOP)) + am->prev_stats = *curr_stats; + + return am->profile_ix != prev_ix; +} + +static void net_am_sample(u16 event_ctr, + u64 packets, + u64 bytes, + struct net_rx_am_sample *s) +{ + s->time = ktime_get(); + s->pkt_ctr = packets; + s->byte_ctr = bytes; + s->event_ctr = event_ctr; +} + +#define NET_AM_NEVENTS 64 +#define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE) +#define BIT_GAP(bits, end, start) ((((end) - (start)) + BIT_ULL(bits)) & (BIT_ULL(bits) - 1)) + +static void net_am_calc_stats(struct net_rx_am_sample *start, + struct net_rx_am_sample *end, + struct net_rx_am_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(NET_AM_NEVENTS * USEC_PER_MSEC, + delta_us); +} + +void net_rx_am(struct net_rx_am *am, + u16 event_ctr, + u64 packets, + u64 bytes) +{ + struct net_rx_am_sample end_sample; + struct net_rx_am_stats curr_stats; + u16 nevents; + + switch (am->state) { + case NET_AM_MEASURE_IN_PROGRESS: + nevents = BIT_GAP(BITS_PER_TYPE(u16), event_ctr, + am->start_sample.event_ctr); + if (nevents < NET_AM_NEVENTS) + break; + net_am_sample(event_ctr, packets, bytes, &end_sample); + net_am_calc_stats(&am->start_sample, &end_sample, + &curr_stats); + if (net_am_decision(&curr_stats, am)) { + am->state = NET_AM_APPLY_NEW_PROFILE; + schedule_work(&am->work); + break; + } + /* fall through */ + case NET_AM_START_MEASURE: + net_am_sample(event_ctr, packets, bytes, &am->start_sample); + am->state = NET_AM_MEASURE_IN_PROGRESS; + break; + case NET_AM_APPLY_NEW_PROFILE: + break; + } +} +EXPORT_SYMBOL_GPL(net_rx_am); -- 2.7.4