]>
Commit | Line | Data |
---|---|---|
4f75da36 TG |
1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
2 | /* | |
3 | * Copyright (c) 2018, Mellanox Technologies inc. All rights reserved. | |
4 | */ | |
5 | ||
6 | #include <linux/dim.h> | |
7 | ||
8 | struct dim_cq_moder | |
9 | net_dim_get_rx_moderation(u8 cq_period_mode, int ix) | |
10 | { | |
11 | struct dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix]; | |
12 | ||
13 | cq_moder.cq_period_mode = cq_period_mode; | |
14 | return cq_moder; | |
15 | } | |
16 | EXPORT_SYMBOL(net_dim_get_rx_moderation); | |
17 | ||
18 | struct dim_cq_moder | |
19 | net_dim_get_def_rx_moderation(u8 cq_period_mode) | |
20 | { | |
21 | u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ? | |
22 | NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE; | |
23 | ||
24 | return net_dim_get_rx_moderation(cq_period_mode, profile_ix); | |
25 | } | |
26 | EXPORT_SYMBOL(net_dim_get_def_rx_moderation); | |
27 | ||
28 | struct dim_cq_moder | |
29 | net_dim_get_tx_moderation(u8 cq_period_mode, int ix) | |
30 | { | |
31 | struct dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix]; | |
32 | ||
33 | cq_moder.cq_period_mode = cq_period_mode; | |
34 | return cq_moder; | |
35 | } | |
36 | EXPORT_SYMBOL(net_dim_get_tx_moderation); | |
37 | ||
38 | struct dim_cq_moder | |
39 | net_dim_get_def_tx_moderation(u8 cq_period_mode) | |
40 | { | |
41 | u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ? | |
42 | NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE; | |
43 | ||
44 | return net_dim_get_tx_moderation(cq_period_mode, profile_ix); | |
45 | } | |
46 | EXPORT_SYMBOL(net_dim_get_def_tx_moderation); | |
47 | ||
48 | static int net_dim_step(struct dim *dim) | |
49 | { | |
50 | if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2)) | |
51 | return DIM_TOO_TIRED; | |
52 | ||
53 | switch (dim->tune_state) { | |
54 | case DIM_PARKING_ON_TOP: | |
55 | case DIM_PARKING_TIRED: | |
56 | break; | |
57 | case DIM_GOING_RIGHT: | |
58 | if (dim->profile_ix == (NET_DIM_PARAMS_NUM_PROFILES - 1)) | |
59 | return DIM_ON_EDGE; | |
60 | dim->profile_ix++; | |
61 | dim->steps_right++; | |
62 | break; | |
63 | case DIM_GOING_LEFT: | |
64 | if (dim->profile_ix == 0) | |
65 | return DIM_ON_EDGE; | |
66 | dim->profile_ix--; | |
67 | dim->steps_left++; | |
68 | break; | |
69 | } | |
70 | ||
71 | dim->tired++; | |
72 | return DIM_STEPPED; | |
73 | } | |
74 | ||
75 | static void net_dim_exit_parking(struct dim *dim) | |
76 | { | |
77 | dim->tune_state = dim->profile_ix ? DIM_GOING_LEFT : DIM_GOING_RIGHT; | |
78 | net_dim_step(dim); | |
79 | } | |
80 | ||
81 | static int net_dim_stats_compare(struct dim_stats *curr, | |
82 | struct dim_stats *prev) | |
83 | { | |
84 | if (!prev->bpms) | |
85 | return curr->bpms ? DIM_STATS_BETTER : DIM_STATS_SAME; | |
86 | ||
87 | if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms)) | |
88 | return (curr->bpms > prev->bpms) ? DIM_STATS_BETTER : | |
89 | DIM_STATS_WORSE; | |
90 | ||
91 | if (!prev->ppms) | |
92 | return curr->ppms ? DIM_STATS_BETTER : | |
93 | DIM_STATS_SAME; | |
94 | ||
95 | if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms)) | |
96 | return (curr->ppms > prev->ppms) ? DIM_STATS_BETTER : | |
97 | DIM_STATS_WORSE; | |
98 | ||
99 | if (!prev->epms) | |
100 | return DIM_STATS_SAME; | |
101 | ||
102 | if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms)) | |
103 | return (curr->epms < prev->epms) ? DIM_STATS_BETTER : | |
104 | DIM_STATS_WORSE; | |
105 | ||
106 | return DIM_STATS_SAME; | |
107 | } | |
108 | ||
109 | static bool net_dim_decision(struct dim_stats *curr_stats, struct dim *dim) | |
110 | { | |
111 | int prev_state = dim->tune_state; | |
112 | int prev_ix = dim->profile_ix; | |
113 | int stats_res; | |
114 | int step_res; | |
115 | ||
116 | switch (dim->tune_state) { | |
117 | case DIM_PARKING_ON_TOP: | |
118 | stats_res = net_dim_stats_compare(curr_stats, | |
119 | &dim->prev_stats); | |
120 | if (stats_res != DIM_STATS_SAME) | |
121 | net_dim_exit_parking(dim); | |
122 | break; | |
123 | ||
124 | case DIM_PARKING_TIRED: | |
125 | dim->tired--; | |
126 | if (!dim->tired) | |
127 | net_dim_exit_parking(dim); | |
128 | break; | |
129 | ||
130 | case DIM_GOING_RIGHT: | |
131 | case DIM_GOING_LEFT: | |
132 | stats_res = net_dim_stats_compare(curr_stats, | |
133 | &dim->prev_stats); | |
134 | if (stats_res != DIM_STATS_BETTER) | |
135 | dim_turn(dim); | |
136 | ||
137 | if (dim_on_top(dim)) { | |
138 | dim_park_on_top(dim); | |
139 | break; | |
140 | } | |
141 | ||
142 | step_res = net_dim_step(dim); | |
143 | switch (step_res) { | |
144 | case DIM_ON_EDGE: | |
145 | dim_park_on_top(dim); | |
146 | break; | |
147 | case DIM_TOO_TIRED: | |
148 | dim_park_tired(dim); | |
149 | break; | |
150 | } | |
151 | ||
152 | break; | |
153 | } | |
154 | ||
155 | if (prev_state != DIM_PARKING_ON_TOP || | |
156 | dim->tune_state != DIM_PARKING_ON_TOP) | |
157 | dim->prev_stats = *curr_stats; | |
158 | ||
159 | return dim->profile_ix != prev_ix; | |
160 | } | |
161 | ||
162 | void net_dim(struct dim *dim, struct dim_sample end_sample) | |
163 | { | |
164 | struct dim_stats curr_stats; | |
165 | u16 nevents; | |
166 | ||
167 | switch (dim->state) { | |
168 | case DIM_MEASURE_IN_PROGRESS: | |
169 | nevents = BIT_GAP(BITS_PER_TYPE(u16), | |
170 | end_sample.event_ctr, | |
171 | dim->start_sample.event_ctr); | |
172 | if (nevents < DIM_NEVENTS) | |
173 | break; | |
174 | dim_calc_stats(&dim->start_sample, &end_sample, &curr_stats); | |
175 | if (net_dim_decision(&curr_stats, dim)) { | |
176 | dim->state = DIM_APPLY_NEW_PROFILE; | |
177 | schedule_work(&dim->work); | |
178 | break; | |
179 | } | |
180 | /* fall through */ | |
181 | case DIM_START_MEASURE: | |
182 | dim_update_sample(end_sample.event_ctr, end_sample.pkt_ctr, | |
183 | end_sample.byte_ctr, &dim->start_sample); | |
184 | dim->state = DIM_MEASURE_IN_PROGRESS; | |
185 | break; | |
186 | case DIM_APPLY_NEW_PROFILE: | |
187 | break; | |
188 | } | |
189 | } | |
190 | EXPORT_SYMBOL(net_dim); |