~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ethtool/coalesce.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php