• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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