]>
Commit | Line | Data |
---|---|---|
cb3c7fd4 GR |
1 | /* |
2 | * Copyright (c) 2016, Mellanox Technologies. All rights reserved. | |
b9c872f2 | 3 | * Copyright (c) 2017-2018, Broadcom Limited. All rights reserved. |
cb3c7fd4 GR |
4 | * |
5 | * This software is available to you under a choice of one of two | |
6 | * licenses. You may choose to be licensed under the terms of the GNU | |
7 | * General Public License (GPL) Version 2, available from the file | |
8 | * COPYING in the main directory of this source tree, or the | |
9 | * OpenIB.org BSD license below: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * - Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * - Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | */ | |
33 | ||
4c4dbb4a AG |
34 | #ifndef NET_DIM_H |
35 | #define NET_DIM_H | |
36 | ||
37 | #include <linux/module.h> | |
38 | ||
39 | struct net_dim_cq_moder { | |
40 | u16 usec; | |
41 | u16 pkts; | |
42 | u8 cq_period_mode; | |
43 | }; | |
44 | ||
45 | struct net_dim_sample { | |
46 | ktime_t time; | |
47 | u32 pkt_ctr; | |
48 | u32 byte_ctr; | |
49 | u16 event_ctr; | |
50 | }; | |
51 | ||
52 | struct net_dim_stats { | |
53 | int ppms; /* packets per msec */ | |
54 | int bpms; /* bytes per msec */ | |
55 | int epms; /* events per msec */ | |
56 | }; | |
57 | ||
58 | struct net_dim { /* Adaptive Moderation */ | |
59 | u8 state; | |
60 | struct net_dim_stats prev_stats; | |
61 | struct net_dim_sample start_sample; | |
62 | struct work_struct work; | |
63 | u8 profile_ix; | |
64 | u8 mode; | |
65 | u8 tune_state; | |
66 | u8 steps_right; | |
67 | u8 steps_left; | |
68 | u8 tired; | |
69 | }; | |
70 | ||
71 | enum { | |
72 | NET_DIM_CQ_PERIOD_MODE_START_FROM_EQE = 0x0, | |
73 | NET_DIM_CQ_PERIOD_MODE_START_FROM_CQE = 0x1, | |
74 | NET_DIM_CQ_PERIOD_NUM_MODES | |
75 | }; | |
76 | ||
77 | /* Adaptive moderation logic */ | |
78 | enum { | |
79 | NET_DIM_START_MEASURE, | |
80 | NET_DIM_MEASURE_IN_PROGRESS, | |
81 | NET_DIM_APPLY_NEW_PROFILE, | |
82 | }; | |
83 | ||
84 | enum { | |
85 | NET_DIM_PARKING_ON_TOP, | |
86 | NET_DIM_PARKING_TIRED, | |
87 | NET_DIM_GOING_RIGHT, | |
88 | NET_DIM_GOING_LEFT, | |
89 | }; | |
90 | ||
91 | enum { | |
92 | NET_DIM_STATS_WORSE, | |
93 | NET_DIM_STATS_SAME, | |
94 | NET_DIM_STATS_BETTER, | |
95 | }; | |
96 | ||
97 | enum { | |
98 | NET_DIM_STEPPED, | |
99 | NET_DIM_TOO_TIRED, | |
100 | NET_DIM_ON_EDGE, | |
101 | }; | |
cb3c7fd4 | 102 | |
9a317425 | 103 | #define NET_DIM_PARAMS_NUM_PROFILES 5 |
cb3c7fd4 | 104 | /* Adaptive moderation profiles */ |
9a317425 | 105 | #define NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE 256 |
623ad755 | 106 | #define NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE 128 |
9a317425 AG |
107 | #define NET_DIM_DEF_PROFILE_CQE 1 |
108 | #define NET_DIM_DEF_PROFILE_EQE 1 | |
109 | ||
110 | /* All profiles sizes must be NET_PARAMS_DIM_NUM_PROFILES */ | |
623ad755 | 111 | #define NET_DIM_RX_EQE_PROFILES { \ |
9a317425 AG |
112 | {1, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ |
113 | {8, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ | |
114 | {64, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ | |
115 | {128, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ | |
116 | {256, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \ | |
cb3c7fd4 GR |
117 | } |
118 | ||
623ad755 | 119 | #define NET_DIM_RX_CQE_PROFILES { \ |
cb3c7fd4 GR |
120 | {2, 256}, \ |
121 | {8, 128}, \ | |
122 | {16, 64}, \ | |
123 | {32, 64}, \ | |
124 | {64, 64} \ | |
125 | } | |
126 | ||
623ad755 TG |
127 | #define NET_DIM_TX_EQE_PROFILES { \ |
128 | {1, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \ | |
129 | {8, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \ | |
130 | {32, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \ | |
131 | {64, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \ | |
132 | {128, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE} \ | |
133 | } | |
134 | ||
135 | #define NET_DIM_TX_CQE_PROFILES { \ | |
136 | {5, 128}, \ | |
137 | {8, 64}, \ | |
138 | {16, 32}, \ | |
139 | {32, 32}, \ | |
140 | {64, 32} \ | |
141 | } | |
142 | ||
9a317425 | 143 | static const struct net_dim_cq_moder |
623ad755 TG |
144 | rx_profile[NET_DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = { |
145 | NET_DIM_RX_EQE_PROFILES, | |
146 | NET_DIM_RX_CQE_PROFILES, | |
147 | }; | |
148 | ||
149 | static const struct net_dim_cq_moder | |
150 | tx_profile[NET_DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = { | |
151 | NET_DIM_TX_EQE_PROFILES, | |
152 | NET_DIM_TX_CQE_PROFILES, | |
cb3c7fd4 GR |
153 | }; |
154 | ||
026a807c TG |
155 | static inline struct net_dim_cq_moder |
156 | net_dim_get_rx_moderation(u8 cq_period_mode, int ix) | |
cb3c7fd4 | 157 | { |
623ad755 | 158 | struct net_dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix]; |
0088cbbc | 159 | |
0088cbbc TG |
160 | cq_moder.cq_period_mode = cq_period_mode; |
161 | return cq_moder; | |
cb3c7fd4 GR |
162 | } |
163 | ||
026a807c | 164 | static inline struct net_dim_cq_moder |
623ad755 TG |
165 | net_dim_get_def_rx_moderation(u8 cq_period_mode) |
166 | { | |
167 | u8 profile_ix = cq_period_mode == NET_DIM_CQ_PERIOD_MODE_START_FROM_CQE ? | |
168 | NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE; | |
169 | ||
170 | return net_dim_get_rx_moderation(cq_period_mode, profile_ix); | |
171 | } | |
172 | ||
173 | static inline struct net_dim_cq_moder | |
174 | net_dim_get_tx_moderation(u8 cq_period_mode, int ix) | |
cb3c7fd4 | 175 | { |
623ad755 | 176 | struct net_dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix]; |
cb3c7fd4 | 177 | |
623ad755 TG |
178 | cq_moder.cq_period_mode = cq_period_mode; |
179 | return cq_moder; | |
180 | } | |
181 | ||
182 | static inline struct net_dim_cq_moder | |
183 | net_dim_get_def_tx_moderation(u8 cq_period_mode) | |
184 | { | |
185 | u8 profile_ix = cq_period_mode == NET_DIM_CQ_PERIOD_MODE_START_FROM_CQE ? | |
186 | NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE; | |
cb3c7fd4 | 187 | |
623ad755 | 188 | return net_dim_get_tx_moderation(cq_period_mode, profile_ix); |
cb3c7fd4 GR |
189 | } |
190 | ||
4c4dbb4a | 191 | static inline bool net_dim_on_top(struct net_dim *dim) |
cb3c7fd4 | 192 | { |
9a317425 AG |
193 | switch (dim->tune_state) { |
194 | case NET_DIM_PARKING_ON_TOP: | |
195 | case NET_DIM_PARKING_TIRED: | |
cb3c7fd4 | 196 | return true; |
9a317425 AG |
197 | case NET_DIM_GOING_RIGHT: |
198 | return (dim->steps_left > 1) && (dim->steps_right == 1); | |
199 | default: /* NET_DIM_GOING_LEFT */ | |
200 | return (dim->steps_right > 1) && (dim->steps_left == 1); | |
cb3c7fd4 GR |
201 | } |
202 | } | |
203 | ||
4c4dbb4a | 204 | static inline void net_dim_turn(struct net_dim *dim) |
cb3c7fd4 | 205 | { |
9a317425 AG |
206 | switch (dim->tune_state) { |
207 | case NET_DIM_PARKING_ON_TOP: | |
208 | case NET_DIM_PARKING_TIRED: | |
cb3c7fd4 | 209 | break; |
9a317425 AG |
210 | case NET_DIM_GOING_RIGHT: |
211 | dim->tune_state = NET_DIM_GOING_LEFT; | |
212 | dim->steps_left = 0; | |
cb3c7fd4 | 213 | break; |
9a317425 AG |
214 | case NET_DIM_GOING_LEFT: |
215 | dim->tune_state = NET_DIM_GOING_RIGHT; | |
216 | dim->steps_right = 0; | |
cb3c7fd4 GR |
217 | break; |
218 | } | |
219 | } | |
220 | ||
4c4dbb4a | 221 | static inline int net_dim_step(struct net_dim *dim) |
cb3c7fd4 | 222 | { |
9a317425 AG |
223 | if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2)) |
224 | return NET_DIM_TOO_TIRED; | |
cb3c7fd4 | 225 | |
9a317425 AG |
226 | switch (dim->tune_state) { |
227 | case NET_DIM_PARKING_ON_TOP: | |
228 | case NET_DIM_PARKING_TIRED: | |
cb3c7fd4 | 229 | break; |
9a317425 AG |
230 | case NET_DIM_GOING_RIGHT: |
231 | if (dim->profile_ix == (NET_DIM_PARAMS_NUM_PROFILES - 1)) | |
232 | return NET_DIM_ON_EDGE; | |
233 | dim->profile_ix++; | |
234 | dim->steps_right++; | |
cb3c7fd4 | 235 | break; |
9a317425 AG |
236 | case NET_DIM_GOING_LEFT: |
237 | if (dim->profile_ix == 0) | |
238 | return NET_DIM_ON_EDGE; | |
239 | dim->profile_ix--; | |
240 | dim->steps_left++; | |
cb3c7fd4 GR |
241 | break; |
242 | } | |
243 | ||
9a317425 AG |
244 | dim->tired++; |
245 | return NET_DIM_STEPPED; | |
cb3c7fd4 GR |
246 | } |
247 | ||
4c4dbb4a | 248 | static inline void net_dim_park_on_top(struct net_dim *dim) |
cb3c7fd4 | 249 | { |
9a317425 AG |
250 | dim->steps_right = 0; |
251 | dim->steps_left = 0; | |
252 | dim->tired = 0; | |
253 | dim->tune_state = NET_DIM_PARKING_ON_TOP; | |
cb3c7fd4 GR |
254 | } |
255 | ||
4c4dbb4a | 256 | static inline void net_dim_park_tired(struct net_dim *dim) |
cb3c7fd4 | 257 | { |
9a317425 AG |
258 | dim->steps_right = 0; |
259 | dim->steps_left = 0; | |
260 | dim->tune_state = NET_DIM_PARKING_TIRED; | |
cb3c7fd4 GR |
261 | } |
262 | ||
4c4dbb4a | 263 | static inline void net_dim_exit_parking(struct net_dim *dim) |
cb3c7fd4 | 264 | { |
9a317425 AG |
265 | dim->tune_state = dim->profile_ix ? NET_DIM_GOING_LEFT : |
266 | NET_DIM_GOING_RIGHT; | |
267 | net_dim_step(dim); | |
cb3c7fd4 GR |
268 | } |
269 | ||
c3164d2f | 270 | #define IS_SIGNIFICANT_DIFF(val, ref) \ |
f97c3dc3 | 271 | (((100UL * abs((val) - (ref))) / (ref)) > 10) /* more than 10% difference */ |
c3164d2f | 272 | |
4c4dbb4a AG |
273 | static inline int net_dim_stats_compare(struct net_dim_stats *curr, |
274 | struct net_dim_stats *prev) | |
cb3c7fd4 | 275 | { |
c3164d2f | 276 | if (!prev->bpms) |
9a317425 AG |
277 | return curr->bpms ? NET_DIM_STATS_BETTER : |
278 | NET_DIM_STATS_SAME; | |
cb3c7fd4 | 279 | |
c3164d2f | 280 | if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms)) |
9a317425 AG |
281 | return (curr->bpms > prev->bpms) ? NET_DIM_STATS_BETTER : |
282 | NET_DIM_STATS_WORSE; | |
cb3c7fd4 | 283 | |
5165674f TB |
284 | if (!prev->ppms) |
285 | return curr->ppms ? NET_DIM_STATS_BETTER : | |
286 | NET_DIM_STATS_SAME; | |
287 | ||
c3164d2f | 288 | if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms)) |
9a317425 AG |
289 | return (curr->ppms > prev->ppms) ? NET_DIM_STATS_BETTER : |
290 | NET_DIM_STATS_WORSE; | |
cb3c7fd4 | 291 | |
5165674f TB |
292 | if (!prev->epms) |
293 | return NET_DIM_STATS_SAME; | |
294 | ||
c3164d2f | 295 | if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms)) |
9a317425 AG |
296 | return (curr->epms < prev->epms) ? NET_DIM_STATS_BETTER : |
297 | NET_DIM_STATS_WORSE; | |
cb3c7fd4 | 298 | |
9a317425 | 299 | return NET_DIM_STATS_SAME; |
cb3c7fd4 GR |
300 | } |
301 | ||
4c4dbb4a AG |
302 | static inline bool net_dim_decision(struct net_dim_stats *curr_stats, |
303 | struct net_dim *dim) | |
cb3c7fd4 | 304 | { |
9a317425 AG |
305 | int prev_state = dim->tune_state; |
306 | int prev_ix = dim->profile_ix; | |
cb3c7fd4 GR |
307 | int stats_res; |
308 | int step_res; | |
309 | ||
9a317425 AG |
310 | switch (dim->tune_state) { |
311 | case NET_DIM_PARKING_ON_TOP: | |
312 | stats_res = net_dim_stats_compare(curr_stats, &dim->prev_stats); | |
313 | if (stats_res != NET_DIM_STATS_SAME) | |
314 | net_dim_exit_parking(dim); | |
cb3c7fd4 GR |
315 | break; |
316 | ||
9a317425 AG |
317 | case NET_DIM_PARKING_TIRED: |
318 | dim->tired--; | |
319 | if (!dim->tired) | |
320 | net_dim_exit_parking(dim); | |
cb3c7fd4 GR |
321 | break; |
322 | ||
9a317425 AG |
323 | case NET_DIM_GOING_RIGHT: |
324 | case NET_DIM_GOING_LEFT: | |
325 | stats_res = net_dim_stats_compare(curr_stats, &dim->prev_stats); | |
326 | if (stats_res != NET_DIM_STATS_BETTER) | |
327 | net_dim_turn(dim); | |
cb3c7fd4 | 328 | |
9a317425 AG |
329 | if (net_dim_on_top(dim)) { |
330 | net_dim_park_on_top(dim); | |
cb3c7fd4 GR |
331 | break; |
332 | } | |
333 | ||
9a317425 | 334 | step_res = net_dim_step(dim); |
cb3c7fd4 | 335 | switch (step_res) { |
9a317425 AG |
336 | case NET_DIM_ON_EDGE: |
337 | net_dim_park_on_top(dim); | |
cb3c7fd4 | 338 | break; |
9a317425 AG |
339 | case NET_DIM_TOO_TIRED: |
340 | net_dim_park_tired(dim); | |
cb3c7fd4 GR |
341 | break; |
342 | } | |
343 | ||
344 | break; | |
345 | } | |
346 | ||
9a317425 AG |
347 | if ((prev_state != NET_DIM_PARKING_ON_TOP) || |
348 | (dim->tune_state != NET_DIM_PARKING_ON_TOP)) | |
349 | dim->prev_stats = *curr_stats; | |
cb3c7fd4 | 350 | |
9a317425 | 351 | return dim->profile_ix != prev_ix; |
cb3c7fd4 GR |
352 | } |
353 | ||
4c4dbb4a AG |
354 | static inline void net_dim_sample(u16 event_ctr, |
355 | u64 packets, | |
356 | u64 bytes, | |
357 | struct net_dim_sample *s) | |
cb3c7fd4 GR |
358 | { |
359 | s->time = ktime_get(); | |
138968e9 AG |
360 | s->pkt_ctr = packets; |
361 | s->byte_ctr = bytes; | |
362 | s->event_ctr = event_ctr; | |
cb3c7fd4 GR |
363 | } |
364 | ||
9a317425 | 365 | #define NET_DIM_NEVENTS 64 |
53acd76c | 366 | #define BIT_GAP(bits, end, start) ((((end) - (start)) + BIT_ULL(bits)) & (BIT_ULL(bits) - 1)) |
cb3c7fd4 | 367 | |
4c4dbb4a AG |
368 | static inline void net_dim_calc_stats(struct net_dim_sample *start, |
369 | struct net_dim_sample *end, | |
370 | struct net_dim_stats *curr_stats) | |
cb3c7fd4 GR |
371 | { |
372 | /* u32 holds up to 71 minutes, should be enough */ | |
373 | u32 delta_us = ktime_us_delta(end->time, start->time); | |
53acd76c TG |
374 | u32 npkts = BIT_GAP(BITS_PER_TYPE(u32), end->pkt_ctr, start->pkt_ctr); |
375 | u32 nbytes = BIT_GAP(BITS_PER_TYPE(u32), end->byte_ctr, | |
376 | start->byte_ctr); | |
cb3c7fd4 | 377 | |
0bbcc0a8 | 378 | if (!delta_us) |
cb3c7fd4 | 379 | return; |
cb3c7fd4 | 380 | |
c3164d2f TG |
381 | curr_stats->ppms = DIV_ROUND_UP(npkts * USEC_PER_MSEC, delta_us); |
382 | curr_stats->bpms = DIV_ROUND_UP(nbytes * USEC_PER_MSEC, delta_us); | |
9a317425 | 383 | curr_stats->epms = DIV_ROUND_UP(NET_DIM_NEVENTS * USEC_PER_MSEC, |
c3164d2f | 384 | delta_us); |
cb3c7fd4 GR |
385 | } |
386 | ||
4c4dbb4a | 387 | static inline void net_dim(struct net_dim *dim, |
8115b750 | 388 | struct net_dim_sample end_sample) |
cb3c7fd4 | 389 | { |
9a317425 | 390 | struct net_dim_stats curr_stats; |
cb3c7fd4 GR |
391 | u16 nevents; |
392 | ||
9a317425 AG |
393 | switch (dim->state) { |
394 | case NET_DIM_MEASURE_IN_PROGRESS: | |
8115b750 AG |
395 | nevents = BIT_GAP(BITS_PER_TYPE(u16), |
396 | end_sample.event_ctr, | |
9a317425 AG |
397 | dim->start_sample.event_ctr); |
398 | if (nevents < NET_DIM_NEVENTS) | |
cb3c7fd4 | 399 | break; |
9a317425 AG |
400 | net_dim_calc_stats(&dim->start_sample, &end_sample, |
401 | &curr_stats); | |
402 | if (net_dim_decision(&curr_stats, dim)) { | |
403 | dim->state = NET_DIM_APPLY_NEW_PROFILE; | |
404 | schedule_work(&dim->work); | |
cb3c7fd4 GR |
405 | break; |
406 | } | |
407 | /* fall through */ | |
9a317425 | 408 | case NET_DIM_START_MEASURE: |
0211dda6 TG |
409 | net_dim_sample(end_sample.event_ctr, end_sample.pkt_ctr, end_sample.byte_ctr, |
410 | &dim->start_sample); | |
9a317425 | 411 | dim->state = NET_DIM_MEASURE_IN_PROGRESS; |
cb3c7fd4 | 412 | break; |
9a317425 | 413 | case NET_DIM_APPLY_NEW_PROFILE: |
cb3c7fd4 GR |
414 | break; |
415 | } | |
416 | } | |
4c4dbb4a AG |
417 | |
418 | #endif /* NET_DIM_H */ |