Skip to content

Commit 0980bfc

Browse files
mkubecekdavem330
authored andcommitted
ethtool: set netdev features with FEATURES_SET request
Implement FEATURES_SET netlink request to set network device features. These are traditionally set using ETHTOOL_SFEATURES ioctl request. Actual change is subject to netdev_change_features() sanity checks so that it can differ from what was requested. Unlike with most other SET requests, in addition to error code and optional extack, kernel provides an optional reply message (ETHTOOL_MSG_FEATURES_SET_REPLY) in the same format but with different semantics: information about difference between user request and actual result and difference between old and new state of dev->features. This reply message can be suppressed by setting ETHTOOL_FLAG_OMIT_REPLY flag in request header. Signed-off-by: Michal Kubecek <mkubecek@suse.cz> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 88db6d1 commit 0980bfc

File tree

5 files changed

+224
-9
lines changed

5 files changed

+224
-9
lines changed

Documentation/networking/ethtool-netlink.rst

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ Userspace to kernel:
190190
``ETHTOOL_MSG_WOL_GET`` get wake-on-lan settings
191191
``ETHTOOL_MSG_WOL_SET`` set wake-on-lan settings
192192
``ETHTOOL_MSG_FEATURES_GET`` get device features
193+
``ETHTOOL_MSG_FEATURES_SET`` set device features
193194
===================================== ================================
194195

195196
Kernel to userspace:
@@ -206,6 +207,7 @@ Kernel to userspace:
206207
``ETHTOOL_MSG_WOL_GET_REPLY`` wake-on-lan settings
207208
``ETHTOOL_MSG_WOL_NTF`` wake-on-lan settings notification
208209
``ETHTOOL_MSG_FEATURES_GET_REPLY`` device features
210+
``ETHTOOL_MSG_FEATURES_SET_REPLY`` optional reply to FEATURES_SET
209211
===================================== =================================
210212

211213
``GET`` requests are sent by userspace applications to retrieve device
@@ -554,6 +556,42 @@ provide all names when using verbose bitmap format), the other three use no
554556
mask (simple bit lists).
555557

556558

559+
FEATURES_SET
560+
============
561+
562+
Request to set netdev features like ``ETHTOOL_SFEATURES`` ioctl request.
563+
564+
Request contents:
565+
566+
==================================== ====== ==========================
567+
``ETHTOOL_A_FEATURES_HEADER`` nested request header
568+
``ETHTOOL_A_FEATURES_WANTED`` bitset requested features
569+
==================================== ====== ==========================
570+
571+
Kernel response contents:
572+
573+
==================================== ====== ==========================
574+
``ETHTOOL_A_FEATURES_HEADER`` nested reply header
575+
``ETHTOOL_A_FEATURES_WANTED`` bitset diff wanted vs. result
576+
``ETHTOOL_A_FEATURES_ACTIVE`` bitset diff old vs. new active
577+
==================================== ====== ==========================
578+
579+
Request constains only one bitset which can be either value/mask pair (request
580+
to change specific feature bits and leave the rest) or only a value (request
581+
to set all features to specified set).
582+
583+
As request is subject to netdev_change_features() sanity checks, optional
584+
kernel reply (can be suppressed by ``ETHTOOL_FLAG_OMIT_REPLY`` flag in request
585+
header) informs client about the actual result. ``ETHTOOL_A_FEATURES_WANTED``
586+
reports the difference between client request and actual result: mask consists
587+
of bits which differ between requested features and result (dev->features
588+
after the operation), value consists of values of these bits in the request
589+
(i.e. negated values from resulting features). ``ETHTOOL_A_FEATURES_ACTIVE``
590+
reports the difference between old and new dev->features: mask consists of
591+
bits which have changed, values are their values in new dev->features (after
592+
the operation).
593+
594+
557595
Request translation
558596
===================
559597

