1 // SPDX-License-Identifier: GPL-2.0-only
7 struct features_req_info
{
8 struct ethnl_req_info base
;
11 struct features_reply_data
{
12 struct ethnl_reply_data base
;
13 u32 hw
[ETHTOOL_DEV_FEATURE_WORDS
];
14 u32 wanted
[ETHTOOL_DEV_FEATURE_WORDS
];
15 u32 active
[ETHTOOL_DEV_FEATURE_WORDS
];
16 u32 nochange
[ETHTOOL_DEV_FEATURE_WORDS
];
17 u32 all
[ETHTOOL_DEV_FEATURE_WORDS
];
20 #define FEATURES_REPDATA(__reply_base) \
21 container_of(__reply_base, struct features_reply_data, base)
23 const struct nla_policy ethnl_features_get_policy
[] = {
24 [ETHTOOL_A_FEATURES_HEADER
] =
25 NLA_POLICY_NESTED(ethnl_header_policy
),
28 static void ethnl_features_to_bitmap32(u32
*dest
, netdev_features_t src
)
32 for (i
= 0; i
< ETHTOOL_DEV_FEATURE_WORDS
; i
++)
33 dest
[i
] = src
>> (32 * i
);
36 static int features_prepare_data(const struct ethnl_req_info
*req_base
,
37 struct ethnl_reply_data
*reply_base
,
38 struct genl_info
*info
)
40 struct features_reply_data
*data
= FEATURES_REPDATA(reply_base
);
41 struct net_device
*dev
= reply_base
->dev
;
42 netdev_features_t all_features
;
44 ethnl_features_to_bitmap32(data
->hw
, dev
->hw_features
);
45 ethnl_features_to_bitmap32(data
->wanted
, dev
->wanted_features
);
46 ethnl_features_to_bitmap32(data
->active
, dev
->features
);
47 ethnl_features_to_bitmap32(data
->nochange
, NETIF_F_NEVER_CHANGE
);
48 all_features
= GENMASK_ULL(NETDEV_FEATURE_COUNT
- 1, 0);
49 ethnl_features_to_bitmap32(data
->all
, all_features
);
54 static int features_reply_size(const struct ethnl_req_info
*req_base
,
55 const struct ethnl_reply_data
*reply_base
)
57 const struct features_reply_data
*data
= FEATURES_REPDATA(reply_base
);
58 bool compact
= req_base
->flags
& ETHTOOL_FLAG_COMPACT_BITSETS
;
62 ret
= ethnl_bitset32_size(data
->hw
, data
->all
, NETDEV_FEATURE_COUNT
,
63 netdev_features_strings
, compact
);
67 ret
= ethnl_bitset32_size(data
->wanted
, NULL
, NETDEV_FEATURE_COUNT
,
68 netdev_features_strings
, compact
);
72 ret
= ethnl_bitset32_size(data
->active
, NULL
, NETDEV_FEATURE_COUNT
,
73 netdev_features_strings
, compact
);
77 ret
= ethnl_bitset32_size(data
->nochange
, NULL
, NETDEV_FEATURE_COUNT
,
78 netdev_features_strings
, compact
);
86 static int features_fill_reply(struct sk_buff
*skb
,
87 const struct ethnl_req_info
*req_base
,
88 const struct ethnl_reply_data
*reply_base
)
90 const struct features_reply_data
*data
= FEATURES_REPDATA(reply_base
);
91 bool compact
= req_base
->flags
& ETHTOOL_FLAG_COMPACT_BITSETS
;
94 ret
= ethnl_put_bitset32(skb
, ETHTOOL_A_FEATURES_HW
, data
->hw
,
95 data
->all
, NETDEV_FEATURE_COUNT
,
96 netdev_features_strings
, compact
);
99 ret
= ethnl_put_bitset32(skb
, ETHTOOL_A_FEATURES_WANTED
, data
->wanted
,
100 NULL
, NETDEV_FEATURE_COUNT
,
101 netdev_features_strings
, compact
);
104 ret
= ethnl_put_bitset32(skb
, ETHTOOL_A_FEATURES_ACTIVE
, data
->active
,
105 NULL
, NETDEV_FEATURE_COUNT
,
106 netdev_features_strings
, compact
);
109 return ethnl_put_bitset32(skb
, ETHTOOL_A_FEATURES_NOCHANGE
,
110 data
->nochange
, NULL
, NETDEV_FEATURE_COUNT
,
111 netdev_features_strings
, compact
);
114 const struct ethnl_request_ops ethnl_features_request_ops
= {
115 .request_cmd
= ETHTOOL_MSG_FEATURES_GET
,
116 .reply_cmd
= ETHTOOL_MSG_FEATURES_GET_REPLY
,
117 .hdr_attr
= ETHTOOL_A_FEATURES_HEADER
,
118 .req_info_size
= sizeof(struct features_req_info
),
119 .reply_data_size
= sizeof(struct features_reply_data
),
121 .prepare_data
= features_prepare_data
,
122 .reply_size
= features_reply_size
,
123 .fill_reply
= features_fill_reply
,
128 const struct nla_policy ethnl_features_set_policy
[] = {
129 [ETHTOOL_A_FEATURES_HEADER
] =
130 NLA_POLICY_NESTED(ethnl_header_policy
),
131 [ETHTOOL_A_FEATURES_WANTED
] = { .type
= NLA_NESTED
},
134 static void ethnl_features_to_bitmap(unsigned long *dest
, netdev_features_t val
)
136 const unsigned int words
= BITS_TO_LONGS(NETDEV_FEATURE_COUNT
);
139 bitmap_zero(dest
, NETDEV_FEATURE_COUNT
);
140 for (i
= 0; i
< words
; i
++)
141 dest
[i
] = (unsigned long)(val
>> (i
* BITS_PER_LONG
));
144 static netdev_features_t
ethnl_bitmap_to_features(unsigned long *src
)
146 const unsigned int nft_bits
= sizeof(netdev_features_t
) * BITS_PER_BYTE
;
147 const unsigned int words
= BITS_TO_LONGS(NETDEV_FEATURE_COUNT
);
148 netdev_features_t ret
= 0;
151 for (i
= 0; i
< words
; i
++)
152 ret
|= (netdev_features_t
)(src
[i
]) << (i
* BITS_PER_LONG
);
153 ret
&= ~(netdev_features_t
)0 >> (nft_bits
- NETDEV_FEATURE_COUNT
);
157 static int features_send_reply(struct net_device
*dev
, struct genl_info
*info
,
158 const unsigned long *wanted
,
159 const unsigned long *wanted_mask
,
160 const unsigned long *active
,
161 const unsigned long *active_mask
, bool compact
)
163 struct sk_buff
*rskb
;
168 reply_len
= ethnl_reply_header_size();
169 ret
= ethnl_bitset_size(wanted
, wanted_mask
, NETDEV_FEATURE_COUNT
,
170 netdev_features_strings
, compact
);
174 ret
= ethnl_bitset_size(active
, active_mask
, NETDEV_FEATURE_COUNT
,
175 netdev_features_strings
, compact
);
181 rskb
= ethnl_reply_init(reply_len
, dev
, ETHTOOL_MSG_FEATURES_SET_REPLY
,
182 ETHTOOL_A_FEATURES_HEADER
, info
,
187 ret
= ethnl_put_bitset(rskb
, ETHTOOL_A_FEATURES_WANTED
, wanted
,
188 wanted_mask
, NETDEV_FEATURE_COUNT
,
189 netdev_features_strings
, compact
);
191 goto nla_put_failure
;
192 ret
= ethnl_put_bitset(rskb
, ETHTOOL_A_FEATURES_ACTIVE
, active
,
193 active_mask
, NETDEV_FEATURE_COUNT
,
194 netdev_features_strings
, compact
);
196 goto nla_put_failure
;
198 genlmsg_end(rskb
, reply_payload
);
199 ret
= genlmsg_reply(rskb
, info
);
204 WARN_ONCE(1, "calculated message payload length (%d) not sufficient\n",
207 GENL_SET_ERR_MSG(info
, "failed to send reply message");
211 int ethnl_set_features(struct sk_buff
*skb
, struct genl_info
*info
)
213 DECLARE_BITMAP(wanted_diff_mask
, NETDEV_FEATURE_COUNT
);
214 DECLARE_BITMAP(active_diff_mask
, NETDEV_FEATURE_COUNT
);
215 DECLARE_BITMAP(old_active
, NETDEV_FEATURE_COUNT
);
216 DECLARE_BITMAP(old_wanted
, NETDEV_FEATURE_COUNT
);
217 DECLARE_BITMAP(new_active
, NETDEV_FEATURE_COUNT
);
218 DECLARE_BITMAP(new_wanted
, NETDEV_FEATURE_COUNT
);
219 DECLARE_BITMAP(req_wanted
, NETDEV_FEATURE_COUNT
);
220 DECLARE_BITMAP(req_mask
, NETDEV_FEATURE_COUNT
);
221 struct ethnl_req_info req_info
= {};
222 struct nlattr
**tb
= info
->attrs
;
223 struct net_device
*dev
;
227 if (!tb
[ETHTOOL_A_FEATURES_WANTED
])
229 ret
= ethnl_parse_header_dev_get(&req_info
,
230 tb
[ETHTOOL_A_FEATURES_HEADER
],
231 genl_info_net(info
), info
->extack
,
238 ethnl_features_to_bitmap(old_active
, dev
->features
);
239 ethnl_features_to_bitmap(old_wanted
, dev
->wanted_features
);
240 ret
= ethnl_parse_bitset(req_wanted
, req_mask
, NETDEV_FEATURE_COUNT
,
241 tb
[ETHTOOL_A_FEATURES_WANTED
],
242 netdev_features_strings
, info
->extack
);
245 if (ethnl_bitmap_to_features(req_mask
) & ~NETIF_F_ETHTOOL_BITS
) {
246 GENL_SET_ERR_MSG(info
, "attempt to change non-ethtool features");
251 /* set req_wanted bits not in req_mask from old_wanted */
252 bitmap_and(req_wanted
, req_wanted
, req_mask
, NETDEV_FEATURE_COUNT
);
253 bitmap_andnot(new_wanted
, old_wanted
, req_mask
, NETDEV_FEATURE_COUNT
);
254 bitmap_or(req_wanted
, new_wanted
, req_wanted
, NETDEV_FEATURE_COUNT
);
255 if (!bitmap_equal(req_wanted
, old_wanted
, NETDEV_FEATURE_COUNT
)) {
256 dev
->wanted_features
&= ~dev
->hw_features
;
257 dev
->wanted_features
|= ethnl_bitmap_to_features(req_wanted
) & dev
->hw_features
;
258 __netdev_update_features(dev
);
260 ethnl_features_to_bitmap(new_active
, dev
->features
);
261 mod
= !bitmap_equal(old_active
, new_active
, NETDEV_FEATURE_COUNT
);
264 if (!(req_info
.flags
& ETHTOOL_FLAG_OMIT_REPLY
)) {
265 bool compact
= req_info
.flags
& ETHTOOL_FLAG_COMPACT_BITSETS
;
267 bitmap_xor(wanted_diff_mask
, req_wanted
, new_active
,
268 NETDEV_FEATURE_COUNT
);
269 bitmap_xor(active_diff_mask
, old_active
, new_active
,
270 NETDEV_FEATURE_COUNT
);
271 bitmap_and(wanted_diff_mask
, wanted_diff_mask
, req_mask
,
272 NETDEV_FEATURE_COUNT
);
273 bitmap_and(req_wanted
, req_wanted
, wanted_diff_mask
,
274 NETDEV_FEATURE_COUNT
);
275 bitmap_and(new_active
, new_active
, active_diff_mask
,
276 NETDEV_FEATURE_COUNT
);
278 ret
= features_send_reply(dev
, info
, req_wanted
,
279 wanted_diff_mask
, new_active
,
280 active_diff_mask
, compact
);
283 netdev_features_change(dev
);