18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2018, Mellanox Technologies inc.  All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/dim.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * Net DIM profiles:
108c2ecf20Sopenharmony_ci *        There are different set of profiles for each CQ period mode.
118c2ecf20Sopenharmony_ci *        There are different set of profiles for RX/TX CQs.
128c2ecf20Sopenharmony_ci *        Each profile size must be of NET_DIM_PARAMS_NUM_PROFILES
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#define NET_DIM_PARAMS_NUM_PROFILES 5
158c2ecf20Sopenharmony_ci#define NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE 256
168c2ecf20Sopenharmony_ci#define NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE 128
178c2ecf20Sopenharmony_ci#define NET_DIM_DEF_PROFILE_CQE 1
188c2ecf20Sopenharmony_ci#define NET_DIM_DEF_PROFILE_EQE 1
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define NET_DIM_RX_EQE_PROFILES { \
218c2ecf20Sopenharmony_ci	{.usec = 1,   .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
228c2ecf20Sopenharmony_ci	{.usec = 8,   .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
238c2ecf20Sopenharmony_ci	{.usec = 64,  .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
248c2ecf20Sopenharmony_ci	{.usec = 128, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
258c2ecf20Sopenharmony_ci	{.usec = 256, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}  \
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define NET_DIM_RX_CQE_PROFILES { \
298c2ecf20Sopenharmony_ci	{.usec = 2,  .pkts = 256,},             \
308c2ecf20Sopenharmony_ci	{.usec = 8,  .pkts = 128,},             \
318c2ecf20Sopenharmony_ci	{.usec = 16, .pkts = 64,},              \
328c2ecf20Sopenharmony_ci	{.usec = 32, .pkts = 64,},              \
338c2ecf20Sopenharmony_ci	{.usec = 64, .pkts = 64,}               \
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define NET_DIM_TX_EQE_PROFILES { \
378c2ecf20Sopenharmony_ci	{.usec = 1,   .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,},  \
388c2ecf20Sopenharmony_ci	{.usec = 8,   .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,},  \
398c2ecf20Sopenharmony_ci	{.usec = 32,  .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,},  \
408c2ecf20Sopenharmony_ci	{.usec = 64,  .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,},  \
418c2ecf20Sopenharmony_ci	{.usec = 128, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,}   \
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define NET_DIM_TX_CQE_PROFILES { \
458c2ecf20Sopenharmony_ci	{.usec = 5,  .pkts = 128,},  \
468c2ecf20Sopenharmony_ci	{.usec = 8,  .pkts = 64,},  \
478c2ecf20Sopenharmony_ci	{.usec = 16, .pkts = 32,},  \
488c2ecf20Sopenharmony_ci	{.usec = 32, .pkts = 32,},  \
498c2ecf20Sopenharmony_ci	{.usec = 64, .pkts = 32,}   \
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const struct dim_cq_moder
538c2ecf20Sopenharmony_cirx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
548c2ecf20Sopenharmony_ci	NET_DIM_RX_EQE_PROFILES,
558c2ecf20Sopenharmony_ci	NET_DIM_RX_CQE_PROFILES,
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic const struct dim_cq_moder
598c2ecf20Sopenharmony_citx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
608c2ecf20Sopenharmony_ci	NET_DIM_TX_EQE_PROFILES,
618c2ecf20Sopenharmony_ci	NET_DIM_TX_CQE_PROFILES,
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistruct dim_cq_moder
658c2ecf20Sopenharmony_cinet_dim_get_rx_moderation(u8 cq_period_mode, int ix)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix];
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	cq_moder.cq_period_mode = cq_period_mode;
708c2ecf20Sopenharmony_ci	return cq_moder;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(net_dim_get_rx_moderation);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct dim_cq_moder
758c2ecf20Sopenharmony_cinet_dim_get_def_rx_moderation(u8 cq_period_mode)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
788c2ecf20Sopenharmony_ci			NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return net_dim_get_rx_moderation(cq_period_mode, profile_ix);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(net_dim_get_def_rx_moderation);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistruct dim_cq_moder
858c2ecf20Sopenharmony_cinet_dim_get_tx_moderation(u8 cq_period_mode, int ix)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix];
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	cq_moder.cq_period_mode = cq_period_mode;
908c2ecf20Sopenharmony_ci	return cq_moder;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(net_dim_get_tx_moderation);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistruct dim_cq_moder
958c2ecf20Sopenharmony_cinet_dim_get_def_tx_moderation(u8 cq_period_mode)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
988c2ecf20Sopenharmony_ci			NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return net_dim_get_tx_moderation(cq_period_mode, profile_ix);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(net_dim_get_def_tx_moderation);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int net_dim_step(struct dim *dim)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2))
1078c2ecf20Sopenharmony_ci		return DIM_TOO_TIRED;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	switch (dim->tune_state) {
1108c2ecf20Sopenharmony_ci	case DIM_PARKING_ON_TOP:
1118c2ecf20Sopenharmony_ci	case DIM_PARKING_TIRED:
1128c2ecf20Sopenharmony_ci		break;
1138c2ecf20Sopenharmony_ci	case DIM_GOING_RIGHT:
1148c2ecf20Sopenharmony_ci		if (dim->profile_ix == (NET_DIM_PARAMS_NUM_PROFILES - 1))
1158c2ecf20Sopenharmony_ci			return DIM_ON_EDGE;
1168c2ecf20Sopenharmony_ci		dim->profile_ix++;
1178c2ecf20Sopenharmony_ci		dim->steps_right++;
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci	case DIM_GOING_LEFT:
1208c2ecf20Sopenharmony_ci		if (dim->profile_ix == 0)
1218c2ecf20Sopenharmony_ci			return DIM_ON_EDGE;
1228c2ecf20Sopenharmony_ci		dim->profile_ix--;
1238c2ecf20Sopenharmony_ci		dim->steps_left++;
1248c2ecf20Sopenharmony_ci		break;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	dim->tired++;
1288c2ecf20Sopenharmony_ci	return DIM_STEPPED;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic void net_dim_exit_parking(struct dim *dim)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	dim->tune_state = dim->profile_ix ? DIM_GOING_LEFT : DIM_GOING_RIGHT;
1348c2ecf20Sopenharmony_ci	net_dim_step(dim);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int net_dim_stats_compare(struct dim_stats *curr,
1388c2ecf20Sopenharmony_ci				 struct dim_stats *prev)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	if (!prev->bpms)
1418c2ecf20Sopenharmony_ci		return curr->bpms ? DIM_STATS_BETTER : DIM_STATS_SAME;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms))
1448c2ecf20Sopenharmony_ci		return (curr->bpms > prev->bpms) ? DIM_STATS_BETTER :
1458c2ecf20Sopenharmony_ci						   DIM_STATS_WORSE;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (!prev->ppms)
1488c2ecf20Sopenharmony_ci		return curr->ppms ? DIM_STATS_BETTER :
1498c2ecf20Sopenharmony_ci				    DIM_STATS_SAME;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms))
1528c2ecf20Sopenharmony_ci		return (curr->ppms > prev->ppms) ? DIM_STATS_BETTER :
1538c2ecf20Sopenharmony_ci						   DIM_STATS_WORSE;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (!prev->epms)
1568c2ecf20Sopenharmony_ci		return DIM_STATS_SAME;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms))
1598c2ecf20Sopenharmony_ci		return (curr->epms < prev->epms) ? DIM_STATS_BETTER :
1608c2ecf20Sopenharmony_ci						   DIM_STATS_WORSE;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return DIM_STATS_SAME;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic bool net_dim_decision(struct dim_stats *curr_stats, struct dim *dim)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	int prev_state = dim->tune_state;
1688c2ecf20Sopenharmony_ci	int prev_ix = dim->profile_ix;
1698c2ecf20Sopenharmony_ci	int stats_res;
1708c2ecf20Sopenharmony_ci	int step_res;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	switch (dim->tune_state) {
1738c2ecf20Sopenharmony_ci	case DIM_PARKING_ON_TOP:
1748c2ecf20Sopenharmony_ci		stats_res = net_dim_stats_compare(curr_stats,
1758c2ecf20Sopenharmony_ci						  &dim->prev_stats);
1768c2ecf20Sopenharmony_ci		if (stats_res != DIM_STATS_SAME)
1778c2ecf20Sopenharmony_ci			net_dim_exit_parking(dim);
1788c2ecf20Sopenharmony_ci		break;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	case DIM_PARKING_TIRED:
1818c2ecf20Sopenharmony_ci		dim->tired--;
1828c2ecf20Sopenharmony_ci		if (!dim->tired)
1838c2ecf20Sopenharmony_ci			net_dim_exit_parking(dim);
1848c2ecf20Sopenharmony_ci		break;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	case DIM_GOING_RIGHT:
1878c2ecf20Sopenharmony_ci	case DIM_GOING_LEFT:
1888c2ecf20Sopenharmony_ci		stats_res = net_dim_stats_compare(curr_stats,
1898c2ecf20Sopenharmony_ci						  &dim->prev_stats);
1908c2ecf20Sopenharmony_ci		if (stats_res != DIM_STATS_BETTER)
1918c2ecf20Sopenharmony_ci			dim_turn(dim);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		if (dim_on_top(dim)) {
1948c2ecf20Sopenharmony_ci			dim_park_on_top(dim);
1958c2ecf20Sopenharmony_ci			break;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		step_res = net_dim_step(dim);
1998c2ecf20Sopenharmony_ci		switch (step_res) {
2008c2ecf20Sopenharmony_ci		case DIM_ON_EDGE:
2018c2ecf20Sopenharmony_ci			dim_park_on_top(dim);
2028c2ecf20Sopenharmony_ci			break;
2038c2ecf20Sopenharmony_ci		case DIM_TOO_TIRED:
2048c2ecf20Sopenharmony_ci			dim_park_tired(dim);
2058c2ecf20Sopenharmony_ci			break;
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		break;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (prev_state != DIM_PARKING_ON_TOP ||
2128c2ecf20Sopenharmony_ci	    dim->tune_state != DIM_PARKING_ON_TOP)
2138c2ecf20Sopenharmony_ci		dim->prev_stats = *curr_stats;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return dim->profile_ix != prev_ix;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_civoid net_dim(struct dim *dim, struct dim_sample end_sample)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct dim_stats curr_stats;
2218c2ecf20Sopenharmony_ci	u16 nevents;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	switch (dim->state) {
2248c2ecf20Sopenharmony_ci	case DIM_MEASURE_IN_PROGRESS:
2258c2ecf20Sopenharmony_ci		nevents = BIT_GAP(BITS_PER_TYPE(u16),
2268c2ecf20Sopenharmony_ci				  end_sample.event_ctr,
2278c2ecf20Sopenharmony_ci				  dim->start_sample.event_ctr);
2288c2ecf20Sopenharmony_ci		if (nevents < DIM_NEVENTS)
2298c2ecf20Sopenharmony_ci			break;
2308c2ecf20Sopenharmony_ci		if (!dim_calc_stats(&dim->start_sample, &end_sample, &curr_stats))
2318c2ecf20Sopenharmony_ci			break;
2328c2ecf20Sopenharmony_ci		if (net_dim_decision(&curr_stats, dim)) {
2338c2ecf20Sopenharmony_ci			dim->state = DIM_APPLY_NEW_PROFILE;
2348c2ecf20Sopenharmony_ci			schedule_work(&dim->work);
2358c2ecf20Sopenharmony_ci			break;
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci		/* fall through */
2388c2ecf20Sopenharmony_ci	case DIM_START_MEASURE:
2398c2ecf20Sopenharmony_ci		dim_update_sample(end_sample.event_ctr, end_sample.pkt_ctr,
2408c2ecf20Sopenharmony_ci				  end_sample.byte_ctr, &dim->start_sample);
2418c2ecf20Sopenharmony_ci		dim->state = DIM_MEASURE_IN_PROGRESS;
2428c2ecf20Sopenharmony_ci		break;
2438c2ecf20Sopenharmony_ci	case DIM_APPLY_NEW_PROFILE:
2448c2ecf20Sopenharmony_ci		break;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(net_dim);
248