@@ -585,30 +623,30 @@ have their netlink replacement yet.
585623
``ETHTOOL_GPAUSEPARAM`` n/a
586624
``ETHTOOL_SPAUSEPARAM`` n/a
587625
``ETHTOOL_GRXCSUM`` ``ETHTOOL_MSG_FEATURES_GET``
588-
``ETHTOOL_SRXCSUM`` n/a
626+
``ETHTOOL_SRXCSUM`` ``ETHTOOL_MSG_FEATURES_SET``
589627
``ETHTOOL_GTXCSUM`` ``ETHTOOL_MSG_FEATURES_GET``
590-
``ETHTOOL_STXCSUM`` n/a
628+
``ETHTOOL_STXCSUM`` ``ETHTOOL_MSG_FEATURES_SET``
591629
``ETHTOOL_GSG`` ``ETHTOOL_MSG_FEATURES_GET``
592-
``ETHTOOL_SSG`` n/a
630+
``ETHTOOL_SSG`` ``ETHTOOL_MSG_FEATURES_SET``
593631
``ETHTOOL_TEST`` n/a
594632
``ETHTOOL_GSTRINGS`` ``ETHTOOL_MSG_STRSET_GET``
595633
``ETHTOOL_PHYS_ID`` n/a
596634
``ETHTOOL_GSTATS`` n/a
597635
``ETHTOOL_GTSO`` ``ETHTOOL_MSG_FEATURES_GET``
598-
``ETHTOOL_STSO`` n/a
636+
``ETHTOOL_STSO`` ``ETHTOOL_MSG_FEATURES_SET``
599637
``ETHTOOL_GPERMADDR`` rtnetlink ``RTM_GETLINK``
600638
``ETHTOOL_GUFO`` ``ETHTOOL_MSG_FEATURES_GET``
601-
``ETHTOOL_SUFO`` n/a
639+
``ETHTOOL_SUFO`` ``ETHTOOL_MSG_FEATURES_SET``
602640
``ETHTOOL_GGSO`` ``ETHTOOL_MSG_FEATURES_GET``
603-
``ETHTOOL_SGSO`` n/a
641+
``ETHTOOL_SGSO`` ``ETHTOOL_MSG_FEATURES_SET``
604642
``ETHTOOL_GFLAGS`` ``ETHTOOL_MSG_FEATURES_GET``
605-
``ETHTOOL_SFLAGS`` n/a
643+
``ETHTOOL_SFLAGS`` ``ETHTOOL_MSG_FEATURES_SET``
606644
``ETHTOOL_GPFLAGS`` n/a
607645
``ETHTOOL_SPFLAGS`` n/a
608646
``ETHTOOL_GRXFH`` n/a
609647
``ETHTOOL_SRXFH`` n/a
610648
``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET``
611-
``ETHTOOL_SGRO`` n/a
649+
``ETHTOOL_SGRO`` ``ETHTOOL_MSG_FEATURES_SET``
612650
``ETHTOOL_GRXRINGS`` n/a
613651
``ETHTOOL_GRXCLSRLCNT`` n/a
614652
``ETHTOOL_GRXCLSRULE`` n/a
@@ -623,7 +661,7 @@ have their netlink replacement yet.
623661
``ETHTOOL_GRXFHINDIR`` n/a
624662
``ETHTOOL_SRXFHINDIR`` n/a
625663
``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET``
626-
``ETHTOOL_SFEATURES`` n/a
664+
``ETHTOOL_SFEATURES`` ``ETHTOOL_MSG_FEATURES_SET``
627665
``ETHTOOL_GCHANNELS`` n/a
628666
``ETHTOOL_SCHANNELS`` n/a
629667
``ETHTOOL_SET_DUMP`` n/a

