1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2018, Mellanox Technologies inc. All rights reserved.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/dim.h>
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun * Net DIM profiles:
10*4882a593Smuzhiyun * There are different set of profiles for each CQ period mode.
11*4882a593Smuzhiyun * There are different set of profiles for RX/TX CQs.
12*4882a593Smuzhiyun * Each profile size must be of NET_DIM_PARAMS_NUM_PROFILES
13*4882a593Smuzhiyun */
14*4882a593Smuzhiyun #define NET_DIM_PARAMS_NUM_PROFILES 5
15*4882a593Smuzhiyun #define NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE 256
16*4882a593Smuzhiyun #define NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE 128
17*4882a593Smuzhiyun #define NET_DIM_DEF_PROFILE_CQE 1
18*4882a593Smuzhiyun #define NET_DIM_DEF_PROFILE_EQE 1
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define NET_DIM_RX_EQE_PROFILES { \
21*4882a593Smuzhiyun {.usec = 1, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
22*4882a593Smuzhiyun {.usec = 8, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
23*4882a593Smuzhiyun {.usec = 64, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
24*4882a593Smuzhiyun {.usec = 128, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
25*4882a593Smuzhiyun {.usec = 256, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,} \
26*4882a593Smuzhiyun }
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define NET_DIM_RX_CQE_PROFILES { \
29*4882a593Smuzhiyun {.usec = 2, .pkts = 256,}, \
30*4882a593Smuzhiyun {.usec = 8, .pkts = 128,}, \
31*4882a593Smuzhiyun {.usec = 16, .pkts = 64,}, \
32*4882a593Smuzhiyun {.usec = 32, .pkts = 64,}, \
33*4882a593Smuzhiyun {.usec = 64, .pkts = 64,} \
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define NET_DIM_TX_EQE_PROFILES { \
37*4882a593Smuzhiyun {.usec = 1, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,}, \
38*4882a593Smuzhiyun {.usec = 8, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,}, \
39*4882a593Smuzhiyun {.usec = 32, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,}, \
40*4882a593Smuzhiyun {.usec = 64, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,}, \
41*4882a593Smuzhiyun {.usec = 128, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,} \
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun #define NET_DIM_TX_CQE_PROFILES { \
45*4882a593Smuzhiyun {.usec = 5, .pkts = 128,}, \
46*4882a593Smuzhiyun {.usec = 8, .pkts = 64,}, \
47*4882a593Smuzhiyun {.usec = 16, .pkts = 32,}, \
48*4882a593Smuzhiyun {.usec = 32, .pkts = 32,}, \
49*4882a593Smuzhiyun {.usec = 64, .pkts = 32,} \
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun static const struct dim_cq_moder
53*4882a593Smuzhiyun rx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
54*4882a593Smuzhiyun NET_DIM_RX_EQE_PROFILES,
55*4882a593Smuzhiyun NET_DIM_RX_CQE_PROFILES,
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun static const struct dim_cq_moder
59*4882a593Smuzhiyun tx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
60*4882a593Smuzhiyun NET_DIM_TX_EQE_PROFILES,
61*4882a593Smuzhiyun NET_DIM_TX_CQE_PROFILES,
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun struct dim_cq_moder
net_dim_get_rx_moderation(u8 cq_period_mode,int ix)65*4882a593Smuzhiyun net_dim_get_rx_moderation(u8 cq_period_mode, int ix)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun struct dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix];
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun cq_moder.cq_period_mode = cq_period_mode;
70*4882a593Smuzhiyun return cq_moder;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun EXPORT_SYMBOL(net_dim_get_rx_moderation);
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun struct dim_cq_moder
net_dim_get_def_rx_moderation(u8 cq_period_mode)75*4882a593Smuzhiyun net_dim_get_def_rx_moderation(u8 cq_period_mode)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
78*4882a593Smuzhiyun NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun return net_dim_get_rx_moderation(cq_period_mode, profile_ix);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun EXPORT_SYMBOL(net_dim_get_def_rx_moderation);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun struct dim_cq_moder
net_dim_get_tx_moderation(u8 cq_period_mode,int ix)85*4882a593Smuzhiyun net_dim_get_tx_moderation(u8 cq_period_mode, int ix)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun struct dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix];
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun cq_moder.cq_period_mode = cq_period_mode;
90*4882a593Smuzhiyun return cq_moder;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun EXPORT_SYMBOL(net_dim_get_tx_moderation);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun struct dim_cq_moder
net_dim_get_def_tx_moderation(u8 cq_period_mode)95*4882a593Smuzhiyun net_dim_get_def_tx_moderation(u8 cq_period_mode)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
98*4882a593Smuzhiyun NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun return net_dim_get_tx_moderation(cq_period_mode, profile_ix);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun EXPORT_SYMBOL(net_dim_get_def_tx_moderation);
103*4882a593Smuzhiyun
net_dim_step(struct dim * dim)104*4882a593Smuzhiyun static int net_dim_step(struct dim *dim)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2))
107*4882a593Smuzhiyun return DIM_TOO_TIRED;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun switch (dim->tune_state) {
110*4882a593Smuzhiyun case DIM_PARKING_ON_TOP:
111*4882a593Smuzhiyun case DIM_PARKING_TIRED:
112*4882a593Smuzhiyun break;
113*4882a593Smuzhiyun case DIM_GOING_RIGHT:
114*4882a593Smuzhiyun if (dim->profile_ix == (NET_DIM_PARAMS_NUM_PROFILES - 1))
115*4882a593Smuzhiyun return DIM_ON_EDGE;
116*4882a593Smuzhiyun dim->profile_ix++;
117*4882a593Smuzhiyun dim->steps_right++;
118*4882a593Smuzhiyun break;
119*4882a593Smuzhiyun case DIM_GOING_LEFT:
120*4882a593Smuzhiyun if (dim->profile_ix == 0)
121*4882a593Smuzhiyun return DIM_ON_EDGE;
122*4882a593Smuzhiyun dim->profile_ix--;
123*4882a593Smuzhiyun dim->steps_left++;
124*4882a593Smuzhiyun break;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun dim->tired++;
128*4882a593Smuzhiyun return DIM_STEPPED;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
net_dim_exit_parking(struct dim * dim)131*4882a593Smuzhiyun static void net_dim_exit_parking(struct dim *dim)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun dim->tune_state = dim->profile_ix ? DIM_GOING_LEFT : DIM_GOING_RIGHT;
134*4882a593Smuzhiyun net_dim_step(dim);
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
net_dim_stats_compare(struct dim_stats * curr,struct dim_stats * prev)137*4882a593Smuzhiyun static int net_dim_stats_compare(struct dim_stats *curr,
138*4882a593Smuzhiyun struct dim_stats *prev)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun if (!prev->bpms)
141*4882a593Smuzhiyun return curr->bpms ? DIM_STATS_BETTER : DIM_STATS_SAME;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms))
144*4882a593Smuzhiyun return (curr->bpms > prev->bpms) ? DIM_STATS_BETTER :
145*4882a593Smuzhiyun DIM_STATS_WORSE;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (!prev->ppms)
148*4882a593Smuzhiyun return curr->ppms ? DIM_STATS_BETTER :
149*4882a593Smuzhiyun DIM_STATS_SAME;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms))
152*4882a593Smuzhiyun return (curr->ppms > prev->ppms) ? DIM_STATS_BETTER :
153*4882a593Smuzhiyun DIM_STATS_WORSE;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun if (!prev->epms)
156*4882a593Smuzhiyun return DIM_STATS_SAME;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms))
159*4882a593Smuzhiyun return (curr->epms < prev->epms) ? DIM_STATS_BETTER :
160*4882a593Smuzhiyun DIM_STATS_WORSE;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun return DIM_STATS_SAME;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
net_dim_decision(struct dim_stats * curr_stats,struct dim * dim)165*4882a593Smuzhiyun static bool net_dim_decision(struct dim_stats *curr_stats, struct dim *dim)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun int prev_state = dim->tune_state;
168*4882a593Smuzhiyun int prev_ix = dim->profile_ix;
169*4882a593Smuzhiyun int stats_res;
170*4882a593Smuzhiyun int step_res;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun switch (dim->tune_state) {
173*4882a593Smuzhiyun case DIM_PARKING_ON_TOP:
174*4882a593Smuzhiyun stats_res = net_dim_stats_compare(curr_stats,
175*4882a593Smuzhiyun &dim->prev_stats);
176*4882a593Smuzhiyun if (stats_res != DIM_STATS_SAME)
177*4882a593Smuzhiyun net_dim_exit_parking(dim);
178*4882a593Smuzhiyun break;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun case DIM_PARKING_TIRED:
181*4882a593Smuzhiyun dim->tired--;
182*4882a593Smuzhiyun if (!dim->tired)
183*4882a593Smuzhiyun net_dim_exit_parking(dim);
184*4882a593Smuzhiyun break;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun case DIM_GOING_RIGHT:
187*4882a593Smuzhiyun case DIM_GOING_LEFT:
188*4882a593Smuzhiyun stats_res = net_dim_stats_compare(curr_stats,
189*4882a593Smuzhiyun &dim->prev_stats);
190*4882a593Smuzhiyun if (stats_res != DIM_STATS_BETTER)
191*4882a593Smuzhiyun dim_turn(dim);
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun if (dim_on_top(dim)) {
194*4882a593Smuzhiyun dim_park_on_top(dim);
195*4882a593Smuzhiyun break;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun step_res = net_dim_step(dim);
199*4882a593Smuzhiyun switch (step_res) {
200*4882a593Smuzhiyun case DIM_ON_EDGE:
201*4882a593Smuzhiyun dim_park_on_top(dim);
202*4882a593Smuzhiyun break;
203*4882a593Smuzhiyun case DIM_TOO_TIRED:
204*4882a593Smuzhiyun dim_park_tired(dim);
205*4882a593Smuzhiyun break;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun break;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun if (prev_state != DIM_PARKING_ON_TOP ||
212*4882a593Smuzhiyun dim->tune_state != DIM_PARKING_ON_TOP)
213*4882a593Smuzhiyun dim->prev_stats = *curr_stats;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun return dim->profile_ix != prev_ix;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
net_dim(struct dim * dim,struct dim_sample end_sample)218*4882a593Smuzhiyun void net_dim(struct dim *dim, struct dim_sample end_sample)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun struct dim_stats curr_stats;
221*4882a593Smuzhiyun u16 nevents;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun switch (dim->state) {
224*4882a593Smuzhiyun case DIM_MEASURE_IN_PROGRESS:
225*4882a593Smuzhiyun nevents = BIT_GAP(BITS_PER_TYPE(u16),
226*4882a593Smuzhiyun end_sample.event_ctr,
227*4882a593Smuzhiyun dim->start_sample.event_ctr);
228*4882a593Smuzhiyun if (nevents < DIM_NEVENTS)
229*4882a593Smuzhiyun break;
230*4882a593Smuzhiyun dim_calc_stats(&dim->start_sample, &end_sample, &curr_stats);
231*4882a593Smuzhiyun if (net_dim_decision(&curr_stats, dim)) {
232*4882a593Smuzhiyun dim->state = DIM_APPLY_NEW_PROFILE;
233*4882a593Smuzhiyun schedule_work(&dim->work);
234*4882a593Smuzhiyun break;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun /* fall through */
237*4882a593Smuzhiyun case DIM_START_MEASURE:
238*4882a593Smuzhiyun dim_update_sample(end_sample.event_ctr, end_sample.pkt_ctr,
239*4882a593Smuzhiyun end_sample.byte_ctr, &dim->start_sample);
240*4882a593Smuzhiyun dim->state = DIM_MEASURE_IN_PROGRESS;
241*4882a593Smuzhiyun break;
242*4882a593Smuzhiyun case DIM_APPLY_NEW_PROFILE:
243*4882a593Smuzhiyun break;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun EXPORT_SYMBOL(net_dim);
247