1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | #include <linux/ethtool_netlink.h> |
4 | #include <linux/bitmap.h> |
5 | #include "netlink.h" |
6 | #include "bitset.h" |
7 | |
8 | /* Some bitmaps are internally represented as an array of unsigned long, some |
9 | * as an array of u32 (some even as single u32 for now). To avoid the need of |
10 | * wrappers on caller side, we provide two set of functions: those with "32" |
11 | * suffix in their names expect u32 based bitmaps, those without it expect |
12 | * unsigned long bitmaps. |
13 | */ |
14 | |
15 | static u32 ethnl_lower_bits(unsigned int n) |
16 | { |
17 | return ~(u32)0 >> (32 - n % 32); |
18 | } |
19 | |
20 | static u32 ethnl_upper_bits(unsigned int n) |
21 | { |
22 | return ~(u32)0 << (n % 32); |
23 | } |
24 | |
25 | /** |
26 | * ethnl_bitmap32_clear() - Clear u32 based bitmap |
27 | * @dst: bitmap to clear |
28 | * @start: beginning of the interval |
29 | * @end: end of the interval |
30 | * @mod: set if bitmap was modified |
31 | * |
32 | * Clear @nbits bits of a bitmap with indices @start <= i < @end |
33 | */ |
34 | static void ethnl_bitmap32_clear(u32 *dst, unsigned int start, unsigned int end, |
35 | bool *mod) |
36 | { |
37 | unsigned int start_word = start / 32; |
38 | unsigned int end_word = end / 32; |
39 | unsigned int i; |
40 | u32 mask; |
41 | |
42 | if (end <= start) |
43 | return; |
44 | |
45 | if (start % 32) { |
46 | mask = ethnl_upper_bits(n: start); |
47 | if (end_word == start_word) { |
48 | mask &= ethnl_lower_bits(n: end); |
49 | if (dst[start_word] & mask) { |
50 | dst[start_word] &= ~mask; |
51 | *mod = true; |
52 | } |
53 | return; |
54 | } |
55 | if (dst[start_word] & mask) { |
56 | dst[start_word] &= ~mask; |
57 | *mod = true; |
58 | } |
59 | start_word++; |
60 | } |
61 | |
62 | for (i = start_word; i < end_word; i++) { |
63 | if (dst[i]) { |
64 | dst[i] = 0; |
65 | *mod = true; |
66 | } |
67 | } |
68 | if (end % 32) { |
69 | mask = ethnl_lower_bits(n: end); |
70 | if (dst[end_word] & mask) { |
71 | dst[end_word] &= ~mask; |
72 | *mod = true; |
73 | } |
74 | } |
75 | } |
76 | |
77 | /** |
78 | * ethnl_bitmap32_not_zero() - Check if any bit is set in an interval |
79 | * @map: bitmap to test |
80 | * @start: beginning of the interval |
81 | * @end: end of the interval |
82 | * |
83 | * Return: true if there is non-zero bit with index @start <= i < @end, |
84 | * false if the whole interval is zero |
85 | */ |
86 | static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, |
87 | unsigned int end) |
88 | { |
89 | unsigned int start_word = start / 32; |
90 | unsigned int end_word = end / 32; |
91 | u32 mask; |
92 | |
93 | if (end <= start) |
94 | return true; |
95 | |
96 | if (start % 32) { |
97 | mask = ethnl_upper_bits(n: start); |
98 | if (end_word == start_word) { |
99 | mask &= ethnl_lower_bits(n: end); |
100 | return map[start_word] & mask; |
101 | } |
102 | if (map[start_word] & mask) |
103 | return true; |
104 | start_word++; |
105 | } |
106 | |
107 | if (!memchr_inv(p: map + start_word, c: '\0', |
108 | size: (end_word - start_word) * sizeof(u32))) |
109 | return true; |
110 | if (end % 32 == 0) |
111 | return true; |
112 | return map[end_word] & ethnl_lower_bits(n: end); |
113 | } |
114 | |
115 | /** |
116 | * ethnl_bitmap32_update() - Modify u32 based bitmap according to value/mask |
117 | * pair |
118 | * @dst: bitmap to update |
119 | * @nbits: bit size of the bitmap |
120 | * @value: values to set |
121 | * @mask: mask of bits to set |
122 | * @mod: set to true if bitmap is modified, preserve if not |
123 | * |
124 | * Set bits in @dst bitmap which are set in @mask to values from @value, leave |
125 | * the rest untouched. If destination bitmap was modified, set @mod to true, |
126 | * leave as it is if not. |
127 | */ |
128 | static void ethnl_bitmap32_update(u32 *dst, unsigned int nbits, |
129 | const u32 *value, const u32 *mask, bool *mod) |
130 | { |
131 | while (nbits > 0) { |
132 | u32 real_mask = mask ? *mask : ~(u32)0; |
133 | u32 new_value; |
134 | |
135 | if (nbits < 32) |
136 | real_mask &= ethnl_lower_bits(n: nbits); |
137 | new_value = (*dst & ~real_mask) | (*value & real_mask); |
138 | if (new_value != *dst) { |
139 | *dst = new_value; |
140 | *mod = true; |
141 | } |
142 | |
143 | if (nbits <= 32) |
144 | break; |
145 | dst++; |
146 | nbits -= 32; |
147 | value++; |
148 | if (mask) |
149 | mask++; |
150 | } |
151 | } |
152 | |
153 | static bool ethnl_bitmap32_test_bit(const u32 *map, unsigned int index) |
154 | { |
155 | return map[index / 32] & (1U << (index % 32)); |
156 | } |
157 | |
158 | /** |
159 | * ethnl_bitset32_size() - Calculate size of bitset nested attribute |
160 | * @val: value bitmap (u32 based) |
161 | * @mask: mask bitmap (u32 based, optional) |
162 | * @nbits: bit length of the bitset |
163 | * @names: array of bit names (optional) |
164 | * @compact: assume compact format for output |
165 | * |
166 | * Estimate length of netlink attribute composed by a later call to |
167 | * ethnl_put_bitset32() call with the same arguments. |
168 | * |
169 | * Return: negative error code or attribute length estimate |
170 | */ |
171 | int ethnl_bitset32_size(const u32 *val, const u32 *mask, unsigned int nbits, |
172 | ethnl_string_array_t names, bool compact) |
173 | { |
174 | unsigned int len = 0; |
175 | |
176 | /* list flag */ |
177 | if (!mask) |
178 | len += nla_total_size(payload: sizeof(u32)); |
179 | /* size */ |
180 | len += nla_total_size(payload: sizeof(u32)); |
181 | |
182 | if (compact) { |
183 | unsigned int nwords = DIV_ROUND_UP(nbits, 32); |
184 | |
185 | /* value, mask */ |
186 | len += (mask ? 2 : 1) * nla_total_size(payload: nwords * sizeof(u32)); |
187 | } else { |
188 | unsigned int bits_len = 0; |
189 | unsigned int bit_len, i; |
190 | |
191 | for (i = 0; i < nbits; i++) { |
192 | const char *name = names ? names[i] : NULL; |
193 | |
194 | if (!ethnl_bitmap32_test_bit(map: mask ?: val, index: i)) |
195 | continue; |
196 | /* index */ |
197 | bit_len = nla_total_size(payload: sizeof(u32)); |
198 | /* name */ |
199 | if (name) |
200 | bit_len += ethnl_strz_size(s: name); |
201 | /* value */ |
202 | if (mask && ethnl_bitmap32_test_bit(map: val, index: i)) |
203 | bit_len += nla_total_size(payload: 0); |
204 | |
205 | /* bit nest */ |
206 | bits_len += nla_total_size(payload: bit_len); |
207 | } |
208 | /* bits nest */ |
209 | len += nla_total_size(payload: bits_len); |
210 | } |
211 | |
212 | /* outermost nest */ |
213 | return nla_total_size(payload: len); |
214 | } |
215 | |
216 | /** |
217 | * ethnl_put_bitset32() - Put a bitset nest into a message |
218 | * @skb: skb with the message |
219 | * @attrtype: attribute type for the bitset nest |
220 | * @val: value bitmap (u32 based) |
221 | * @mask: mask bitmap (u32 based, optional) |
222 | * @nbits: bit length of the bitset |
223 | * @names: array of bit names (optional) |
224 | * @compact: use compact format for the output |
225 | * |
226 | * Compose a nested attribute representing a bitset. If @mask is null, simple |
227 | * bitmap (bit list) is created, if @mask is provided, represent a value/mask |
228 | * pair. Bit names are only used in verbose mode and when provided by calller. |
229 | * |
230 | * Return: 0 on success, negative error value on error |
231 | */ |
232 | int ethnl_put_bitset32(struct sk_buff *skb, int attrtype, const u32 *val, |
233 | const u32 *mask, unsigned int nbits, |
234 | ethnl_string_array_t names, bool compact) |
235 | { |
236 | struct nlattr *nest; |
237 | struct nlattr *attr; |
238 | |
239 | nest = nla_nest_start(skb, attrtype); |
240 | if (!nest) |
241 | return -EMSGSIZE; |
242 | |
243 | if (!mask && nla_put_flag(skb, attrtype: ETHTOOL_A_BITSET_NOMASK)) |
244 | goto nla_put_failure; |
245 | if (nla_put_u32(skb, attrtype: ETHTOOL_A_BITSET_SIZE, value: nbits)) |
246 | goto nla_put_failure; |
247 | if (compact) { |
248 | unsigned int nwords = DIV_ROUND_UP(nbits, 32); |
249 | unsigned int nbytes = nwords * sizeof(u32); |
250 | u32 *dst; |
251 | |
252 | attr = nla_reserve(skb, attrtype: ETHTOOL_A_BITSET_VALUE, attrlen: nbytes); |
253 | if (!attr) |
254 | goto nla_put_failure; |
255 | dst = nla_data(nla: attr); |
256 | memcpy(dst, val, nbytes); |
257 | if (nbits % 32) |
258 | dst[nwords - 1] &= ethnl_lower_bits(n: nbits); |
259 | |
260 | if (mask) { |
261 | attr = nla_reserve(skb, attrtype: ETHTOOL_A_BITSET_MASK, attrlen: nbytes); |
262 | if (!attr) |
263 | goto nla_put_failure; |
264 | dst = nla_data(nla: attr); |
265 | memcpy(dst, mask, nbytes); |
266 | if (nbits % 32) |
267 | dst[nwords - 1] &= ethnl_lower_bits(n: nbits); |
268 | } |
269 | } else { |
270 | struct nlattr *bits; |
271 | unsigned int i; |
272 | |
273 | bits = nla_nest_start(skb, attrtype: ETHTOOL_A_BITSET_BITS); |
274 | if (!bits) |
275 | goto nla_put_failure; |
276 | for (i = 0; i < nbits; i++) { |
277 | const char *name = names ? names[i] : NULL; |
278 | |
279 | if (!ethnl_bitmap32_test_bit(map: mask ?: val, index: i)) |
280 | continue; |
281 | attr = nla_nest_start(skb, attrtype: ETHTOOL_A_BITSET_BITS_BIT); |
282 | if (!attr) |
283 | goto nla_put_failure; |
284 | if (nla_put_u32(skb, attrtype: ETHTOOL_A_BITSET_BIT_INDEX, value: i)) |
285 | goto nla_put_failure; |
286 | if (name && |
287 | ethnl_put_strz(skb, attrtype: ETHTOOL_A_BITSET_BIT_NAME, s: name)) |
288 | goto nla_put_failure; |
289 | if (mask && ethnl_bitmap32_test_bit(map: val, index: i) && |
290 | nla_put_flag(skb, attrtype: ETHTOOL_A_BITSET_BIT_VALUE)) |
291 | goto nla_put_failure; |
292 | nla_nest_end(skb, start: attr); |
293 | } |
294 | nla_nest_end(skb, start: bits); |
295 | } |
296 | |
297 | nla_nest_end(skb, start: nest); |
298 | return 0; |
299 | |
300 | nla_put_failure: |
301 | nla_nest_cancel(skb, start: nest); |
302 | return -EMSGSIZE; |
303 | } |
304 | |
305 | static const struct nla_policy bitset_policy[] = { |
306 | [ETHTOOL_A_BITSET_NOMASK] = { .type = NLA_FLAG }, |
307 | [ETHTOOL_A_BITSET_SIZE] = NLA_POLICY_MAX(NLA_U32, |
308 | ETHNL_MAX_BITSET_SIZE), |
309 | [ETHTOOL_A_BITSET_BITS] = { .type = NLA_NESTED }, |
310 | [ETHTOOL_A_BITSET_VALUE] = { .type = NLA_BINARY }, |
311 | [ETHTOOL_A_BITSET_MASK] = { .type = NLA_BINARY }, |
312 | }; |
313 | |
314 | static const struct nla_policy bit_policy[] = { |
315 | [ETHTOOL_A_BITSET_BIT_INDEX] = { .type = NLA_U32 }, |
316 | [ETHTOOL_A_BITSET_BIT_NAME] = { .type = NLA_NUL_STRING }, |
317 | [ETHTOOL_A_BITSET_BIT_VALUE] = { .type = NLA_FLAG }, |
318 | }; |
319 | |
320 | /** |
321 | * ethnl_bitset_is_compact() - check if bitset attribute represents a compact |
322 | * bitset |
323 | * @bitset: nested attribute representing a bitset |
324 | * @compact: pointer for return value |
325 | * |
326 | * Return: 0 on success, negative error code on failure |
327 | */ |
328 | int ethnl_bitset_is_compact(const struct nlattr *bitset, bool *compact) |
329 | { |
330 | struct nlattr *tb[ARRAY_SIZE(bitset_policy)]; |
331 | int ret; |
332 | |
333 | ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, nla: bitset, |
334 | policy: bitset_policy, NULL); |
335 | if (ret < 0) |
336 | return ret; |
337 | |
338 | if (tb[ETHTOOL_A_BITSET_BITS]) { |
339 | if (tb[ETHTOOL_A_BITSET_VALUE] || tb[ETHTOOL_A_BITSET_MASK]) |
340 | return -EINVAL; |
341 | *compact = false; |
342 | return 0; |
343 | } |
344 | if (!tb[ETHTOOL_A_BITSET_SIZE] || !tb[ETHTOOL_A_BITSET_VALUE]) |
345 | return -EINVAL; |
346 | |
347 | *compact = true; |
348 | return 0; |
349 | } |
350 | |
351 | /** |
352 | * ethnl_name_to_idx() - look up string index for a name |
353 | * @names: array of ETH_GSTRING_LEN sized strings |
354 | * @n_names: number of strings in the array |
355 | * @name: name to look up |
356 | * |
357 | * Return: index of the string if found, -ENOENT if not found |
358 | */ |
359 | static int ethnl_name_to_idx(ethnl_string_array_t names, unsigned int n_names, |
360 | const char *name) |
361 | { |
362 | unsigned int i; |
363 | |
364 | if (!names) |
365 | return -ENOENT; |
366 | |
367 | for (i = 0; i < n_names; i++) { |
368 | /* names[i] may not be null terminated */ |
369 | if (!strncmp(names[i], name, ETH_GSTRING_LEN) && |
370 | strlen(name) <= ETH_GSTRING_LEN) |
371 | return i; |
372 | } |
373 | |
374 | return -ENOENT; |
375 | } |
376 | |
377 | static int ethnl_parse_bit(unsigned int *index, bool *val, unsigned int nbits, |
378 | const struct nlattr *bit_attr, bool no_mask, |
379 | ethnl_string_array_t names, |
380 | struct netlink_ext_ack *extack) |
381 | { |
382 | struct nlattr *tb[ARRAY_SIZE(bit_policy)]; |
383 | int ret, idx; |
384 | |
385 | ret = nla_parse_nested(tb, ARRAY_SIZE(bit_policy) - 1, nla: bit_attr, |
386 | policy: bit_policy, extack); |
387 | if (ret < 0) |
388 | return ret; |
389 | |
390 | if (tb[ETHTOOL_A_BITSET_BIT_INDEX]) { |
391 | const char *name; |
392 | |
393 | idx = nla_get_u32(nla: tb[ETHTOOL_A_BITSET_BIT_INDEX]); |
394 | if (idx >= nbits) { |
395 | NL_SET_ERR_MSG_ATTR(extack, |
396 | tb[ETHTOOL_A_BITSET_BIT_INDEX], |
397 | "bit index too high" ); |
398 | return -EOPNOTSUPP; |
399 | } |
400 | name = names ? names[idx] : NULL; |
401 | if (tb[ETHTOOL_A_BITSET_BIT_NAME] && name && |
402 | strncmp(nla_data(nla: tb[ETHTOOL_A_BITSET_BIT_NAME]), name, |
403 | nla_len(nla: tb[ETHTOOL_A_BITSET_BIT_NAME]))) { |
404 | NL_SET_ERR_MSG_ATTR(extack, bit_attr, |
405 | "bit index and name mismatch" ); |
406 | return -EINVAL; |
407 | } |
408 | } else if (tb[ETHTOOL_A_BITSET_BIT_NAME]) { |
409 | idx = ethnl_name_to_idx(names, n_names: nbits, |
410 | name: nla_data(nla: tb[ETHTOOL_A_BITSET_BIT_NAME])); |
411 | if (idx < 0) { |
412 | NL_SET_ERR_MSG_ATTR(extack, |
413 | tb[ETHTOOL_A_BITSET_BIT_NAME], |
414 | "bit name not found" ); |
415 | return -EOPNOTSUPP; |
416 | } |
417 | } else { |
418 | NL_SET_ERR_MSG_ATTR(extack, bit_attr, |
419 | "neither bit index nor name specified" ); |
420 | return -EINVAL; |
421 | } |
422 | |
423 | *index = idx; |
424 | *val = no_mask || tb[ETHTOOL_A_BITSET_BIT_VALUE]; |
425 | return 0; |
426 | } |
427 | |
428 | static int |
429 | ethnl_update_bitset32_verbose(u32 *bitmap, unsigned int nbits, |
430 | const struct nlattr *attr, struct nlattr **tb, |
431 | ethnl_string_array_t names, |
432 | struct netlink_ext_ack *extack, bool *mod) |
433 | { |
434 | struct nlattr *bit_attr; |
435 | bool no_mask; |
436 | int rem; |
437 | int ret; |
438 | |
439 | if (tb[ETHTOOL_A_BITSET_VALUE]) { |
440 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE], |
441 | "value only allowed in compact bitset" ); |
442 | return -EINVAL; |
443 | } |
444 | if (tb[ETHTOOL_A_BITSET_MASK]) { |
445 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], |
446 | "mask only allowed in compact bitset" ); |
447 | return -EINVAL; |
448 | } |
449 | |
450 | no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; |
451 | if (no_mask) |
452 | ethnl_bitmap32_clear(dst: bitmap, start: 0, end: nbits, mod); |
453 | |
454 | nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) { |
455 | bool old_val, new_val; |
456 | unsigned int idx; |
457 | |
458 | if (nla_type(nla: bit_attr) != ETHTOOL_A_BITSET_BITS_BIT) { |
459 | NL_SET_ERR_MSG_ATTR(extack, bit_attr, |
460 | "only ETHTOOL_A_BITSET_BITS_BIT allowed in ETHTOOL_A_BITSET_BITS" ); |
461 | return -EINVAL; |
462 | } |
463 | ret = ethnl_parse_bit(index: &idx, val: &new_val, nbits, bit_attr, no_mask, |
464 | names, extack); |
465 | if (ret < 0) |
466 | return ret; |
467 | old_val = bitmap[idx / 32] & ((u32)1 << (idx % 32)); |
468 | if (new_val != old_val) { |
469 | if (new_val) |
470 | bitmap[idx / 32] |= ((u32)1 << (idx % 32)); |
471 | else |
472 | bitmap[idx / 32] &= ~((u32)1 << (idx % 32)); |
473 | *mod = true; |
474 | } |
475 | } |
476 | |
477 | return 0; |
478 | } |
479 | |
480 | static int ethnl_compact_sanity_checks(unsigned int nbits, |
481 | const struct nlattr *nest, |
482 | struct nlattr **tb, |
483 | struct netlink_ext_ack *extack) |
484 | { |
485 | bool no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; |
486 | unsigned int attr_nbits, attr_nwords; |
487 | const struct nlattr *test_attr; |
488 | |
489 | if (no_mask && tb[ETHTOOL_A_BITSET_MASK]) { |
490 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], |
491 | "mask not allowed in list bitset" ); |
492 | return -EINVAL; |
493 | } |
494 | if (!tb[ETHTOOL_A_BITSET_SIZE]) { |
495 | NL_SET_ERR_MSG_ATTR(extack, nest, |
496 | "missing size in compact bitset" ); |
497 | return -EINVAL; |
498 | } |
499 | if (!tb[ETHTOOL_A_BITSET_VALUE]) { |
500 | NL_SET_ERR_MSG_ATTR(extack, nest, |
501 | "missing value in compact bitset" ); |
502 | return -EINVAL; |
503 | } |
504 | if (!no_mask && !tb[ETHTOOL_A_BITSET_MASK]) { |
505 | NL_SET_ERR_MSG_ATTR(extack, nest, |
506 | "missing mask in compact nonlist bitset" ); |
507 | return -EINVAL; |
508 | } |
509 | |
510 | attr_nbits = nla_get_u32(nla: tb[ETHTOOL_A_BITSET_SIZE]); |
511 | attr_nwords = DIV_ROUND_UP(attr_nbits, 32); |
512 | if (nla_len(nla: tb[ETHTOOL_A_BITSET_VALUE]) != attr_nwords * sizeof(u32)) { |
513 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE], |
514 | "bitset value length does not match size" ); |
515 | return -EINVAL; |
516 | } |
517 | if (tb[ETHTOOL_A_BITSET_MASK] && |
518 | nla_len(nla: tb[ETHTOOL_A_BITSET_MASK]) != attr_nwords * sizeof(u32)) { |
519 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], |
520 | "bitset mask length does not match size" ); |
521 | return -EINVAL; |
522 | } |
523 | if (attr_nbits <= nbits) |
524 | return 0; |
525 | |
526 | test_attr = no_mask ? tb[ETHTOOL_A_BITSET_VALUE] : |
527 | tb[ETHTOOL_A_BITSET_MASK]; |
528 | if (ethnl_bitmap32_not_zero(map: nla_data(nla: test_attr), start: nbits, end: attr_nbits)) { |
529 | NL_SET_ERR_MSG_ATTR(extack, test_attr, |
530 | "cannot modify bits past kernel bitset size" ); |
531 | return -EINVAL; |
532 | } |
533 | return 0; |
534 | } |
535 | |
536 | /** |
537 | * ethnl_update_bitset32() - Apply a bitset nest to a u32 based bitmap |
538 | * @bitmap: bitmap to update |
539 | * @nbits: size of the updated bitmap in bits |
540 | * @attr: nest attribute to parse and apply |
541 | * @names: array of bit names; may be null for compact format |
542 | * @extack: extack for error reporting |
543 | * @mod: set this to true if bitmap is modified, leave as it is if not |
544 | * |
545 | * Apply bitset netsted attribute to a bitmap. If the attribute represents |
546 | * a bit list, @bitmap is set to its contents; otherwise, bits in mask are |
547 | * set to values from value. Bitmaps in the attribute may be longer than |
548 | * @nbits but the message must not request modifying any bits past @nbits. |
549 | * |
550 | * Return: negative error code on failure, 0 on success |
551 | */ |
552 | int ethnl_update_bitset32(u32 *bitmap, unsigned int nbits, |
553 | const struct nlattr *attr, ethnl_string_array_t names, |
554 | struct netlink_ext_ack *extack, bool *mod) |
555 | { |
556 | struct nlattr *tb[ARRAY_SIZE(bitset_policy)]; |
557 | unsigned int change_bits; |
558 | bool no_mask; |
559 | int ret; |
560 | |
561 | if (!attr) |
562 | return 0; |
563 | ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, nla: attr, |
564 | policy: bitset_policy, extack); |
565 | if (ret < 0) |
566 | return ret; |
567 | |
568 | if (tb[ETHTOOL_A_BITSET_BITS]) |
569 | return ethnl_update_bitset32_verbose(bitmap, nbits, attr, tb, |
570 | names, extack, mod); |
571 | ret = ethnl_compact_sanity_checks(nbits, nest: attr, tb, extack); |
572 | if (ret < 0) |
573 | return ret; |
574 | |
575 | no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; |
576 | change_bits = min_t(unsigned int, |
577 | nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]), nbits); |
578 | ethnl_bitmap32_update(dst: bitmap, nbits: change_bits, |
579 | value: nla_data(nla: tb[ETHTOOL_A_BITSET_VALUE]), |
580 | mask: no_mask ? NULL : |
581 | nla_data(nla: tb[ETHTOOL_A_BITSET_MASK]), |
582 | mod); |
583 | if (no_mask && change_bits < nbits) |
584 | ethnl_bitmap32_clear(dst: bitmap, start: change_bits, end: nbits, mod); |
585 | |
586 | return 0; |
587 | } |
588 | |
589 | /** |
590 | * ethnl_parse_bitset() - Compute effective value and mask from bitset nest |
591 | * @val: unsigned long based bitmap to put value into |
592 | * @mask: unsigned long based bitmap to put mask into |
593 | * @nbits: size of @val and @mask bitmaps |
594 | * @attr: nest attribute to parse and apply |
595 | * @names: array of bit names; may be null for compact format |
596 | * @extack: extack for error reporting |
597 | * |
598 | * Provide @nbits size long bitmaps for value and mask so that |
599 | * x = (val & mask) | (x & ~mask) would modify any @nbits sized bitmap x |
600 | * the same way ethnl_update_bitset() with the same bitset attribute would. |
601 | * |
602 | * Return: negative error code on failure, 0 on success |
603 | */ |
604 | int ethnl_parse_bitset(unsigned long *val, unsigned long *mask, |
605 | unsigned int nbits, const struct nlattr *attr, |
606 | ethnl_string_array_t names, |
607 | struct netlink_ext_ack *extack) |
608 | { |
609 | struct nlattr *tb[ARRAY_SIZE(bitset_policy)]; |
610 | const struct nlattr *bit_attr; |
611 | bool no_mask; |
612 | int rem; |
613 | int ret; |
614 | |
615 | if (!attr) |
616 | return 0; |
617 | ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, nla: attr, |
618 | policy: bitset_policy, extack); |
619 | if (ret < 0) |
620 | return ret; |
621 | no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; |
622 | |
623 | if (!tb[ETHTOOL_A_BITSET_BITS]) { |
624 | unsigned int change_bits; |
625 | |
626 | ret = ethnl_compact_sanity_checks(nbits, nest: attr, tb, extack); |
627 | if (ret < 0) |
628 | return ret; |
629 | |
630 | change_bits = nla_get_u32(nla: tb[ETHTOOL_A_BITSET_SIZE]); |
631 | if (change_bits > nbits) |
632 | change_bits = nbits; |
633 | bitmap_from_arr32(bitmap: val, buf: nla_data(nla: tb[ETHTOOL_A_BITSET_VALUE]), |
634 | nbits: change_bits); |
635 | if (change_bits < nbits) |
636 | bitmap_clear(map: val, start: change_bits, nbits: nbits - change_bits); |
637 | if (no_mask) { |
638 | bitmap_fill(dst: mask, nbits); |
639 | } else { |
640 | bitmap_from_arr32(bitmap: mask, |
641 | buf: nla_data(nla: tb[ETHTOOL_A_BITSET_MASK]), |
642 | nbits: change_bits); |
643 | if (change_bits < nbits) |
644 | bitmap_clear(map: mask, start: change_bits, |
645 | nbits: nbits - change_bits); |
646 | } |
647 | |
648 | return 0; |
649 | } |
650 | |
651 | if (tb[ETHTOOL_A_BITSET_VALUE]) { |
652 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE], |
653 | "value only allowed in compact bitset" ); |
654 | return -EINVAL; |
655 | } |
656 | if (tb[ETHTOOL_A_BITSET_MASK]) { |
657 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], |
658 | "mask only allowed in compact bitset" ); |
659 | return -EINVAL; |
660 | } |
661 | |
662 | bitmap_zero(dst: val, nbits); |
663 | if (no_mask) |
664 | bitmap_fill(dst: mask, nbits); |
665 | else |
666 | bitmap_zero(dst: mask, nbits); |
667 | |
668 | nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) { |
669 | unsigned int idx; |
670 | bool bit_val; |
671 | |
672 | ret = ethnl_parse_bit(index: &idx, val: &bit_val, nbits, bit_attr, no_mask, |
673 | names, extack); |
674 | if (ret < 0) |
675 | return ret; |
676 | if (bit_val) |
677 | __set_bit(idx, val); |
678 | if (!no_mask) |
679 | __set_bit(idx, mask); |
680 | } |
681 | |
682 | return 0; |
683 | } |
684 | |
685 | #if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN) |
686 | |
687 | /* 64-bit big endian architectures are the only case when u32 based bitmaps |
688 | * and unsigned long based bitmaps have different memory layout so that we |
689 | * cannot simply cast the latter to the former and need actual wrappers |
690 | * converting the latter to the former. |
691 | * |
692 | * To reduce the number of slab allocations, the wrappers use fixed size local |
693 | * variables for bitmaps up to ETHNL_SMALL_BITMAP_BITS bits which is the |
694 | * majority of bitmaps used by ethtool. |
695 | */ |
696 | #define ETHNL_SMALL_BITMAP_BITS 128 |
697 | #define ETHNL_SMALL_BITMAP_WORDS DIV_ROUND_UP(ETHNL_SMALL_BITMAP_BITS, 32) |
698 | |
699 | int ethnl_bitset_size(const unsigned long *val, const unsigned long *mask, |
700 | unsigned int nbits, ethnl_string_array_t names, |
701 | bool compact) |
702 | { |
703 | u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS]; |
704 | u32 small_val32[ETHNL_SMALL_BITMAP_WORDS]; |
705 | u32 *mask32; |
706 | u32 *val32; |
707 | int ret; |
708 | |
709 | if (nbits > ETHNL_SMALL_BITMAP_BITS) { |
710 | unsigned int nwords = DIV_ROUND_UP(nbits, 32); |
711 | |
712 | val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL); |
713 | if (!val32) |
714 | return -ENOMEM; |
715 | mask32 = val32 + nwords; |
716 | } else { |
717 | val32 = small_val32; |
718 | mask32 = small_mask32; |
719 | } |
720 | |
721 | bitmap_to_arr32(val32, val, nbits); |
722 | if (mask) |
723 | bitmap_to_arr32(mask32, mask, nbits); |
724 | else |
725 | mask32 = NULL; |
726 | ret = ethnl_bitset32_size(val32, mask32, nbits, names, compact); |
727 | |
728 | if (nbits > ETHNL_SMALL_BITMAP_BITS) |
729 | kfree(val32); |
730 | |
731 | return ret; |
732 | } |
733 | |
734 | int ethnl_put_bitset(struct sk_buff *skb, int attrtype, |
735 | const unsigned long *val, const unsigned long *mask, |
736 | unsigned int nbits, ethnl_string_array_t names, |
737 | bool compact) |
738 | { |
739 | u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS]; |
740 | u32 small_val32[ETHNL_SMALL_BITMAP_WORDS]; |
741 | u32 *mask32; |
742 | u32 *val32; |
743 | int ret; |
744 | |
745 | if (nbits > ETHNL_SMALL_BITMAP_BITS) { |
746 | unsigned int nwords = DIV_ROUND_UP(nbits, 32); |
747 | |
748 | val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL); |
749 | if (!val32) |
750 | return -ENOMEM; |
751 | mask32 = val32 + nwords; |
752 | } else { |
753 | val32 = small_val32; |
754 | mask32 = small_mask32; |
755 | } |
756 | |
757 | bitmap_to_arr32(val32, val, nbits); |
758 | if (mask) |
759 | bitmap_to_arr32(mask32, mask, nbits); |
760 | else |
761 | mask32 = NULL; |
762 | ret = ethnl_put_bitset32(skb, attrtype, val32, mask32, nbits, names, |
763 | compact); |
764 | |
765 | if (nbits > ETHNL_SMALL_BITMAP_BITS) |
766 | kfree(val32); |
767 | |
768 | return ret; |
769 | } |
770 | |
771 | int ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits, |
772 | const struct nlattr *attr, ethnl_string_array_t names, |
773 | struct netlink_ext_ack *extack, bool *mod) |
774 | { |
775 | u32 small_bitmap32[ETHNL_SMALL_BITMAP_WORDS]; |
776 | u32 *bitmap32 = small_bitmap32; |
777 | bool u32_mod = false; |
778 | int ret; |
779 | |
780 | if (nbits > ETHNL_SMALL_BITMAP_BITS) { |
781 | unsigned int dst_words = DIV_ROUND_UP(nbits, 32); |
782 | |
783 | bitmap32 = kmalloc_array(dst_words, sizeof(u32), GFP_KERNEL); |
784 | if (!bitmap32) |
785 | return -ENOMEM; |
786 | } |
787 | |
788 | bitmap_to_arr32(bitmap32, bitmap, nbits); |
789 | ret = ethnl_update_bitset32(bitmap32, nbits, attr, names, extack, |
790 | &u32_mod); |
791 | if (u32_mod) { |
792 | bitmap_from_arr32(bitmap, bitmap32, nbits); |
793 | *mod = true; |
794 | } |
795 | |
796 | if (nbits > ETHNL_SMALL_BITMAP_BITS) |
797 | kfree(bitmap32); |
798 | |
799 | return ret; |
800 | } |
801 | |
802 | #else |
803 | |
804 | /* On little endian 64-bit and all 32-bit architectures, an unsigned long |
805 | * based bitmap can be interpreted as u32 based one using a simple cast. |
806 | */ |
807 | |
808 | int ethnl_bitset_size(const unsigned long *val, const unsigned long *mask, |
809 | unsigned int nbits, ethnl_string_array_t names, |
810 | bool compact) |
811 | { |
812 | return ethnl_bitset32_size(val: (const u32 *)val, mask: (const u32 *)mask, nbits, |
813 | names, compact); |
814 | } |
815 | |
816 | int ethnl_put_bitset(struct sk_buff *skb, int attrtype, |
817 | const unsigned long *val, const unsigned long *mask, |
818 | unsigned int nbits, ethnl_string_array_t names, |
819 | bool compact) |
820 | { |
821 | return ethnl_put_bitset32(skb, attrtype, val: (const u32 *)val, |
822 | mask: (const u32 *)mask, nbits, names, compact); |
823 | } |
824 | |
825 | int ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits, |
826 | const struct nlattr *attr, ethnl_string_array_t names, |
827 | struct netlink_ext_ack *extack, bool *mod) |
828 | { |
829 | return ethnl_update_bitset32(bitmap: (u32 *)bitmap, nbits, attr, names, extack, |
830 | mod); |
831 | } |
832 | |
833 | #endif /* BITS_PER_LONG == 64 && defined(__BIG_ENDIAN) */ |
834 | |