1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | #include "netlink.h" |
4 | #include "common.h" |
5 | |
6 | struct coalesce_req_info { |
7 | struct ethnl_req_info base; |
8 | }; |
9 | |
10 | struct coalesce_reply_data { |
11 | struct ethnl_reply_data base; |
12 | struct ethtool_coalesce coalesce; |
13 | struct kernel_ethtool_coalesce kernel_coalesce; |
14 | u32 supported_params; |
15 | }; |
16 | |
17 | #define COALESCE_REPDATA(__reply_base) \ |
18 | container_of(__reply_base, struct coalesce_reply_data, base) |
19 | |
20 | #define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS |
21 | static u32 attr_to_mask(unsigned int attr_type) |
22 | { |
23 | return BIT(attr_type - __SUPPORTED_OFFSET); |
24 | } |
25 | |
26 | /* build time check that indices in ethtool_ops::supported_coalesce_params |
27 | * match corresponding attribute types with an offset |
28 | */ |
29 | #define __CHECK_SUPPORTED_OFFSET(x) \ |
30 | static_assert((ETHTOOL_ ## x) == \ |
31 | BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET)) |
32 | __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS); |
33 | __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES); |
34 | __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ); |
35 | __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ); |
36 | __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS); |
37 | __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES); |
38 | __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ); |
39 | __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ); |
40 | __CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS); |
41 | __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX); |
42 | __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX); |
43 | __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW); |
44 | __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW); |
45 | __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW); |
46 | __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW); |
47 | __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW); |
48 | __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH); |
49 | __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH); |
50 | __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH); |
51 | __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH); |
52 | __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH); |
53 | __CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL); |
54 | |
55 | const struct nla_policy ethnl_coalesce_get_policy[] = { |
56 | [ETHTOOL_A_COALESCE_HEADER] = |
57 | NLA_POLICY_NESTED(ethnl_header_policy), |
58 | }; |
59 | |
60 | static int coalesce_prepare_data(const struct ethnl_req_info *req_base, |
61 | struct ethnl_reply_data *reply_base, |
62 | const struct genl_info *info) |
63 | { |
64 | struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); |
65 | struct net_device *dev = reply_base->dev; |
66 | int ret; |
67 | |
68 | if (!dev->ethtool_ops->get_coalesce) |
69 | return -EOPNOTSUPP; |
70 | data->supported_params = dev->ethtool_ops->supported_coalesce_params; |
71 | ret = ethnl_ops_begin(dev); |
72 | if (ret < 0) |
73 | return ret; |
74 | ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce, |
75 | &data->kernel_coalesce, |
76 | info->extack); |
77 | ethnl_ops_complete(dev); |
78 | |
79 | return ret; |
80 | } |
81 | |
82 | static int coalesce_reply_size(const struct ethnl_req_info *req_base, |
83 | const struct ethnl_reply_data *reply_base) |
84 | { |
85 | return nla_total_size(payload: sizeof(u32)) + /* _RX_USECS */ |
86 | nla_total_size(payload: sizeof(u32)) + /* _RX_MAX_FRAMES */ |
87 | nla_total_size(payload: sizeof(u32)) + /* _RX_USECS_IRQ */ |
88 | nla_total_size(payload: sizeof(u32)) + /* _RX_MAX_FRAMES_IRQ */ |
89 | nla_total_size(payload: sizeof(u32)) + /* _TX_USECS */ |
90 | nla_total_size(payload: sizeof(u32)) + /* _TX_MAX_FRAMES */ |
91 | nla_total_size(payload: sizeof(u32)) + /* _TX_USECS_IRQ */ |
92 | nla_total_size(payload: sizeof(u32)) + /* _TX_MAX_FRAMES_IRQ */ |
93 | nla_total_size(payload: sizeof(u32)) + /* _STATS_BLOCK_USECS */ |
94 | nla_total_size(payload: sizeof(u8)) + /* _USE_ADAPTIVE_RX */ |
95 | nla_total_size(payload: sizeof(u8)) + /* _USE_ADAPTIVE_TX */ |
96 | nla_total_size(payload: sizeof(u32)) + /* _PKT_RATE_LOW */ |
97 | nla_total_size(payload: sizeof(u32)) + /* _RX_USECS_LOW */ |
98 | nla_total_size(payload: sizeof(u32)) + /* _RX_MAX_FRAMES_LOW */ |
99 | nla_total_size(payload: sizeof(u32)) + /* _TX_USECS_LOW */ |
100 | nla_total_size(payload: sizeof(u32)) + /* _TX_MAX_FRAMES_LOW */ |
101 | nla_total_size(payload: sizeof(u32)) + /* _PKT_RATE_HIGH */ |
102 | nla_total_size(payload: sizeof(u32)) + /* _RX_USECS_HIGH */ |
103 | nla_total_size(payload: sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */ |
104 | nla_total_size(payload: sizeof(u32)) + /* _TX_USECS_HIGH */ |
105 | nla_total_size(payload: sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */ |
106 | nla_total_size(payload: sizeof(u32)) + /* _RATE_SAMPLE_INTERVAL */ |
107 | nla_total_size(payload: sizeof(u8)) + /* _USE_CQE_MODE_TX */ |
108 | nla_total_size(payload: sizeof(u8)) + /* _USE_CQE_MODE_RX */ |
109 | nla_total_size(payload: sizeof(u32)) + /* _TX_AGGR_MAX_BYTES */ |
110 | nla_total_size(payload: sizeof(u32)) + /* _TX_AGGR_MAX_FRAMES */ |
111 | nla_total_size(payload: sizeof(u32)); /* _TX_AGGR_TIME_USECS */ |
112 | } |
113 | |
114 | static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val, |
115 | u32 supported_params) |
116 | { |
117 | if (!val && !(supported_params & attr_to_mask(attr_type))) |
118 | return false; |
119 | return nla_put_u32(skb, attrtype: attr_type, value: val); |
120 | } |
121 | |
122 | static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val, |
123 | u32 supported_params) |
124 | { |
125 | if (!val && !(supported_params & attr_to_mask(attr_type))) |
126 | return false; |
127 | return nla_put_u8(skb, attrtype: attr_type, value: !!val); |
128 | } |
129 | |
130 | static int coalesce_fill_reply(struct sk_buff *skb, |
131 | const struct ethnl_req_info *req_base, |
132 | const struct ethnl_reply_data *reply_base) |
133 | { |
134 | const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); |
135 | const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce; |
136 | const struct ethtool_coalesce *coal = &data->coalesce; |
137 | u32 supported = data->supported_params; |
138 | |
139 | if (coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_RX_USECS, |
140 | val: coal->rx_coalesce_usecs, supported_params: supported) || |
141 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_RX_MAX_FRAMES, |
142 | val: coal->rx_max_coalesced_frames, supported_params: supported) || |
143 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_RX_USECS_IRQ, |
144 | val: coal->rx_coalesce_usecs_irq, supported_params: supported) || |
145 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, |
146 | val: coal->rx_max_coalesced_frames_irq, supported_params: supported) || |
147 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_USECS, |
148 | val: coal->tx_coalesce_usecs, supported_params: supported) || |
149 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_MAX_FRAMES, |
150 | val: coal->tx_max_coalesced_frames, supported_params: supported) || |
151 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_USECS_IRQ, |
152 | val: coal->tx_coalesce_usecs_irq, supported_params: supported) || |
153 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, |
154 | val: coal->tx_max_coalesced_frames_irq, supported_params: supported) || |
155 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, |
156 | val: coal->stats_block_coalesce_usecs, supported_params: supported) || |
157 | coalesce_put_bool(skb, attr_type: ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, |
158 | val: coal->use_adaptive_rx_coalesce, supported_params: supported) || |
159 | coalesce_put_bool(skb, attr_type: ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, |
160 | val: coal->use_adaptive_tx_coalesce, supported_params: supported) || |
161 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_PKT_RATE_LOW, |
162 | val: coal->pkt_rate_low, supported_params: supported) || |
163 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_RX_USECS_LOW, |
164 | val: coal->rx_coalesce_usecs_low, supported_params: supported) || |
165 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, |
166 | val: coal->rx_max_coalesced_frames_low, supported_params: supported) || |
167 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_USECS_LOW, |
168 | val: coal->tx_coalesce_usecs_low, supported_params: supported) || |
169 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, |
170 | val: coal->tx_max_coalesced_frames_low, supported_params: supported) || |
171 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_PKT_RATE_HIGH, |
172 | val: coal->pkt_rate_high, supported_params: supported) || |
173 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_RX_USECS_HIGH, |
174 | val: coal->rx_coalesce_usecs_high, supported_params: supported) || |
175 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, |
176 | val: coal->rx_max_coalesced_frames_high, supported_params: supported) || |
177 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_USECS_HIGH, |
178 | val: coal->tx_coalesce_usecs_high, supported_params: supported) || |
179 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, |
180 | val: coal->tx_max_coalesced_frames_high, supported_params: supported) || |
181 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, |
182 | val: coal->rate_sample_interval, supported_params: supported) || |
183 | coalesce_put_bool(skb, attr_type: ETHTOOL_A_COALESCE_USE_CQE_MODE_TX, |
184 | val: kcoal->use_cqe_mode_tx, supported_params: supported) || |
185 | coalesce_put_bool(skb, attr_type: ETHTOOL_A_COALESCE_USE_CQE_MODE_RX, |
186 | val: kcoal->use_cqe_mode_rx, supported_params: supported) || |
187 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES, |
188 | val: kcoal->tx_aggr_max_bytes, supported_params: supported) || |
189 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES, |
190 | val: kcoal->tx_aggr_max_frames, supported_params: supported) || |
191 | coalesce_put_u32(skb, attr_type: ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS, |
192 | val: kcoal->tx_aggr_time_usecs, supported_params: supported)) |
193 | return -EMSGSIZE; |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | /* COALESCE_SET */ |
199 | |
200 | const struct nla_policy ethnl_coalesce_set_policy[] = { |
201 | [ETHTOOL_A_COALESCE_HEADER] = |
202 | NLA_POLICY_NESTED(ethnl_header_policy), |
203 | [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_U32 }, |
204 | [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_U32 }, |
205 | [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_U32 }, |
206 | [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, |
207 | [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_U32 }, |
208 | [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_U32 }, |
209 | [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_U32 }, |
210 | [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, |
211 | [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_U32 }, |
212 | [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_U8 }, |
213 | [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_U8 }, |
214 | [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_U32 }, |
215 | [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_U32 }, |
216 | [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, |
217 | [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_U32 }, |
218 | [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, |
219 | [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_U32 }, |
220 | [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_U32 }, |
221 | [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, |
222 | [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 }, |
223 | [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, |
224 | [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 }, |
225 | [ETHTOOL_A_COALESCE_USE_CQE_MODE_TX] = NLA_POLICY_MAX(NLA_U8, 1), |
226 | [ETHTOOL_A_COALESCE_USE_CQE_MODE_RX] = NLA_POLICY_MAX(NLA_U8, 1), |
227 | [ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 }, |
228 | [ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 }, |
229 | [ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 }, |
230 | }; |
231 | |
232 | static int |
233 | ethnl_set_coalesce_validate(struct ethnl_req_info *req_info, |
234 | struct genl_info *info) |
235 | { |
236 | const struct ethtool_ops *ops = req_info->dev->ethtool_ops; |
237 | struct nlattr **tb = info->attrs; |
238 | u32 supported_params; |
239 | u16 a; |
240 | |
241 | if (!ops->get_coalesce || !ops->set_coalesce) |
242 | return -EOPNOTSUPP; |
243 | |
244 | /* make sure that only supported parameters are present */ |
245 | supported_params = ops->supported_coalesce_params; |
246 | for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++) |
247 | if (tb[a] && !(supported_params & attr_to_mask(attr_type: a))) { |
248 | NL_SET_ERR_MSG_ATTR(info->extack, tb[a], |
249 | "cannot modify an unsupported parameter" ); |
250 | return -EINVAL; |
251 | } |
252 | |
253 | return 1; |
254 | } |
255 | |
256 | static int |
257 | __ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info, |
258 | bool *dual_change) |
259 | { |
260 | struct kernel_ethtool_coalesce kernel_coalesce = {}; |
261 | struct net_device *dev = req_info->dev; |
262 | struct ethtool_coalesce coalesce = {}; |
263 | bool mod_mode = false, mod = false; |
264 | struct nlattr **tb = info->attrs; |
265 | int ret; |
266 | |
267 | ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce, |
268 | info->extack); |
269 | if (ret < 0) |
270 | return ret; |
271 | |
272 | /* Update values */ |
273 | ethnl_update_u32(dst: &coalesce.rx_coalesce_usecs, |
274 | attr: tb[ETHTOOL_A_COALESCE_RX_USECS], mod: &mod); |
275 | ethnl_update_u32(dst: &coalesce.rx_max_coalesced_frames, |
276 | attr: tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], mod: &mod); |
277 | ethnl_update_u32(dst: &coalesce.rx_coalesce_usecs_irq, |
278 | attr: tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], mod: &mod); |
279 | ethnl_update_u32(dst: &coalesce.rx_max_coalesced_frames_irq, |
280 | attr: tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], mod: &mod); |
281 | ethnl_update_u32(dst: &coalesce.tx_coalesce_usecs, |
282 | attr: tb[ETHTOOL_A_COALESCE_TX_USECS], mod: &mod); |
283 | ethnl_update_u32(dst: &coalesce.tx_max_coalesced_frames, |
284 | attr: tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], mod: &mod); |
285 | ethnl_update_u32(dst: &coalesce.tx_coalesce_usecs_irq, |
286 | attr: tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], mod: &mod); |
287 | ethnl_update_u32(dst: &coalesce.tx_max_coalesced_frames_irq, |
288 | attr: tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], mod: &mod); |
289 | ethnl_update_u32(dst: &coalesce.stats_block_coalesce_usecs, |
290 | attr: tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], mod: &mod); |
291 | ethnl_update_u32(dst: &coalesce.pkt_rate_low, |
292 | attr: tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], mod: &mod); |
293 | ethnl_update_u32(dst: &coalesce.rx_coalesce_usecs_low, |
294 | attr: tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], mod: &mod); |
295 | ethnl_update_u32(dst: &coalesce.rx_max_coalesced_frames_low, |
296 | attr: tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], mod: &mod); |
297 | ethnl_update_u32(dst: &coalesce.tx_coalesce_usecs_low, |
298 | attr: tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], mod: &mod); |
299 | ethnl_update_u32(dst: &coalesce.tx_max_coalesced_frames_low, |
300 | attr: tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], mod: &mod); |
301 | ethnl_update_u32(dst: &coalesce.pkt_rate_high, |
302 | attr: tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], mod: &mod); |
303 | ethnl_update_u32(dst: &coalesce.rx_coalesce_usecs_high, |
304 | attr: tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], mod: &mod); |
305 | ethnl_update_u32(dst: &coalesce.rx_max_coalesced_frames_high, |
306 | attr: tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], mod: &mod); |
307 | ethnl_update_u32(dst: &coalesce.tx_coalesce_usecs_high, |
308 | attr: tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], mod: &mod); |
309 | ethnl_update_u32(dst: &coalesce.tx_max_coalesced_frames_high, |
310 | attr: tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], mod: &mod); |
311 | ethnl_update_u32(dst: &coalesce.rate_sample_interval, |
312 | attr: tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], mod: &mod); |
313 | ethnl_update_u32(dst: &kernel_coalesce.tx_aggr_max_bytes, |
314 | attr: tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES], mod: &mod); |
315 | ethnl_update_u32(dst: &kernel_coalesce.tx_aggr_max_frames, |
316 | attr: tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], mod: &mod); |
317 | ethnl_update_u32(dst: &kernel_coalesce.tx_aggr_time_usecs, |
318 | attr: tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], mod: &mod); |
319 | |
320 | /* Update operation modes */ |
321 | ethnl_update_bool32(dst: &coalesce.use_adaptive_rx_coalesce, |
322 | attr: tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], mod: &mod_mode); |
323 | ethnl_update_bool32(dst: &coalesce.use_adaptive_tx_coalesce, |
324 | attr: tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], mod: &mod_mode); |
325 | ethnl_update_u8(dst: &kernel_coalesce.use_cqe_mode_tx, |
326 | attr: tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], mod: &mod_mode); |
327 | ethnl_update_u8(dst: &kernel_coalesce.use_cqe_mode_rx, |
328 | attr: tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], mod: &mod_mode); |
329 | |
330 | *dual_change = mod && mod_mode; |
331 | if (!mod && !mod_mode) |
332 | return 0; |
333 | |
334 | ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce, |
335 | info->extack); |
336 | return ret < 0 ? ret : 1; |
337 | } |
338 | |
339 | static int |
340 | ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info) |
341 | { |
342 | bool dual_change; |
343 | int err, ret; |
344 | |
345 | /* SET_COALESCE may change operation mode and parameters in one call. |
346 | * Changing operation mode may cause the driver to reset the parameter |
347 | * values, and therefore ignore user input (driver does not know which |
348 | * parameters come from user and which are echoed back from ->get). |
349 | * To not complicate the drivers if user tries to change both the mode |
350 | * and parameters at once - call the driver twice. |
351 | */ |
352 | err = __ethnl_set_coalesce(req_info, info, dual_change: &dual_change); |
353 | if (err < 0) |
354 | return err; |
355 | ret = err; |
356 | |
357 | if (ret && dual_change) { |
358 | err = __ethnl_set_coalesce(req_info, info, dual_change: &dual_change); |
359 | if (err < 0) |
360 | return err; |
361 | } |
362 | return ret; |
363 | } |
364 | |
365 | const struct ethnl_request_ops ethnl_coalesce_request_ops = { |
366 | .request_cmd = ETHTOOL_MSG_COALESCE_GET, |
367 | .reply_cmd = ETHTOOL_MSG_COALESCE_GET_REPLY, |
368 | .hdr_attr = ETHTOOL_A_COALESCE_HEADER, |
369 | .req_info_size = sizeof(struct coalesce_req_info), |
370 | .reply_data_size = sizeof(struct coalesce_reply_data), |
371 | |
372 | .prepare_data = coalesce_prepare_data, |
373 | .reply_size = coalesce_reply_size, |
374 | .fill_reply = coalesce_fill_reply, |
375 | |
376 | .set_validate = ethnl_set_coalesce_validate, |
377 | .set = ethnl_set_coalesce, |
378 | .set_ntf_cmd = ETHTOOL_MSG_COALESCE_NTF, |
379 | }; |
380 | |