include/uapi/linux/ethtool_netlink.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum {
2525
ETHTOOL_MSG_WOL_GET,
2626
ETHTOOL_MSG_WOL_SET,
2727
ETHTOOL_MSG_FEATURES_GET,
28+
ETHTOOL_MSG_FEATURES_SET,
2829

2930
/* add new constants above here */
3031
__ETHTOOL_MSG_USER_CNT,
@@ -45,6 +46,7 @@ enum {
4546
ETHTOOL_MSG_WOL_GET_REPLY,
4647
ETHTOOL_MSG_WOL_NTF,
4748
ETHTOOL_MSG_FEATURES_GET_REPLY,
49+
ETHTOOL_MSG_FEATURES_SET_REPLY,
4850

4951
/* add new constants above here */
5052
__ETHTOOL_MSG_KERNEL_CNT,

net/ethtool/features.c

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,172 @@ const struct ethnl_request_ops ethnl_features_request_ops = {
129129
.reply_size = features_reply_size,
130130
.fill_reply = features_fill_reply,
131131
};
132+
133+
/* FEATURES_SET */
134+
135+
static const struct nla_policy
136+
features_set_policy[ETHTOOL_A_FEATURES_MAX + 1] = {
137+
[ETHTOOL_A_FEATURES_UNSPEC] = { .type = NLA_REJECT },
138+
[ETHTOOL_A_FEATURES_HEADER] = { .type = NLA_NESTED },
139+
[ETHTOOL_A_FEATURES_HW] = { .type = NLA_REJECT },
140+
[ETHTOOL_A_FEATURES_WANTED] = { .type = NLA_NESTED },
141+
[ETHTOOL_A_FEATURES_ACTIVE] = { .type = NLA_REJECT },
142+
[ETHTOOL_A_FEATURES_NOCHANGE] = { .type = NLA_REJECT },
143+
};
144+
145+
static void ethnl_features_to_bitmap(unsigned long *dest, netdev_features_t val)
146+
{
147+
const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT);
148+
unsigned int i;
149+
150+
bitmap_zero(dest, NETDEV_FEATURE_COUNT);
151+
for (i = 0; i < words; i++)
152+
dest[i] = (unsigned long)(val >> (i * BITS_PER_LONG));
153+
}
154+
155+
static netdev_features_t ethnl_bitmap_to_features(unsigned long *src)
156+
{
157+
const unsigned int nft_bits = sizeof(netdev_features_t) * BITS_PER_BYTE;
158+
const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT);
159+
netdev_features_t ret = 0;
160+
unsigned int i;
161+
162+
for (i = 0; i < words; i++)
163+
ret |= (netdev_features_t)(src[i]) << (i * BITS_PER_LONG);
164+
ret &= ~(netdev_features_t)0 >> (nft_bits - NETDEV_FEATURE_COUNT);
165+
return ret;
166+
}
167+
168+
static int features_send_reply(struct net_device *dev, struct genl_info *info,
169+
const unsigned long *wanted,
170+
const unsigned long *wanted_mask,
171+
const unsigned long *active,
172+
const unsigned long *active_mask, bool compact)
173+
{
174+
struct sk_buff *rskb;
175+
void *reply_payload;
176+
int reply_len = 0;
177+
int ret;
178+
179+
reply_len = ethnl_reply_header_size();
180+
ret = ethnl_bitset_size(wanted, wanted_mask, NETDEV_FEATURE_COUNT,
181+
netdev_features_strings, compact);
182+
if (ret < 0)
183+
goto err;
184+
reply_len += ret;
185+
ret = ethnl_bitset_size(active, active_mask, NETDEV_FEATURE_COUNT,
186+
netdev_features_strings, compact);
187+
if (ret < 0)
188+
goto err;
189+
reply_len += ret;
190+
191+
ret = -ENOMEM;
192+
rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_MSG_FEATURES_SET_REPLY,
193+
ETHTOOL_A_FEATURES_HEADER, info,
194+
&reply_payload);
195+
if (!rskb)
196+
goto err;
197+
198+
ret = ethnl_put_bitset(rskb, ETHTOOL_A_FEATURES_WANTED, wanted,
199+
wanted_mask, NETDEV_FEATURE_COUNT,
200+
netdev_features_strings, compact);
201+
if (ret < 0)
202+
goto nla_put_failure;
203+
ret = ethnl_put_bitset(rskb, ETHTOOL_A_FEATURES_ACTIVE, active,
204+
active_mask, NETDEV_FEATURE_COUNT,
205+
netdev_features_strings, compact);
206+
if (ret < 0)
207+
goto nla_put_failure;
208+
209+
genlmsg_end(rskb, reply_payload);
210+
ret = genlmsg_reply(rskb, info);
211+
return ret;
212+
213+
nla_put_failure:
214+
nlmsg_free(rskb);
215+
WARN_ONCE(1, "calculated message payload length (%d) not sufficient\n",
216+
reply_len);
217+
err:
218+
GENL_SET_ERR_MSG(info, "failed to send reply message");
219+
return ret;
220+
}
221+
222+
int ethnl_set_features(struct sk_buff *skb, struct genl_info *info)
223+
{
224+
DECLARE_BITMAP(wanted_diff_mask, NETDEV_FEATURE_COUNT);
225+
DECLARE_BITMAP(active_diff_mask, NETDEV_FEATURE_COUNT);
226+
DECLARE_BITMAP(old_active, NETDEV_FEATURE_COUNT);
227+
DECLARE_BITMAP(new_active, NETDEV_FEATURE_COUNT);
228+
DECLARE_BITMAP(req_wanted, NETDEV_FEATURE_COUNT);
229+
DECLARE_BITMAP(req_mask, NETDEV_FEATURE_COUNT);
230+
struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1];
231+
struct ethnl_req_info req_info = {};
232+
struct net_device *dev;
233+
int ret;
234+
235+
ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb,
236+
ETHTOOL_A_FEATURES_MAX, features_set_policy,
237+
info->extack);
238+
if (ret < 0)
239+
return ret;
240+
if (!tb[ETHTOOL_A_FEATURES_WANTED])
241+
return -EINVAL;
242+
ret = ethnl_parse_header_dev_get(&req_info,
243+
tb[ETHTOOL_A_FEATURES_HEADER],
244+
genl_info_net(info), info->extack,
245+
true);
246+
if (ret < 0)
247+
return ret;
248+
dev = req_info.dev;
249+
250+
rtnl_lock();
251+
ethnl_features_to_bitmap(old_active, dev->features);
252+
ret = ethnl_parse_bitset(req_wanted, req_mask, NETDEV_FEATURE_COUNT,
253+
tb[ETHTOOL_A_FEATURES_WANTED],
254+
netdev_features_strings, info->extack);
255+
if (ret < 0)
256+
goto out_rtnl;
257+
if (ethnl_bitmap_to_features(req_mask) & ~NETIF_F_ETHTOOL_BITS) {
258+
GENL_SET_ERR_MSG(info, "attempt to change non-ethtool features");
259+
ret = -EINVAL;
260+
goto out_rtnl;
261+
}
262+
263+
/* set req_wanted bits not in req_mask from old_active */
264+
bitmap_and(req_wanted, req_wanted, req_mask, NETDEV_FEATURE_COUNT);
265+
bitmap_andnot(new_active, old_active, req_mask, NETDEV_FEATURE_COUNT);
266+
bitmap_or(req_wanted, new_active, req_wanted, NETDEV_FEATURE_COUNT);
267+
if (bitmap_equal(req_wanted, old_active, NETDEV_FEATURE_COUNT)) {
268+
ret = 0;
269+
goto out_rtnl;
270+
}
271+
272+
dev->wanted_features = ethnl_bitmap_to_features(req_wanted);
273+
__netdev_update_features(dev);
274+
ethnl_features_to_bitmap(new_active, dev->features);
275+
276+
ret = 0;
277+
if (!(req_info.flags & ETHTOOL_FLAG_OMIT_REPLY)) {
278+
bool compact = req_info.flags & ETHTOOL_FLAG_COMPACT_BITSETS;
279+
280+
bitmap_xor(wanted_diff_mask, req_wanted, new_active,
281+
NETDEV_FEATURE_COUNT);
282+
bitmap_xor(active_diff_mask, old_active, new_active,
283+
NETDEV_FEATURE_COUNT);
284+
bitmap_and(wanted_diff_mask, wanted_diff_mask, req_mask,
285+
NETDEV_FEATURE_COUNT);
286+
bitmap_and(req_wanted, req_wanted, wanted_diff_mask,
287+
NETDEV_FEATURE_COUNT);
288+
bitmap_and(new_active, new_active, active_diff_mask,
289+
NETDEV_FEATURE_COUNT);
290+
291+
ret = features_send_reply(dev, info, req_wanted,
292+
wanted_diff_mask, new_active,
293+
active_diff_mask, compact);
294+
}
295+
296+
out_rtnl:
297+
rtnl_unlock();
298+
dev_put(dev);
299+
return ret;
300+
}

net/ethtool/netlink.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,11 @@ static const struct genl_ops ethtool_genl_ops[] = {
703703
.dumpit = ethnl_default_dumpit,
704704
.done = ethnl_default_done,
705705
},
706+
{
707+
.cmd = ETHTOOL_MSG_FEATURES_SET,
708+
.flags = GENL_UNS_ADMIN_PERM,
709+
.doit = ethnl_set_features,
710+
},
706711
};
707712

708713
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {

net/ethtool/netlink.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,5 +343,6 @@ int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info);
343343
int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);
344344
int ethnl_set_debug(struct sk_buff *skb, struct genl_info *info);
345345
int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info);
346+
int ethnl_set_features(struct sk_buff *skb, struct genl_info *info);
346347

347348
#endif /* _NET_ETHTOOL_NETLINK_H */

0 commit comments

Comments
 (0)