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