1 /*
2 * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses. You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
12 * conditions are met:
13 *
14 * - Redistributions of source code must retain the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer.
17 *
18 * - Redistributions in binary form must reproduce the above
19 * copyright notice, this list of conditions and the following
20 * disclaimer in the documentation and/or other materials
21 * provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 */
32
33 #include "en.h"
34
35 /* Adaptive moderation profiles */
36 #define MLX5E_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE 256
37 #define MLX5E_RX_AM_DEF_PROFILE_CQE 1
38 #define MLX5E_RX_AM_DEF_PROFILE_EQE 1
39 #define MLX5E_PARAMS_AM_NUM_PROFILES 5
40
41 /* All profiles sizes must be MLX5E_PARAMS_AM_NUM_PROFILES */
42 #define MLX5_AM_EQE_PROFILES { \
43 {1, MLX5E_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
44 {8, MLX5E_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
45 {64, MLX5E_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
46 {128, MLX5E_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
47 {256, MLX5E_AM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
48 }
49
50 #define MLX5_AM_CQE_PROFILES { \
51 {2, 256}, \
52 {8, 128}, \
53 {16, 64}, \
54 {32, 64}, \
55 {64, 64} \
56 }
57
58 static const struct mlx5e_cq_moder
59 profile[MLX5_CQ_PERIOD_NUM_MODES][MLX5E_PARAMS_AM_NUM_PROFILES] = {
60 MLX5_AM_EQE_PROFILES,
61 MLX5_AM_CQE_PROFILES,
62 };
63
mlx5e_am_get_profile(u8 cq_period_mode,int ix)64 static inline struct mlx5e_cq_moder mlx5e_am_get_profile(u8 cq_period_mode, int ix)
65 {
66 return profile[cq_period_mode][ix];
67 }
68
mlx5e_am_get_def_profile(u8 rx_cq_period_mode)69 struct mlx5e_cq_moder mlx5e_am_get_def_profile(u8 rx_cq_period_mode)
70 {
71 int default_profile_ix;
72
73 if (rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE)
74 default_profile_ix = MLX5E_RX_AM_DEF_PROFILE_CQE;
75 else /* MLX5_CQ_PERIOD_MODE_START_FROM_EQE */
76 default_profile_ix = MLX5E_RX_AM_DEF_PROFILE_EQE;
77
78 return profile[rx_cq_period_mode][default_profile_ix];
79 }
80
81 /* Adaptive moderation logic */
82 enum {
83 MLX5E_AM_START_MEASURE,
84 MLX5E_AM_MEASURE_IN_PROGRESS,
85 MLX5E_AM_APPLY_NEW_PROFILE,
86 };
87
88 enum {
89 MLX5E_AM_PARKING_ON_TOP,
90 MLX5E_AM_PARKING_TIRED,
91 MLX5E_AM_GOING_RIGHT,
92 MLX5E_AM_GOING_LEFT,
93 };
94
95 enum {
96 MLX5E_AM_STATS_WORSE,
97 MLX5E_AM_STATS_SAME,
98 MLX5E_AM_STATS_BETTER,
99 };
100
101 enum {
102 MLX5E_AM_STEPPED,
103 MLX5E_AM_TOO_TIRED,
104 MLX5E_AM_ON_EDGE,
105 };
106
mlx5e_am_on_top(struct mlx5e_rx_am * am)107 static bool mlx5e_am_on_top(struct mlx5e_rx_am *am)
108 {
109 switch (am->tune_state) {
110 case MLX5E_AM_PARKING_ON_TOP:
111 case MLX5E_AM_PARKING_TIRED:
112 return true;
113 case MLX5E_AM_GOING_RIGHT:
114 return (am->steps_left > 1) && (am->steps_right == 1);
115 default: /* MLX5E_AM_GOING_LEFT */
116 return (am->steps_right > 1) && (am->steps_left == 1);
117 }
118 }
119
mlx5e_am_turn(struct mlx5e_rx_am * am)120 static void mlx5e_am_turn(struct mlx5e_rx_am *am)
121 {
122 switch (am->tune_state) {
123 case MLX5E_AM_PARKING_ON_TOP:
124 case MLX5E_AM_PARKING_TIRED:
125 break;
126 case MLX5E_AM_GOING_RIGHT:
127 am->tune_state = MLX5E_AM_GOING_LEFT;
128 am->steps_left = 0;
129 break;
130 case MLX5E_AM_GOING_LEFT:
131 am->tune_state = MLX5E_AM_GOING_RIGHT;
132 am->steps_right = 0;
133 break;
134 }
135 }
136
mlx5e_am_step(struct mlx5e_rx_am * am)137 static int mlx5e_am_step(struct mlx5e_rx_am *am)
138 {
139 if (am->tired == (MLX5E_PARAMS_AM_NUM_PROFILES * 2))
140 return MLX5E_AM_TOO_TIRED;
141
142 switch (am->tune_state) {
143 case MLX5E_AM_PARKING_ON_TOP:
144 case MLX5E_AM_PARKING_TIRED:
145 break;
146 case MLX5E_AM_GOING_RIGHT:
147 if (am->profile_ix == (MLX5E_PARAMS_AM_NUM_PROFILES - 1))
148 return MLX5E_AM_ON_EDGE;
149 am->profile_ix++;
150 am->steps_right++;
151 break;
152 case MLX5E_AM_GOING_LEFT:
153 if (am->profile_ix == 0)
154 return MLX5E_AM_ON_EDGE;
155 am->profile_ix--;
156 am->steps_left++;
157 break;
158 }
159
160 am->tired++;
161 return MLX5E_AM_STEPPED;
162 }
163
mlx5e_am_park_on_top(struct mlx5e_rx_am * am)164 static void mlx5e_am_park_on_top(struct mlx5e_rx_am *am)
165 {
166 am->steps_right = 0;
167 am->steps_left = 0;
168 am->tired = 0;
169 am->tune_state = MLX5E_AM_PARKING_ON_TOP;
170 }
171
mlx5e_am_park_tired(struct mlx5e_rx_am * am)172 static void mlx5e_am_park_tired(struct mlx5e_rx_am *am)
173 {
174 am->steps_right = 0;
175 am->steps_left = 0;
176 am->tune_state = MLX5E_AM_PARKING_TIRED;
177 }
178
mlx5e_am_exit_parking(struct mlx5e_rx_am * am)179 static void mlx5e_am_exit_parking(struct mlx5e_rx_am *am)
180 {
181 am->tune_state = am->profile_ix ? MLX5E_AM_GOING_LEFT :
182 MLX5E_AM_GOING_RIGHT;
183 mlx5e_am_step(am);
184 }
185
186 #define IS_SIGNIFICANT_DIFF(val, ref) \
187 (((100 * abs((val) - (ref))) / (ref)) > 10) /* more than 10% difference */
188
mlx5e_am_stats_compare(struct mlx5e_rx_am_stats * curr,struct mlx5e_rx_am_stats * prev)189 static int mlx5e_am_stats_compare(struct mlx5e_rx_am_stats *curr,
190 struct mlx5e_rx_am_stats *prev)
191 {
192 if (!prev->bpms)
193 return curr->bpms ? MLX5E_AM_STATS_BETTER :
194 MLX5E_AM_STATS_SAME;
195
196 if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms))
197 return (curr->bpms > prev->bpms) ? MLX5E_AM_STATS_BETTER :
198 MLX5E_AM_STATS_WORSE;
199
200 if (!prev->ppms)
201 return curr->ppms ? MLX5E_AM_STATS_BETTER :
202 MLX5E_AM_STATS_SAME;
203
204 if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms))
205 return (curr->ppms > prev->ppms) ? MLX5E_AM_STATS_BETTER :
206 MLX5E_AM_STATS_WORSE;
207 if (!prev->epms)
208 return MLX5E_AM_STATS_SAME;
209
210 if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms))
211 return (curr->epms < prev->epms) ? MLX5E_AM_STATS_BETTER :
212 MLX5E_AM_STATS_WORSE;
213
214 return MLX5E_AM_STATS_SAME;
215 }
216
mlx5e_am_decision(struct mlx5e_rx_am_stats * curr_stats,struct mlx5e_rx_am * am)217 static bool mlx5e_am_decision(struct mlx5e_rx_am_stats *curr_stats,
218 struct mlx5e_rx_am *am)
219 {
220 int prev_state = am->tune_state;
221 int prev_ix = am->profile_ix;
222 int stats_res;
223 int step_res;
224
225 switch (am->tune_state) {
226 case MLX5E_AM_PARKING_ON_TOP:
227 stats_res = mlx5e_am_stats_compare(curr_stats, &am->prev_stats);
228 if (stats_res != MLX5E_AM_STATS_SAME)
229 mlx5e_am_exit_parking(am);
230 break;
231
232 case MLX5E_AM_PARKING_TIRED:
233 am->tired--;
234 if (!am->tired)
235 mlx5e_am_exit_parking(am);
236 break;
237
238 case MLX5E_AM_GOING_RIGHT:
239 case MLX5E_AM_GOING_LEFT:
240 stats_res = mlx5e_am_stats_compare(curr_stats, &am->prev_stats);
241 if (stats_res != MLX5E_AM_STATS_BETTER)
242 mlx5e_am_turn(am);
243
244 if (mlx5e_am_on_top(am)) {
245 mlx5e_am_park_on_top(am);
246 break;
247 }
248
249 step_res = mlx5e_am_step(am);
250 switch (step_res) {
251 case MLX5E_AM_ON_EDGE:
252 mlx5e_am_park_on_top(am);
253 break;
254 case MLX5E_AM_TOO_TIRED:
255 mlx5e_am_park_tired(am);
256 break;
257 }
258
259 break;
260 }
261
262 if ((prev_state != MLX5E_AM_PARKING_ON_TOP) ||
263 (am->tune_state != MLX5E_AM_PARKING_ON_TOP))
264 am->prev_stats = *curr_stats;
265
266 return am->profile_ix != prev_ix;
267 }
268
mlx5e_am_sample(struct mlx5e_rq * rq,struct mlx5e_rx_am_sample * s)269 static void mlx5e_am_sample(struct mlx5e_rq *rq,
270 struct mlx5e_rx_am_sample *s)
271 {
272 s->time = ktime_get();
273 s->pkt_ctr = rq->stats.packets;
274 s->byte_ctr = rq->stats.bytes;
275 s->event_ctr = rq->cq.event_ctr;
276 }
277
278 #define MLX5E_AM_NEVENTS 64
279 #define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE)
280 #define BIT_GAP(bits, end, start) ((((end) - (start)) + BIT_ULL(bits)) & (BIT_ULL(bits) - 1))
281
mlx5e_am_calc_stats(struct mlx5e_rx_am_sample * start,struct mlx5e_rx_am_sample * end,struct mlx5e_rx_am_stats * curr_stats)282 static void mlx5e_am_calc_stats(struct mlx5e_rx_am_sample *start,
283 struct mlx5e_rx_am_sample *end,
284 struct mlx5e_rx_am_stats *curr_stats)
285 {
286 /* u32 holds up to 71 minutes, should be enough */
287 u32 delta_us = ktime_us_delta(end->time, start->time);
288 u32 npkts = BIT_GAP(BITS_PER_TYPE(u32), end->pkt_ctr, start->pkt_ctr);
289 u32 nbytes = BIT_GAP(BITS_PER_TYPE(u32), end->byte_ctr,
290 start->byte_ctr);
291
292 if (!delta_us)
293 return;
294
295 curr_stats->ppms = DIV_ROUND_UP(npkts * USEC_PER_MSEC, delta_us);
296 curr_stats->bpms = DIV_ROUND_UP(nbytes * USEC_PER_MSEC, delta_us);
297 curr_stats->epms = DIV_ROUND_UP(MLX5E_AM_NEVENTS * USEC_PER_MSEC,
298 delta_us);
299 }
300
mlx5e_rx_am_work(struct work_struct * work)301 void mlx5e_rx_am_work(struct work_struct *work)
302 {
303 struct mlx5e_rx_am *am = container_of(work, struct mlx5e_rx_am,
304 work);
305 struct mlx5e_rq *rq = container_of(am, struct mlx5e_rq, am);
306 struct mlx5e_cq_moder cur_profile = profile[am->mode][am->profile_ix];
307
308 mlx5_core_modify_cq_moderation(rq->mdev, &rq->cq.mcq,
309 cur_profile.usec, cur_profile.pkts);
310
311 am->state = MLX5E_AM_START_MEASURE;
312 }
313
mlx5e_rx_am(struct mlx5e_rq * rq)314 void mlx5e_rx_am(struct mlx5e_rq *rq)
315 {
316 struct mlx5e_rx_am *am = &rq->am;
317 struct mlx5e_rx_am_sample end_sample;
318 struct mlx5e_rx_am_stats curr_stats;
319 u16 nevents;
320
321 switch (am->state) {
322 case MLX5E_AM_MEASURE_IN_PROGRESS:
323 nevents = BIT_GAP(BITS_PER_TYPE(u16), rq->cq.event_ctr,
324 am->start_sample.event_ctr);
325 if (nevents < MLX5E_AM_NEVENTS)
326 break;
327 mlx5e_am_sample(rq, &end_sample);
328 mlx5e_am_calc_stats(&am->start_sample, &end_sample,
329 &curr_stats);
330 if (mlx5e_am_decision(&curr_stats, am)) {
331 am->state = MLX5E_AM_APPLY_NEW_PROFILE;
332 schedule_work(&am->work);
333 break;
334 }
335 /* fall through */
336 case MLX5E_AM_START_MEASURE:
337 mlx5e_am_sample(rq, &am->start_sample);
338 am->state = MLX5E_AM_MEASURE_IN_PROGRESS;
339 break;
340 case MLX5E_AM_APPLY_NEW_PROFILE:
341 break;
342 }
343 }
344