History

Merge tag 'blk-dim-v2' into rdma.git for-next

Generic DIM From: Tal Gilboa and Yamin Fridman Implement net DIM over a generic DIM library, add RDMA DIM dim.h lib exposes an implementation of the DIM algorithm for dynamically-tuned interrupt moderation for networking interfaces. We want a similar functionality for other protocols, which might need to optimize interrupts differently. Main motivation here is DIM for NVMf storage protocol. Current DIM implementation prioritizes reducing interrupt overhead over latency. Also, in order to reduce DIM's own overhead, the algorithm might take some time to identify it needs to change profiles. While this is acceptable for networking, it might not work well on other scenarios. Here we propose a new structure to DIM. The idea is to allow a slightly modified functionality without the risk of breaking Net DIM behavior for netdev. We verified there are no degradations in current DIM behavior with the modified solution. Suggested solution: - Common logic is implemented in lib/dim/dim.c - Net DIM (existing) logic is implemented in lib/dim/net_dim.c, which uses the common logic in dim.c - Any new DIM logic will be implemented in "lib/dim/new_dim.c". This new implementation will expose modified versions of profiles, dim_step() and dim_decision(). - DIM API is declared in include/linux/dim.h for all implementations. Pros for this solution are: - Zero impact on existing net_dim implementation and usage - Relatively more code reuse (compared to two separate solutions) - Increased extensibility Required for dependencies in the next series.

Jason Gunthorpe committed 7 years ago

Commit 2ef38e32 parents 89705e9 398c2b023 files · +728 −489Browse files

Showing the changes to include/linux (2 of 23 files). Show all

Unified · Split

2 files
  1. include/linux/dim.h +366 −0
  2. include/linux/net_dim.h +0 −418
