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