Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2
3#include <linux/dim.h>
4#include "netlink.h"
5#include "common.h"
6
7struct coalesce_req_info {
8 struct ethnl_req_info base;
9};
10
11struct coalesce_reply_data {
12 struct ethnl_reply_data base;
13 struct ethtool_coalesce coalesce;
14 struct kernel_ethtool_coalesce kernel_coalesce;
15 u32 supported_params;
16};
17
18#define COALESCE_REPDATA(__reply_base) \
19 container_of(__reply_base, struct coalesce_reply_data, base)
20
21#define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS
22static u32 attr_to_mask(unsigned int attr_type)
23{
24 return BIT(attr_type - __SUPPORTED_OFFSET);
25}
26
27/* build time check that indices in ethtool_ops::supported_coalesce_params
28 * match corresponding attribute types with an offset
29 */
30#define __CHECK_SUPPORTED_OFFSET(x) \
31 static_assert((ETHTOOL_ ## x) == \
32 BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET))
33__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS);
34__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES);
35__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ);
36__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ);
37__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS);
38__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES);
39__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ);
40__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ);
41__CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS);
42__CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX);
43__CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX);
44__CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW);
45__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW);
46__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW);
47__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW);
48__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW);
49__CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH);
50__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH);
51__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH);
52__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH);
53__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH);
54__CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL);
55
56const struct nla_policy ethnl_coalesce_get_policy[] = {
57 [ETHTOOL_A_COALESCE_HEADER] =
58 NLA_POLICY_NESTED(ethnl_header_policy),
59};
60
61static int coalesce_prepare_data(const struct ethnl_req_info *req_base,
62 struct ethnl_reply_data *reply_base,
63 const struct genl_info *info)
64{
65 struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
66 struct net_device *dev = reply_base->dev;
67 int ret;
68
69 if (!dev->ethtool_ops->get_coalesce)
70 return -EOPNOTSUPP;
71 data->supported_params = dev->ethtool_ops->supported_coalesce_params;
72 ret = ethnl_ops_begin(dev);
73 if (ret < 0)
74 return ret;
75 ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce,
76 &data->kernel_coalesce,
77 info->extack);
78 ethnl_ops_complete(dev);
79
80 return ret;
81}
82
83static int coalesce_reply_size(const struct ethnl_req_info *req_base,
84 const struct ethnl_reply_data *reply_base)
85{
86 int modersz = nla_total_size(0) + /* _PROFILE_IRQ_MODERATION, nest */
87 nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_USEC */
88 nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_PKTS */
89 nla_total_size(sizeof(u32)); /* _IRQ_MODERATION_COMPS */
90
91 int total_modersz = nla_total_size(0) + /* _{R,T}X_PROFILE, nest */
92 modersz * NET_DIM_PARAMS_NUM_PROFILES;
93
94 return nla_total_size(sizeof(u32)) + /* _RX_USECS */
95 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES */
96 nla_total_size(sizeof(u32)) + /* _RX_USECS_IRQ */
97 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_IRQ */
98 nla_total_size(sizeof(u32)) + /* _TX_USECS */
99 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES */
100 nla_total_size(sizeof(u32)) + /* _TX_USECS_IRQ */
101 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_IRQ */
102 nla_total_size(sizeof(u32)) + /* _STATS_BLOCK_USECS */
103 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_RX */
104 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_TX */
105 nla_total_size(sizeof(u32)) + /* _PKT_RATE_LOW */
106 nla_total_size(sizeof(u32)) + /* _RX_USECS_LOW */
107 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_LOW */
108 nla_total_size(sizeof(u32)) + /* _TX_USECS_LOW */
109 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_LOW */
110 nla_total_size(sizeof(u32)) + /* _PKT_RATE_HIGH */
111 nla_total_size(sizeof(u32)) + /* _RX_USECS_HIGH */
112 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */
113 nla_total_size(sizeof(u32)) + /* _TX_USECS_HIGH */
114 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */
115 nla_total_size(sizeof(u32)) + /* _RATE_SAMPLE_INTERVAL */
116 nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_TX */
117 nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_RX */
118 nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_BYTES */
119 nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_FRAMES */
120 nla_total_size(sizeof(u32)) + /* _TX_AGGR_TIME_USECS */
121 total_modersz * 2; /* _{R,T}X_PROFILE */
122}
123
124static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val,
125 u32 supported_params)
126{
127 if (!val && !(supported_params & attr_to_mask(attr_type)))
128 return false;
129 return nla_put_u32(skb, attr_type, val);
130}
131
132static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val,
133 u32 supported_params)
134{
135 if (!val && !(supported_params & attr_to_mask(attr_type)))
136 return false;
137 return nla_put_u8(skb, attr_type, !!val);
138}
139
140/**
141 * coalesce_put_profile - fill reply with a nla nest with four child nla nests.
142 * @skb: socket buffer the message is stored in
143 * @attr_type: nest attr type ETHTOOL_A_COALESCE_*X_PROFILE
144 * @profile: data passed to userspace
145 * @coal_flags: modifiable parameters supported by the driver
146 *
147 * Put a dim profile nest attribute. Refer to ETHTOOL_A_PROFILE_IRQ_MODERATION.
148 *
149 * Return: 0 on success or a negative error code.
150 */
151static int coalesce_put_profile(struct sk_buff *skb, u16 attr_type,
152 const struct dim_cq_moder *profile,
153 u8 coal_flags)
154{
155 struct nlattr *profile_attr, *moder_attr;
156 int i, ret;
157
158 if (!profile || !coal_flags)
159 return 0;
160
161 profile_attr = nla_nest_start(skb, attr_type);
162 if (!profile_attr)
163 return -EMSGSIZE;
164
165 for (i = 0; i < NET_DIM_PARAMS_NUM_PROFILES; i++) {
166 moder_attr = nla_nest_start(skb,
167 ETHTOOL_A_PROFILE_IRQ_MODERATION);
168 if (!moder_attr) {
169 ret = -EMSGSIZE;
170 goto cancel_profile;
171 }
172
173 if (coal_flags & DIM_COALESCE_USEC) {
174 ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_USEC,
175 profile[i].usec);
176 if (ret)
177 goto cancel_moder;
178 }
179
180 if (coal_flags & DIM_COALESCE_PKTS) {
181 ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_PKTS,
182 profile[i].pkts);
183 if (ret)
184 goto cancel_moder;
185 }
186
187 if (coal_flags & DIM_COALESCE_COMPS) {
188 ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_COMPS,
189 profile[i].comps);
190 if (ret)
191 goto cancel_moder;
192 }
193
194 nla_nest_end(skb, moder_attr);
195 }
196
197 nla_nest_end(skb, profile_attr);
198
199 return 0;
200
201cancel_moder:
202 nla_nest_cancel(skb, moder_attr);
203cancel_profile:
204 nla_nest_cancel(skb, profile_attr);
205 return ret;
206}
207
208static int coalesce_fill_reply(struct sk_buff *skb,
209 const struct ethnl_req_info *req_base,
210 const struct ethnl_reply_data *reply_base)
211{
212 const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
213 const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce;
214 const struct ethtool_coalesce *coal = &data->coalesce;
215 u32 supported = data->supported_params;
216 struct dim_irq_moder *moder;
217 int ret = 0;
218
219 if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS,
220 coal->rx_coalesce_usecs, supported) ||
221 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES,
222 coal->rx_max_coalesced_frames, supported) ||
223 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ,
224 coal->rx_coalesce_usecs_irq, supported) ||
225 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ,
226 coal->rx_max_coalesced_frames_irq, supported) ||
227 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS,
228 coal->tx_coalesce_usecs, supported) ||
229 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES,
230 coal->tx_max_coalesced_frames, supported) ||
231 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ,
232 coal->tx_coalesce_usecs_irq, supported) ||
233 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ,
234 coal->tx_max_coalesced_frames_irq, supported) ||
235 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS,
236 coal->stats_block_coalesce_usecs, supported) ||
237 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX,
238 coal->use_adaptive_rx_coalesce, supported) ||
239 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX,
240 coal->use_adaptive_tx_coalesce, supported) ||
241 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW,
242 coal->pkt_rate_low, supported) ||
243 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW,
244 coal->rx_coalesce_usecs_low, supported) ||
245 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW,
246 coal->rx_max_coalesced_frames_low, supported) ||
247 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW,
248 coal->tx_coalesce_usecs_low, supported) ||
249 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW,
250 coal->tx_max_coalesced_frames_low, supported) ||
251 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH,
252 coal->pkt_rate_high, supported) ||
253 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH,
254 coal->rx_coalesce_usecs_high, supported) ||
255 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH,
256 coal->rx_max_coalesced_frames_high, supported) ||
257 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH,
258 coal->tx_coalesce_usecs_high, supported) ||
259 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH,
260 coal->tx_max_coalesced_frames_high, supported) ||
261 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL,
262 coal->rate_sample_interval, supported) ||
263 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_TX,
264 kcoal->use_cqe_mode_tx, supported) ||
265 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_RX,
266 kcoal->use_cqe_mode_rx, supported) ||
267 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES,
268 kcoal->tx_aggr_max_bytes, supported) ||
269 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES,
270 kcoal->tx_aggr_max_frames, supported) ||
271 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS,
272 kcoal->tx_aggr_time_usecs, supported))
273 return -EMSGSIZE;
274
275 if (!req_base->dev || !req_base->dev->irq_moder)
276 return 0;
277
278 moder = req_base->dev->irq_moder;
279 rcu_read_lock();
280 if (moder->profile_flags & DIM_PROFILE_RX) {
281 ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_RX_PROFILE,
282 rcu_dereference(moder->rx_profile),
283 moder->coal_flags);
284 if (ret)
285 goto out;
286 }
287
288 if (moder->profile_flags & DIM_PROFILE_TX)
289 ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_TX_PROFILE,
290 rcu_dereference(moder->tx_profile),
291 moder->coal_flags);
292
293out:
294 rcu_read_unlock();
295 return ret;
296}
297
298/* COALESCE_SET */
299
300static const struct nla_policy coalesce_irq_moderation_policy[] = {
301 [ETHTOOL_A_IRQ_MODERATION_USEC] = { .type = NLA_U32 },
302 [ETHTOOL_A_IRQ_MODERATION_PKTS] = { .type = NLA_U32 },
303 [ETHTOOL_A_IRQ_MODERATION_COMPS] = { .type = NLA_U32 },
304};
305
306static const struct nla_policy coalesce_profile_policy[] = {
307 [ETHTOOL_A_PROFILE_IRQ_MODERATION] =
308 NLA_POLICY_NESTED(coalesce_irq_moderation_policy),
309};
310
311const struct nla_policy ethnl_coalesce_set_policy[] = {
312 [ETHTOOL_A_COALESCE_HEADER] =
313 NLA_POLICY_NESTED(ethnl_header_policy),
314 [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_U32 },
315 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_U32 },
316 [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_U32 },
317 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_U32 },
318 [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_U32 },
319 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_U32 },
320 [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_U32 },
321 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_U32 },
322 [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_U32 },
323 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_U8 },
324 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_U8 },
325 [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_U32 },
326 [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_U32 },
327 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_U32 },
328 [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_U32 },
329 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_U32 },
330 [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_U32 },
331 [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_U32 },
332 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 },
333 [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 },
334 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 },
335 [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 },
336 [ETHTOOL_A_COALESCE_USE_CQE_MODE_TX] = NLA_POLICY_MAX(NLA_U8, 1),
337 [ETHTOOL_A_COALESCE_USE_CQE_MODE_RX] = NLA_POLICY_MAX(NLA_U8, 1),
338 [ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 },
339 [ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 },
340 [ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 },
341 [ETHTOOL_A_COALESCE_RX_PROFILE] =
342 NLA_POLICY_NESTED(coalesce_profile_policy),
343 [ETHTOOL_A_COALESCE_TX_PROFILE] =
344 NLA_POLICY_NESTED(coalesce_profile_policy),
345};
346
347static int
348ethnl_set_coalesce_validate(struct ethnl_req_info *req_info,
349 struct genl_info *info)
350{
351 const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
352 struct dim_irq_moder *irq_moder = req_info->dev->irq_moder;
353 struct nlattr **tb = info->attrs;
354 u32 supported_params;
355 u16 a;
356
357 if (!ops->get_coalesce || !ops->set_coalesce)
358 return -EOPNOTSUPP;
359
360 /* make sure that only supported parameters are present */
361 supported_params = ops->supported_coalesce_params;
362 if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_RX)
363 supported_params |= ETHTOOL_COALESCE_RX_PROFILE;
364
365 if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_TX)
366 supported_params |= ETHTOOL_COALESCE_TX_PROFILE;
367
368 for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++)
369 if (tb[a] && !(supported_params & attr_to_mask(a))) {
370 NL_SET_ERR_MSG_ATTR(info->extack, tb[a],
371 "cannot modify an unsupported parameter");
372 return -EINVAL;
373 }
374
375 return 1;
376}
377
378/**
379 * ethnl_update_irq_moder - update a specific field in the given profile
380 * @irq_moder: place that collects dim related information
381 * @irq_field: field in profile to modify
382 * @attr_type: attr type ETHTOOL_A_IRQ_MODERATION_*
383 * @tb: netlink attribute with new values or null
384 * @coal_bit: DIM_COALESCE_* bit from coal_flags
385 * @mod: pointer to bool for modification tracking
386 * @extack: netlink extended ack
387 *
388 * Return: 0 on success or a negative error code.
389 */
390static int ethnl_update_irq_moder(struct dim_irq_moder *irq_moder,
391 u16 *irq_field, u16 attr_type,
392 struct nlattr **tb,
393 u8 coal_bit, bool *mod,
394 struct netlink_ext_ack *extack)
395{
396 int ret = 0;
397 u32 val;
398
399 if (!tb[attr_type])
400 return 0;
401
402 if (irq_moder->coal_flags & coal_bit) {
403 val = nla_get_u32(tb[attr_type]);
404 if (*irq_field == val)
405 return 0;
406
407 *irq_field = val;
408 *mod = true;
409 } else {
410 NL_SET_BAD_ATTR(extack, tb[attr_type]);
411 ret = -EOPNOTSUPP;
412 }
413
414 return ret;
415}
416
417/**
418 * ethnl_update_profile - get a profile nest with child nests from userspace.
419 * @dev: netdevice to update the profile
420 * @dst: profile get from the driver and modified by ethnl_update_profile.
421 * @nests: nest attr ETHTOOL_A_COALESCE_*X_PROFILE to set profile.
422 * @mod: pointer to bool for modification tracking
423 * @extack: Netlink extended ack
424 *
425 * Layout of nests:
426 * Nested ETHTOOL_A_COALESCE_*X_PROFILE attr
427 * Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr
428 * ETHTOOL_A_IRQ_MODERATION_USEC attr
429 * ETHTOOL_A_IRQ_MODERATION_PKTS attr
430 * ETHTOOL_A_IRQ_MODERATION_COMPS attr
431 * ...
432 * Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr
433 * ETHTOOL_A_IRQ_MODERATION_USEC attr
434 * ETHTOOL_A_IRQ_MODERATION_PKTS attr
435 * ETHTOOL_A_IRQ_MODERATION_COMPS attr
436 *
437 * Return: 0 on success or a negative error code.
438 */
439static int ethnl_update_profile(struct net_device *dev,
440 struct dim_cq_moder __rcu **dst,
441 const struct nlattr *nests,
442 bool *mod,
443 struct netlink_ext_ack *extack)
444{
445 int len_irq_moder = ARRAY_SIZE(coalesce_irq_moderation_policy);
446 struct nlattr *tb[ARRAY_SIZE(coalesce_irq_moderation_policy)];
447 struct dim_irq_moder *irq_moder = dev->irq_moder;
448 struct dim_cq_moder *new_profile, *old_profile;
449 int ret, rem, i = 0, len;
450 struct nlattr *nest;
451
452 if (!nests)
453 return 0;
454
455 if (!*dst)
456 return -EOPNOTSUPP;
457
458 old_profile = rtnl_dereference(*dst);
459 len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*old_profile);
460 new_profile = kmemdup(old_profile, len, GFP_KERNEL);
461 if (!new_profile)
462 return -ENOMEM;
463
464 nla_for_each_nested_type(nest, ETHTOOL_A_PROFILE_IRQ_MODERATION,
465 nests, rem) {
466 ret = nla_parse_nested(tb, len_irq_moder - 1, nest,
467 coalesce_irq_moderation_policy,
468 extack);
469 if (ret)
470 goto err_out;
471
472 ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].usec,
473 ETHTOOL_A_IRQ_MODERATION_USEC,
474 tb, DIM_COALESCE_USEC,
475 mod, extack);
476 if (ret)
477 goto err_out;
478
479 ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].pkts,
480 ETHTOOL_A_IRQ_MODERATION_PKTS,
481 tb, DIM_COALESCE_PKTS,
482 mod, extack);
483 if (ret)
484 goto err_out;
485
486 ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].comps,
487 ETHTOOL_A_IRQ_MODERATION_COMPS,
488 tb, DIM_COALESCE_COMPS,
489 mod, extack);
490 if (ret)
491 goto err_out;
492
493 i++;
494 }
495
496 /* After the profile is modified, dim itself is a dynamic
497 * mechanism and will quickly fit to the appropriate
498 * coalescing parameters according to the new profile.
499 */
500 rcu_assign_pointer(*dst, new_profile);
501 kfree_rcu(old_profile, rcu);
502
503 return 0;
504
505err_out:
506 kfree(new_profile);
507 return ret;
508}
509
510static int
511__ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info,
512 bool *dual_change)
513{
514 struct kernel_ethtool_coalesce kernel_coalesce = {};
515 struct net_device *dev = req_info->dev;
516 struct ethtool_coalesce coalesce = {};
517 bool mod_mode = false, mod = false;
518 struct nlattr **tb = info->attrs;
519 int ret;
520
521 ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce,
522 info->extack);
523 if (ret < 0)
524 return ret;
525
526 /* Update values */
527 ethnl_update_u32(&coalesce.rx_coalesce_usecs,
528 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod);
529 ethnl_update_u32(&coalesce.rx_max_coalesced_frames,
530 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod);
531 ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq,
532 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod);
533 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq,
534 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod);
535 ethnl_update_u32(&coalesce.tx_coalesce_usecs,
536 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod);
537 ethnl_update_u32(&coalesce.tx_max_coalesced_frames,
538 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod);
539 ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq,
540 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod);
541 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq,
542 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod);
543 ethnl_update_u32(&coalesce.stats_block_coalesce_usecs,
544 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod);
545 ethnl_update_u32(&coalesce.pkt_rate_low,
546 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod);
547 ethnl_update_u32(&coalesce.rx_coalesce_usecs_low,
548 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod);
549 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low,
550 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod);
551 ethnl_update_u32(&coalesce.tx_coalesce_usecs_low,
552 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod);
553 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low,
554 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod);
555 ethnl_update_u32(&coalesce.pkt_rate_high,
556 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod);
557 ethnl_update_u32(&coalesce.rx_coalesce_usecs_high,
558 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod);
559 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high,
560 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod);
561 ethnl_update_u32(&coalesce.tx_coalesce_usecs_high,
562 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod);
563 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high,
564 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod);
565 ethnl_update_u32(&coalesce.rate_sample_interval,
566 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod);
567 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_bytes,
568 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES], &mod);
569 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_frames,
570 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], &mod);
571 ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs,
572 tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod);
573
574 if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_RX) {
575 ret = ethnl_update_profile(dev, &dev->irq_moder->rx_profile,
576 tb[ETHTOOL_A_COALESCE_RX_PROFILE],
577 &mod, info->extack);
578 if (ret < 0)
579 return ret;
580 }
581
582 if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_TX) {
583 ret = ethnl_update_profile(dev, &dev->irq_moder->tx_profile,
584 tb[ETHTOOL_A_COALESCE_TX_PROFILE],
585 &mod, info->extack);
586 if (ret < 0)
587 return ret;
588 }
589
590 /* Update operation modes */
591 ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce,
592 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod_mode);
593 ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce,
594 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod_mode);
595 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx,
596 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod_mode);
597 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx,
598 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &mod_mode);
599
600 *dual_change = mod && mod_mode;
601 if (!mod && !mod_mode)
602 return 0;
603
604 ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce,
605 info->extack);
606 return ret < 0 ? ret : 1;
607}
608
609static int
610ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info)
611{
612 bool dual_change;
613 int err, ret;
614
615 /* SET_COALESCE may change operation mode and parameters in one call.
616 * Changing operation mode may cause the driver to reset the parameter
617 * values, and therefore ignore user input (driver does not know which
618 * parameters come from user and which are echoed back from ->get).
619 * To not complicate the drivers if user tries to change both the mode
620 * and parameters at once - call the driver twice.
621 */
622 err = __ethnl_set_coalesce(req_info, info, &dual_change);
623 if (err < 0)
624 return err;
625 ret = err;
626
627 if (ret && dual_change) {
628 err = __ethnl_set_coalesce(req_info, info, &dual_change);
629 if (err < 0)
630 return err;
631 }
632 return ret;
633}
634
635const struct ethnl_request_ops ethnl_coalesce_request_ops = {
636 .request_cmd = ETHTOOL_MSG_COALESCE_GET,
637 .reply_cmd = ETHTOOL_MSG_COALESCE_GET_REPLY,
638 .hdr_attr = ETHTOOL_A_COALESCE_HEADER,
639 .req_info_size = sizeof(struct coalesce_req_info),
640 .reply_data_size = sizeof(struct coalesce_reply_data),
641
642 .prepare_data = coalesce_prepare_data,
643 .reply_size = coalesce_reply_size,
644 .fill_reply = coalesce_fill_reply,
645
646 .set_validate = ethnl_set_coalesce_validate,
647 .set = ethnl_set_coalesce,
648 .set_ntf_cmd = ETHTOOL_MSG_COALESCE_NTF,
649};
1// SPDX-License-Identifier: GPL-2.0-only
2
3#include "netlink.h"
4#include "common.h"
5
6struct coalesce_req_info {
7 struct ethnl_req_info base;
8};
9
10struct 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
21static 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
55const struct nla_policy ethnl_coalesce_get_policy[] = {
56 [ETHTOOL_A_COALESCE_HEADER] =
57 NLA_POLICY_NESTED(ethnl_header_policy),
58};
59
60static 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
82static 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(sizeof(u32)) + /* _RX_USECS */
86 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES */
87 nla_total_size(sizeof(u32)) + /* _RX_USECS_IRQ */
88 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_IRQ */
89 nla_total_size(sizeof(u32)) + /* _TX_USECS */
90 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES */
91 nla_total_size(sizeof(u32)) + /* _TX_USECS_IRQ */
92 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_IRQ */
93 nla_total_size(sizeof(u32)) + /* _STATS_BLOCK_USECS */
94 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_RX */
95 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_TX */
96 nla_total_size(sizeof(u32)) + /* _PKT_RATE_LOW */
97 nla_total_size(sizeof(u32)) + /* _RX_USECS_LOW */
98 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_LOW */
99 nla_total_size(sizeof(u32)) + /* _TX_USECS_LOW */
100 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_LOW */
101 nla_total_size(sizeof(u32)) + /* _PKT_RATE_HIGH */
102 nla_total_size(sizeof(u32)) + /* _RX_USECS_HIGH */
103 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */
104 nla_total_size(sizeof(u32)) + /* _TX_USECS_HIGH */
105 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */
106 nla_total_size(sizeof(u32)) + /* _RATE_SAMPLE_INTERVAL */
107 nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_TX */
108 nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_RX */
109 nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_BYTES */
110 nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_FRAMES */
111 nla_total_size(sizeof(u32)); /* _TX_AGGR_TIME_USECS */
112}
113
114static 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, attr_type, val);
120}
121
122static 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, attr_type, !!val);
128}
129
130static 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, ETHTOOL_A_COALESCE_RX_USECS,
140 coal->rx_coalesce_usecs, supported) ||
141 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES,
142 coal->rx_max_coalesced_frames, supported) ||
143 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ,
144 coal->rx_coalesce_usecs_irq, supported) ||
145 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ,
146 coal->rx_max_coalesced_frames_irq, supported) ||
147 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS,
148 coal->tx_coalesce_usecs, supported) ||
149 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES,
150 coal->tx_max_coalesced_frames, supported) ||
151 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ,
152 coal->tx_coalesce_usecs_irq, supported) ||
153 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ,
154 coal->tx_max_coalesced_frames_irq, supported) ||
155 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS,
156 coal->stats_block_coalesce_usecs, supported) ||
157 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX,
158 coal->use_adaptive_rx_coalesce, supported) ||
159 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX,
160 coal->use_adaptive_tx_coalesce, supported) ||
161 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW,
162 coal->pkt_rate_low, supported) ||
163 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW,
164 coal->rx_coalesce_usecs_low, supported) ||
165 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW,
166 coal->rx_max_coalesced_frames_low, supported) ||
167 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW,
168 coal->tx_coalesce_usecs_low, supported) ||
169 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW,
170 coal->tx_max_coalesced_frames_low, supported) ||
171 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH,
172 coal->pkt_rate_high, supported) ||
173 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH,
174 coal->rx_coalesce_usecs_high, supported) ||
175 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH,
176 coal->rx_max_coalesced_frames_high, supported) ||
177 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH,
178 coal->tx_coalesce_usecs_high, supported) ||
179 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH,
180 coal->tx_max_coalesced_frames_high, supported) ||
181 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL,
182 coal->rate_sample_interval, supported) ||
183 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_TX,
184 kcoal->use_cqe_mode_tx, supported) ||
185 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_RX,
186 kcoal->use_cqe_mode_rx, supported) ||
187 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES,
188 kcoal->tx_aggr_max_bytes, supported) ||
189 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES,
190 kcoal->tx_aggr_max_frames, supported) ||
191 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS,
192 kcoal->tx_aggr_time_usecs, supported))
193 return -EMSGSIZE;
194
195 return 0;
196}
197
198/* COALESCE_SET */
199
200const 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
232static int
233ethnl_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(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
256static 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(&coalesce.rx_coalesce_usecs,
274 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod);
275 ethnl_update_u32(&coalesce.rx_max_coalesced_frames,
276 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod);
277 ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq,
278 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod);
279 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq,
280 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod);
281 ethnl_update_u32(&coalesce.tx_coalesce_usecs,
282 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod);
283 ethnl_update_u32(&coalesce.tx_max_coalesced_frames,
284 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod);
285 ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq,
286 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod);
287 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq,
288 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod);
289 ethnl_update_u32(&coalesce.stats_block_coalesce_usecs,
290 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod);
291 ethnl_update_u32(&coalesce.pkt_rate_low,
292 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod);
293 ethnl_update_u32(&coalesce.rx_coalesce_usecs_low,
294 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod);
295 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low,
296 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod);
297 ethnl_update_u32(&coalesce.tx_coalesce_usecs_low,
298 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod);
299 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low,
300 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod);
301 ethnl_update_u32(&coalesce.pkt_rate_high,
302 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod);
303 ethnl_update_u32(&coalesce.rx_coalesce_usecs_high,
304 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod);
305 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high,
306 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod);
307 ethnl_update_u32(&coalesce.tx_coalesce_usecs_high,
308 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod);
309 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high,
310 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod);
311 ethnl_update_u32(&coalesce.rate_sample_interval,
312 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod);
313 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_bytes,
314 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES], &mod);
315 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_frames,
316 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], &mod);
317 ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs,
318 tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod);
319
320 /* Update operation modes */
321 ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce,
322 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod_mode);
323 ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce,
324 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod_mode);
325 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx,
326 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod_mode);
327 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx,
328 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &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
339static int
340ethnl_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);
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);
359 if (err < 0)
360 return err;
361 }
362 return ret;
363}
364
365const 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};