Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ethtool/coalesce.c
26278 views
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
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
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
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
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
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
*/
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
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
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
*/
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
*/
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
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
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
651