include/linux/dim.h+366−0
@@ -0,0 +1,366 @@
1/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
2/* Copyright (c) 2019 Mellanox Technologies. */
3
4#ifndef DIM_H
5#define DIM_H
6
7#include <linux/module.h>
8
9/**
10 * Number of events between DIM iterations.
11 * Causes a moderation of the algorithm run.
12 */
13#define DIM_NEVENTS 64
14
15/**
16 * Is a difference between values justifies taking an action.
17 * We consider 10% difference as significant.
18 */
19#define IS_SIGNIFICANT_DIFF(val, ref) \
20 (((100UL * abs((val) - (ref))) / (ref)) > 10)
21
22/**
23 * Calculate the gap between two values.
24 * Take wrap-around and variable size into consideration.
25 */
26#define BIT_GAP(bits, end, start) ((((end) - (start)) + BIT_ULL(bits)) \
27 & (BIT_ULL(bits) - 1))
28
29/**
30 * Structure for CQ moderation values.
31 * Used for communications between DIM and its consumer.
32 *
33 * @usec: CQ timer suggestion (by DIM)
34 * @pkts: CQ packet counter suggestion (by DIM)
35 * @cq_period_mode: CQ priod count mode (from CQE/EQE)
36 */
37struct dim_cq_moder {
38 u16 usec;
39 u16 pkts;
40 u16 comps;
41 u8 cq_period_mode;
42};
43
44/**
45 * Structure for DIM sample data.
46 * Used for communications between DIM and its consumer.
47 *
48 * @time: Sample timestamp
49 * @pkt_ctr: Number of packets
50 * @byte_ctr: Number of bytes
51 * @event_ctr: Number of events
52 */
53struct dim_sample {
54 ktime_t time;
55 u32 pkt_ctr;
56 u32 byte_ctr;
57 u16 event_ctr;
58 u32 comp_ctr;
59};
60
61/**
62 * Structure for DIM stats.
63 * Used for holding current measured rates.
64 *
65 * @ppms: Packets per msec
66 * @bpms: Bytes per msec
67 * @epms: Events per msec
68 */
69struct dim_stats {
70 int ppms; /* packets per msec */
71 int bpms; /* bytes per msec */
72 int epms; /* events per msec */
73 int cpms; /* completions per msec */
74 int cpe_ratio; /* ratio of completions to events */
75};
76
77/**
78 * Main structure for dynamic interrupt moderation (DIM).
79 * Used for holding all information about a specific DIM instance.
80 *
81 * @state: Algorithm state (see below)
82 * @prev_stats: Measured rates from previous iteration (for comparison)
83 * @start_sample: Sampled data at start of current iteration
84 * @work: Work to perform on action required
85 * @profile_ix: Current moderation profile
86 * @mode: CQ period count mode
87 * @tune_state: Algorithm tuning state (see below)
88 * @steps_right: Number of steps taken towards higher moderation
89 * @steps_left: Number of steps taken towards lower moderation
90 * @tired: Parking depth counter
91 */
92struct dim {
93 u8 state;
94 struct dim_stats prev_stats;
95 struct dim_sample start_sample;
96 struct dim_sample measuring_sample;
97 struct work_struct work;
98 u8 profile_ix;
99 u8 mode;
100 u8 tune_state;
101 u8 steps_right;
102 u8 steps_left;
103 u8 tired;
104};
105
106/**
107 * enum dim_cq_period_mode
108 *
109 * These are the modes for CQ period count.
110 *
111 * @DIM_CQ_PERIOD_MODE_START_FROM_EQE: Start counting from EQE
112 * @DIM_CQ_PERIOD_MODE_START_FROM_CQE: Start counting from CQE (implies timer reset)
113 * @DIM_CQ_PERIOD_NUM_MODES: Number of modes
114 */
115enum {
116 DIM_CQ_PERIOD_MODE_START_FROM_EQE = 0x0,
117 DIM_CQ_PERIOD_MODE_START_FROM_CQE = 0x1,
118 DIM_CQ_PERIOD_NUM_MODES
119};
120
121/**
122 * enum dim_state
123 *
124 * These are the DIM algorithm states.
125 * These will determine if the algorithm is in a valid state to start an iteration.
126 *
127 * @DIM_START_MEASURE: This is the first iteration (also after applying a new profile)
128 * @DIM_MEASURE_IN_PROGRESS: Algorithm is already in progress - check if
129 * need to perform an action
130 * @DIM_APPLY_NEW_PROFILE: DIM consumer is currently applying a profile - no need to measure
131 */
132enum {
133 DIM_START_MEASURE,
134 DIM_MEASURE_IN_PROGRESS,
135 DIM_APPLY_NEW_PROFILE,
136};
137
138/**
139 * enum dim_tune_state
140 *
141 * These are the DIM algorithm tune states.
142 * These will determine which action the algorithm should perform.
143 *
144 * @DIM_PARKING_ON_TOP: Algorithm found a local top point - exit on significant difference
145 * @DIM_PARKING_TIRED: Algorithm found a deep top point - don't exit if tired > 0
146 * @DIM_GOING_RIGHT: Algorithm is currently trying higher moderation levels
147 * @DIM_GOING_LEFT: Algorithm is currently trying lower moderation levels
148 */
149enum {
150 DIM_PARKING_ON_TOP,
151 DIM_PARKING_TIRED,
152 DIM_GOING_RIGHT,
153 DIM_GOING_LEFT,
154};
155
156/**
157 * enum dim_stats_state
158 *
159 * These are the DIM algorithm statistics states.
160 * These will determine the verdict of current iteration.
161 *
162 * @DIM_STATS_WORSE: Current iteration shows worse performance than before
163 * @DIM_STATS_WORSE: Current iteration shows same performance than before
164 * @DIM_STATS_WORSE: Current iteration shows better performance than before
165 */
166enum {
167 DIM_STATS_WORSE,
168 DIM_STATS_SAME,
169 DIM_STATS_BETTER,
170};
171
172/**
173 * enum dim_step_result
174 *
175 * These are the DIM algorithm step results.
176 * These describe the result of a step.
177 *
178 * @DIM_STEPPED: Performed a regular step
179 * @DIM_TOO_TIRED: Same kind of step was done multiple times - should go to
180 * tired parking
181 * @DIM_ON_EDGE: Stepped to the most left/right profile
182 */
183enum {
184 DIM_STEPPED,
185 DIM_TOO_TIRED,
186 DIM_ON_EDGE,
187};
188
189/**
190 * dim_on_top - check if current state is a good place to stop (top location)
191 * @dim: DIM context
192 *
193 * Check if current profile is a good place to park at.
194 * This will result in reducing the DIM checks frequency as we assume we
195 * shouldn't probably change profiles, unless traffic pattern wasn't changed.
196 */
197bool dim_on_top(struct dim *dim);
198
199/**
200 * dim_turn - change profile alterning direction
201 * @dim: DIM context
202 *
203 * Go left if we were going right and vice-versa.
204 * Do nothing if currently parking.
205 */
206void dim_turn(struct dim *dim);
207
208/**
209 * dim_park_on_top - enter a parking state on a top location
210 * @dim: DIM context
211 *
212 * Enter parking state.
213 * Clear all movement history.
214 */
215void dim_park_on_top(struct dim *dim);
216
217/**
218 * dim_park_tired - enter a tired parking state
219 * @dim: DIM context
220 *
221 * Enter parking state.
222 * Clear all movement history and cause DIM checks frequency to reduce.
223 */
224void dim_park_tired(struct dim *dim);
225
226/**
227 * dim_calc_stats - calculate the difference between two samples
228 * @start: start sample
229 * @end: end sample
230 * @curr_stats: delta between samples
231 *
232 * Calculate the delta between two samples (in data rates).
233 * Takes into consideration counter wrap-around.
234 */
235void dim_calc_stats(struct dim_sample *start, struct dim_sample *end,
236 struct dim_stats *curr_stats);
237
238/**
239 * dim_update_sample - set a sample's fields with give values
240 * @event_ctr: number of events to set
241 * @packets: number of packets to set
242 * @bytes: number of bytes to set
243 * @s: DIM sample
244 */
245static inline void
246dim_update_sample(u16 event_ctr, u64 packets, u64 bytes, struct dim_sample *s)
247{
248 s->time = ktime_get();
249 s->pkt_ctr = packets;
250 s->byte_ctr = bytes;
251 s->event_ctr = event_ctr;
252}
253
254/**
255 * dim_update_sample_with_comps - set a sample's fields with given
256 * values including the completion parameter
257 * @event_ctr: number of events to set
258 * @packets: number of packets to set
259 * @bytes: number of bytes to set
260 * @comps: number of completions to set
261 * @s: DIM sample
262 */
263static inline void
264dim_update_sample_with_comps(u16 event_ctr, u64 packets, u64 bytes, u64 comps,
265 struct dim_sample *s)
266{
267 dim_update_sample(event_ctr, packets, bytes, s);
268 s->comp_ctr = comps;
269}
270
271/* Net DIM */
272
273/*
274 * Net DIM profiles:
275 * There are different set of profiles for each CQ period mode.
276 * There are different set of profiles for RX/TX CQs.
277 * Each profile size must be of NET_DIM_PARAMS_NUM_PROFILES
278 */
279#define NET_DIM_PARAMS_NUM_PROFILES 5
280#define NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE 256
281#define NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE 128
282#define NET_DIM_DEF_PROFILE_CQE 1
283#define NET_DIM_DEF_PROFILE_EQE 1
284
285#define NET_DIM_RX_EQE_PROFILES { \
286 {1, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
287 {8, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
288 {64, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
289 {128, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
290 {256, NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE}, \
291}
292
293#define NET_DIM_RX_CQE_PROFILES { \
294 {2, 256}, \
295 {8, 128}, \
296 {16, 64}, \
297 {32, 64}, \
298 {64, 64} \
299}
300
301#define NET_DIM_TX_EQE_PROFILES { \
302 {1, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
303 {8, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
304 {32, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
305 {64, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE}, \
306 {128, NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE} \
307}
308
309#define NET_DIM_TX_CQE_PROFILES { \
310 {5, 128}, \
311 {8, 64}, \
312 {16, 32}, \
313 {32, 32}, \
314 {64, 32} \
315}
316
317static const struct dim_cq_moder
318rx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
319 NET_DIM_RX_EQE_PROFILES,
320 NET_DIM_RX_CQE_PROFILES,
321};
322
323static const struct dim_cq_moder
324tx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
325 NET_DIM_TX_EQE_PROFILES,
326 NET_DIM_TX_CQE_PROFILES,
327};
328
329/**
330 * net_dim_get_rx_moderation - provide a CQ moderation object for the given RX profile
331 * @cq_period_mode: CQ period mode
332 * @ix: Profile index
333 */
334struct dim_cq_moder net_dim_get_rx_moderation(u8 cq_period_mode, int ix);
335
336/**
337 * net_dim_get_def_rx_moderation - provide the default RX moderation
338 * @cq_period_mode: CQ period mode
339 */
340struct dim_cq_moder net_dim_get_def_rx_moderation(u8 cq_period_mode);
341
342/**
343 * net_dim_get_tx_moderation - provide a CQ moderation object for the given TX profile
344 * @cq_period_mode: CQ period mode
345 * @ix: Profile index
346 */
347struct dim_cq_moder net_dim_get_tx_moderation(u8 cq_period_mode, int ix);
348
349/**
350 * net_dim_get_def_tx_moderation - provide the default TX moderation
351 * @cq_period_mode: CQ period mode
352 */
353struct dim_cq_moder net_dim_get_def_tx_moderation(u8 cq_period_mode);
354
355/**
356 * net_dim - main DIM algorithm entry point
357 * @dim: DIM instance information
358 * @end_sample: Current data measurement
359 *
360 * Called by the consumer.
361 * This is the main logic of the algorithm, where data is processed in order to decide on next
362 * required action.
363 */
364void net_dim(struct dim *dim, struct dim_sample end_sample);
365
366#endif /* DIM_H */
include/linux/net_dim.h+0−418
@@ -1,418 +0,0 @@
1/*
2 * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
3 * Copyright (c) 2017-2018, Broadcom Limited. All rights reserved.
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
34#ifndef NET_DIM_H
35#define NET_DIM_H
36
37#include <linux/module.h>
38
39struct net_dim_cq_moder {
40 u16 usec;
41 u16 pkts;
42 u8 cq_period_mode;
43};
44
45struct net_dim_sample {
46 ktime_t time;
47 u32 pkt_ctr;
48 u32 byte_ctr;
49 u16 event_ctr;
50};
51
52struct net_dim_stats {
53 int ppms; /* packets per msec */
54 int bpms; /* bytes per msec */
55 int epms; /* events per msec */
56};
57
58struct 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
71enum {
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 */
78enum {
79 NET_DIM_START_MEASURE,
80 NET_DIM_MEASURE_IN_PROGRESS,
81 NET_DIM_APPLY_NEW_PROFILE,
82};
83
84enum {
85 NET_DIM_PARKING_ON_TOP,
86 NET_DIM_PARKING_TIRED,
87 NET_DIM_GOING_RIGHT,
88 NET_DIM_GOING_LEFT,
89};
90
91enum {
92 NET_DIM_STATS_WORSE,
93 NET_DIM_STATS_SAME,
94 NET_DIM_STATS_BETTER,
95};
96
97enum {
98 NET_DIM_STEPPED,
99 NET_DIM_TOO_TIRED,
100 NET_DIM_ON_EDGE,
101};
102
103#define NET_DIM_PARAMS_NUM_PROFILES 5
104/* Adaptive moderation profiles */
105#define NET_DIM_DEFAULT_RX_CQ_MODERATION_PKTS_FROM_EQE 256
106#define NET_DIM_DEFAULT_TX_CQ_MODERATION_PKTS_FROM_EQE 128
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 */
111#define NET_DIM_RX_EQE_PROFILES { \
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}, \
117}
118
119#define NET_DIM_RX_CQE_PROFILES { \
120 {2, 256}, \
121 {8, 128}, \
122 {16, 64}, \
123 {32, 64}, \
124 {64, 64} \
125}
126
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
143static const struct net_dim_cq_moder
144rx_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
149static const struct net_dim_cq_moder
150tx_profile[NET_DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
151 NET_DIM_TX_EQE_PROFILES,
152 NET_DIM_TX_CQE_PROFILES,
153};
154
155static inline struct net_dim_cq_moder
156net_dim_get_rx_moderation(u8 cq_period_mode, int ix)
157{
158 struct net_dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix];
159
160 cq_moder.cq_period_mode = cq_period_mode;
161 return cq_moder;
162}
163
164static inline struct net_dim_cq_moder
165net_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
173static inline struct net_dim_cq_moder
174net_dim_get_tx_moderation(u8 cq_period_mode, int ix)
175{
176 struct net_dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix];
177
178 cq_moder.cq_period_mode = cq_period_mode;
179 return cq_moder;
180}
181
182static inline struct net_dim_cq_moder
183net_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;
187
188 return net_dim_get_tx_moderation(cq_period_mode, profile_ix);
189}
190
191static inline bool net_dim_on_top(struct net_dim *dim)
192{
193 switch (dim->tune_state) {
194 case NET_DIM_PARKING_ON_TOP:
195 case NET_DIM_PARKING_TIRED:
196 return true;
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);
201 }
202}
203
204static inline void net_dim_turn(struct net_dim *dim)
205{
206 switch (dim->tune_state) {
207 case NET_DIM_PARKING_ON_TOP:
208 case NET_DIM_PARKING_TIRED:
209 break;
210 case NET_DIM_GOING_RIGHT:
211 dim->tune_state = NET_DIM_GOING_LEFT;
212 dim->steps_left = 0;
213 break;
214 case NET_DIM_GOING_LEFT:
215 dim->tune_state = NET_DIM_GOING_RIGHT;
216 dim->steps_right = 0;
217 break;
218 }
219}
220
221static inline int net_dim_step(struct net_dim *dim)
222{
223 if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2))
224 return NET_DIM_TOO_TIRED;
225
226 switch (dim->tune_state) {
227 case NET_DIM_PARKING_ON_TOP:
228 case NET_DIM_PARKING_TIRED:
229 break;
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++;
235 break;
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++;
241 break;
242 }
243
244 dim->tired++;
245 return NET_DIM_STEPPED;
246}
247
248static inline void net_dim_park_on_top(struct net_dim *dim)
249{
250 dim->steps_right = 0;
251 dim->steps_left = 0;
252 dim->tired = 0;
253 dim->tune_state = NET_DIM_PARKING_ON_TOP;
254}
255
256static inline void net_dim_park_tired(struct net_dim *dim)
257{
258 dim->steps_right = 0;
259 dim->steps_left = 0;
260 dim->tune_state = NET_DIM_PARKING_TIRED;
261}
262
263static inline void net_dim_exit_parking(struct net_dim *dim)
264{
265 dim->tune_state = dim->profile_ix ? NET_DIM_GOING_LEFT :
266 NET_DIM_GOING_RIGHT;
267 net_dim_step(dim);
268}
269
270#define IS_SIGNIFICANT_DIFF(val, ref) \
271 (((100UL * abs((val) - (ref))) / (ref)) > 10) /* more than 10% difference */
272
273static inline int net_dim_stats_compare(struct net_dim_stats *curr,
274 struct net_dim_stats *prev)
275{
276 if (!prev->bpms)
277 return curr->bpms ? NET_DIM_STATS_BETTER :
278 NET_DIM_STATS_SAME;
279
280 if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms))
281 return (curr->bpms > prev->bpms) ? NET_DIM_STATS_BETTER :
282 NET_DIM_STATS_WORSE;
283
284 if (!prev->ppms)
285 return curr->ppms ? NET_DIM_STATS_BETTER :
286 NET_DIM_STATS_SAME;
287
288 if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms))
289 return (curr->ppms > prev->ppms) ? NET_DIM_STATS_BETTER :
290 NET_DIM_STATS_WORSE;
291
292 if (!prev->epms)
293 return NET_DIM_STATS_SAME;
294
295 if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms))
296 return (curr->epms < prev->epms) ? NET_DIM_STATS_BETTER :
297 NET_DIM_STATS_WORSE;
298
299 return NET_DIM_STATS_SAME;
300}
301
302static inline bool net_dim_decision(struct net_dim_stats *curr_stats,
303 struct net_dim *dim)
304{
305 int prev_state = dim->tune_state;
306 int prev_ix = dim->profile_ix;
307 int stats_res;
308 int step_res;
309
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);
315 break;
316
317 case NET_DIM_PARKING_TIRED:
318 dim->tired--;
319 if (!dim->tired)
320 net_dim_exit_parking(dim);
321 break;
322
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);
328
329 if (net_dim_on_top(dim)) {
330 net_dim_park_on_top(dim);
331 break;
332 }
333
334 step_res = net_dim_step(dim);
335 switch (step_res) {
336 case NET_DIM_ON_EDGE:
337 net_dim_park_on_top(dim);
338 break;
339 case NET_DIM_TOO_TIRED:
340 net_dim_park_tired(dim);
341 break;
342 }
343
344 break;
345 }
346
347 if ((prev_state != NET_DIM_PARKING_ON_TOP) ||
348 (dim->tune_state != NET_DIM_PARKING_ON_TOP))
349 dim->prev_stats = *curr_stats;
350
351 return dim->profile_ix != prev_ix;
352}
353
354static inline void net_dim_sample(u16 event_ctr,
355 u64 packets,
356 u64 bytes,
357 struct net_dim_sample *s)
358{
359 s->time = ktime_get();
360 s->pkt_ctr = packets;
361 s->byte_ctr = bytes;
362 s->event_ctr = event_ctr;
363}
364
365#define NET_DIM_NEVENTS 64
366#define BIT_GAP(bits, end, start) ((((end) - (start)) + BIT_ULL(bits)) & (BIT_ULL(bits) - 1))
367
368static inline void net_dim_calc_stats(struct net_dim_sample *start,
369 struct net_dim_sample *end,
370 struct net_dim_stats *curr_stats)
371{
372 /* u32 holds up to 71 minutes, should be enough */
373 u32 delta_us = ktime_us_delta(end->time, start->time);
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);
377
378 if (!delta_us)
379 return;
380
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);
383 curr_stats->epms = DIV_ROUND_UP(NET_DIM_NEVENTS * USEC_PER_MSEC,
384 delta_us);
385}
386
387static inline void net_dim(struct net_dim *dim,
388 struct net_dim_sample end_sample)
389{
390 struct net_dim_stats curr_stats;
391 u16 nevents;
392
393 switch (dim->state) {
394 case NET_DIM_MEASURE_IN_PROGRESS:
395 nevents = BIT_GAP(BITS_PER_TYPE(u16),
396 end_sample.event_ctr,
397 dim->start_sample.event_ctr);
398 if (nevents < NET_DIM_NEVENTS)
399 break;
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);
405 break;
406 }
407 /* fall through */
408 case NET_DIM_START_MEASURE:
409 net_dim_sample(end_sample.event_ctr, end_sample.pkt_ctr, end_sample.byte_ctr,
410 &dim->start_sample);
411 dim->state = NET_DIM_MEASURE_IN_PROGRESS;
412 break;
413 case NET_DIM_APPLY_NEW_PROFILE:
414 break;
415 }
416}
417
418#endif /* NET_DIM_H */

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t