1 // SPDX-License-Identifier: GPL-2.0-only
2
3 #include <linux/dim.h>
4 #include "netlink.h"
5 #include "common.h"
6
7 struct coalesce_req_info {
8 struct ethnl_req_info base;
9 };
10
11 struct 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
attr_to_mask(unsigned int attr_type)22 static 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
56 const struct nla_policy ethnl_coalesce_get_policy[] = {
57 [ETHTOOL_A_COALESCE_HEADER] =
58 NLA_POLICY_NESTED(ethnl_header_policy),
59 };
60
coalesce_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)61 static 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
coalesce_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)83 static 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
coalesce_put_u32(struct sk_buff * skb,u16 attr_type,u32 val,u32 supported_params)124 static 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
coalesce_put_bool(struct sk_buff * skb,u16 attr_type,u32 val,u32 supported_params)132 static 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 */
coalesce_put_profile(struct sk_buff * skb,u16 attr_type,const struct dim_cq_moder * profile,u8 coal_flags)151 static 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
201 cancel_moder:
202 nla_nest_cancel(skb, moder_attr);
203 cancel_profile:
204 nla_nest_cancel(skb, profile_attr);
205 return ret;
206 }
207
coalesce_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)208 static 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
293 out:
294 rcu_read_unlock();
295 return ret;
296 }
297
298 /* COALESCE_SET */
299
300 static 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
306 static const struct nla_policy coalesce_profile_policy[] = {
307 [ETHTOOL_A_PROFILE_IRQ_MODERATION] =
308 NLA_POLICY_NESTED(coalesce_irq_moderation_policy),
309 };
310
311 const 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
347 static int
ethnl_set_coalesce_validate(struct ethnl_req_info * req_info,struct genl_info * info)348 ethnl_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 */
ethnl_update_irq_moder(struct dim_irq_moder * irq_moder,u16 * irq_field,u16 attr_type,struct nlattr ** tb,u8 coal_bit,bool * mod,struct netlink_ext_ack * extack)390 static 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 */
ethnl_update_profile(struct net_device * dev,struct dim_cq_moder __rcu ** dst,const struct nlattr * nests,bool * mod,struct netlink_ext_ack * extack)439 static 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
505 err_out:
506 kfree(new_profile);
507 return ret;
508 }
509
510 static int
__ethnl_set_coalesce(struct ethnl_req_info * req_info,struct genl_info * info,bool * dual_change)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
609 static int
ethnl_set_coalesce(struct ethnl_req_info * req_info,struct genl_info * info)610 ethnl_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
635 const 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 };
650