| 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(s: map + start_word, c: '\0', | 
|---|
| 108 | n: (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(to: dst, from: val, len: 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(to: dst, from: mask, len: 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 | /** | 
|---|
| 429 | * ethnl_bitmap32_equal() - Compare two bitmaps | 
|---|
| 430 | * @map1:  first bitmap | 
|---|
| 431 | * @map2:  second bitmap | 
|---|
| 432 | * @nbits: bit size to compare | 
|---|
| 433 | * | 
|---|
| 434 | * Return: true if first @nbits are equal, false if not | 
|---|
| 435 | */ | 
|---|
| 436 | static bool ethnl_bitmap32_equal(const u32 *map1, const u32 *map2, | 
|---|
| 437 | unsigned int nbits) | 
|---|
| 438 | { | 
|---|
| 439 | if (memcmp(map1, map2, nbits / 32 * sizeof(u32))) | 
|---|
| 440 | return false; | 
|---|
| 441 | if (nbits % 32 == 0) | 
|---|
| 442 | return true; | 
|---|
| 443 | return !((map1[nbits / 32] ^ map2[nbits / 32]) & | 
|---|
| 444 | ethnl_lower_bits(n: nbits % 32)); | 
|---|
| 445 | } | 
|---|
| 446 |  | 
|---|
| 447 | static int | 
|---|
| 448 | ethnl_update_bitset32_verbose(u32 *bitmap, unsigned int nbits, | 
|---|
| 449 | const struct nlattr *attr, struct nlattr **tb, | 
|---|
| 450 | ethnl_string_array_t names, | 
|---|
| 451 | struct netlink_ext_ack *extack, bool *mod) | 
|---|
| 452 | { | 
|---|
| 453 | u32 *saved_bitmap = NULL; | 
|---|
| 454 | struct nlattr *bit_attr; | 
|---|
| 455 | bool no_mask; | 
|---|
| 456 | int rem; | 
|---|
| 457 | int ret; | 
|---|
| 458 |  | 
|---|
| 459 | if (tb[ETHTOOL_A_BITSET_VALUE]) { | 
|---|
| 460 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE], | 
|---|
| 461 | "value only allowed in compact bitset"); | 
|---|
| 462 | return -EINVAL; | 
|---|
| 463 | } | 
|---|
| 464 | if (tb[ETHTOOL_A_BITSET_MASK]) { | 
|---|
| 465 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], | 
|---|
| 466 | "mask only allowed in compact bitset"); | 
|---|
| 467 | return -EINVAL; | 
|---|
| 468 | } | 
|---|
| 469 |  | 
|---|
| 470 | no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; | 
|---|
| 471 | if (no_mask) { | 
|---|
| 472 | unsigned int nwords = DIV_ROUND_UP(nbits, 32); | 
|---|
| 473 | unsigned int nbytes = nwords * sizeof(u32); | 
|---|
| 474 | bool dummy; | 
|---|
| 475 |  | 
|---|
| 476 | /* The bitmap size is only the size of the map part without | 
|---|
| 477 | * its mask part. | 
|---|
| 478 | */ | 
|---|
| 479 | saved_bitmap = kcalloc(nwords, sizeof(u32), GFP_KERNEL); | 
|---|
| 480 | if (!saved_bitmap) | 
|---|
| 481 | return -ENOMEM; | 
|---|
| 482 | memcpy(to: saved_bitmap, from: bitmap, len: nbytes); | 
|---|
| 483 | ethnl_bitmap32_clear(dst: bitmap, start: 0, end: nbits, mod: &dummy); | 
|---|
| 484 | } | 
|---|
| 485 |  | 
|---|
| 486 | nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) { | 
|---|
| 487 | bool old_val, new_val; | 
|---|
| 488 | unsigned int idx; | 
|---|
| 489 |  | 
|---|
| 490 | if (nla_type(nla: bit_attr) != ETHTOOL_A_BITSET_BITS_BIT) { | 
|---|
| 491 | NL_SET_ERR_MSG_ATTR(extack, bit_attr, | 
|---|
| 492 | "only ETHTOOL_A_BITSET_BITS_BIT allowed in ETHTOOL_A_BITSET_BITS"); | 
|---|
| 493 | kfree(objp: saved_bitmap); | 
|---|
| 494 | return -EINVAL; | 
|---|
| 495 | } | 
|---|
| 496 | ret = ethnl_parse_bit(index: &idx, val: &new_val, nbits, bit_attr, no_mask, | 
|---|
| 497 | names, extack); | 
|---|
| 498 | if (ret < 0) { | 
|---|
| 499 | kfree(objp: saved_bitmap); | 
|---|
| 500 | return ret; | 
|---|
| 501 | } | 
|---|
| 502 | old_val = bitmap[idx / 32] & ((u32)1 << (idx % 32)); | 
|---|
| 503 | if (new_val != old_val) { | 
|---|
| 504 | if (new_val) | 
|---|
| 505 | bitmap[idx / 32] |= ((u32)1 << (idx % 32)); | 
|---|
| 506 | else | 
|---|
| 507 | bitmap[idx / 32] &= ~((u32)1 << (idx % 32)); | 
|---|
| 508 | if (!no_mask) | 
|---|
| 509 | *mod = true; | 
|---|
| 510 | } | 
|---|
| 511 | } | 
|---|
| 512 |  | 
|---|
| 513 | if (no_mask && !ethnl_bitmap32_equal(map1: saved_bitmap, map2: bitmap, nbits)) | 
|---|
| 514 | *mod = true; | 
|---|
| 515 |  | 
|---|
| 516 | kfree(objp: saved_bitmap); | 
|---|
| 517 | return 0; | 
|---|
| 518 | } | 
|---|
| 519 |  | 
|---|
| 520 | static int ethnl_compact_sanity_checks(unsigned int nbits, | 
|---|
| 521 | const struct nlattr *nest, | 
|---|
| 522 | struct nlattr **tb, | 
|---|
| 523 | struct netlink_ext_ack *extack) | 
|---|
| 524 | { | 
|---|
| 525 | bool no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; | 
|---|
| 526 | unsigned int attr_nbits, attr_nwords; | 
|---|
| 527 | const struct nlattr *test_attr; | 
|---|
| 528 |  | 
|---|
| 529 | if (no_mask && tb[ETHTOOL_A_BITSET_MASK]) { | 
|---|
| 530 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], | 
|---|
| 531 | "mask not allowed in list bitset"); | 
|---|
| 532 | return -EINVAL; | 
|---|
| 533 | } | 
|---|
| 534 | if (!tb[ETHTOOL_A_BITSET_SIZE]) { | 
|---|
| 535 | NL_SET_ERR_MSG_ATTR(extack, nest, | 
|---|
| 536 | "missing size in compact bitset"); | 
|---|
| 537 | return -EINVAL; | 
|---|
| 538 | } | 
|---|
| 539 | if (!tb[ETHTOOL_A_BITSET_VALUE]) { | 
|---|
| 540 | NL_SET_ERR_MSG_ATTR(extack, nest, | 
|---|
| 541 | "missing value in compact bitset"); | 
|---|
| 542 | return -EINVAL; | 
|---|
| 543 | } | 
|---|
| 544 | if (!no_mask && !tb[ETHTOOL_A_BITSET_MASK]) { | 
|---|
| 545 | NL_SET_ERR_MSG_ATTR(extack, nest, | 
|---|
| 546 | "missing mask in compact nonlist bitset"); | 
|---|
| 547 | return -EINVAL; | 
|---|
| 548 | } | 
|---|
| 549 |  | 
|---|
| 550 | attr_nbits = nla_get_u32(nla: tb[ETHTOOL_A_BITSET_SIZE]); | 
|---|
| 551 | attr_nwords = DIV_ROUND_UP(attr_nbits, 32); | 
|---|
| 552 | if (nla_len(nla: tb[ETHTOOL_A_BITSET_VALUE]) != attr_nwords * sizeof(u32)) { | 
|---|
| 553 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE], | 
|---|
| 554 | "bitset value length does not match size"); | 
|---|
| 555 | return -EINVAL; | 
|---|
| 556 | } | 
|---|
| 557 | if (tb[ETHTOOL_A_BITSET_MASK] && | 
|---|
| 558 | nla_len(nla: tb[ETHTOOL_A_BITSET_MASK]) != attr_nwords * sizeof(u32)) { | 
|---|
| 559 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], | 
|---|
| 560 | "bitset mask length does not match size"); | 
|---|
| 561 | return -EINVAL; | 
|---|
| 562 | } | 
|---|
| 563 | if (attr_nbits <= nbits) | 
|---|
| 564 | return 0; | 
|---|
| 565 |  | 
|---|
| 566 | test_attr = no_mask ? tb[ETHTOOL_A_BITSET_VALUE] : | 
|---|
| 567 | tb[ETHTOOL_A_BITSET_MASK]; | 
|---|
| 568 | if (ethnl_bitmap32_not_zero(map: nla_data(nla: test_attr), start: nbits, end: attr_nbits)) { | 
|---|
| 569 | NL_SET_ERR_MSG_ATTR(extack, test_attr, | 
|---|
| 570 | "cannot modify bits past kernel bitset size"); | 
|---|
| 571 | return -EINVAL; | 
|---|
| 572 | } | 
|---|
| 573 | return 0; | 
|---|
| 574 | } | 
|---|
| 575 |  | 
|---|
| 576 | /** | 
|---|
| 577 | * ethnl_update_bitset32() - Apply a bitset nest to a u32 based bitmap | 
|---|
| 578 | * @bitmap:  bitmap to update | 
|---|
| 579 | * @nbits:   size of the updated bitmap in bits | 
|---|
| 580 | * @attr:    nest attribute to parse and apply | 
|---|
| 581 | * @names:   array of bit names; may be null for compact format | 
|---|
| 582 | * @extack:  extack for error reporting | 
|---|
| 583 | * @mod:     set this to true if bitmap is modified, leave as it is if not | 
|---|
| 584 | * | 
|---|
| 585 | * Apply bitset netsted attribute to a bitmap. If the attribute represents | 
|---|
| 586 | * a bit list, @bitmap is set to its contents; otherwise, bits in mask are | 
|---|
| 587 | * set to values from value. Bitmaps in the attribute may be longer than | 
|---|
| 588 | * @nbits but the message must not request modifying any bits past @nbits. | 
|---|
| 589 | * | 
|---|
| 590 | * Return: negative error code on failure, 0 on success | 
|---|
| 591 | */ | 
|---|
| 592 | int ethnl_update_bitset32(u32 *bitmap, unsigned int nbits, | 
|---|
| 593 | const struct nlattr *attr, ethnl_string_array_t names, | 
|---|
| 594 | struct netlink_ext_ack *extack, bool *mod) | 
|---|
| 595 | { | 
|---|
| 596 | struct nlattr *tb[ARRAY_SIZE(bitset_policy)]; | 
|---|
| 597 | unsigned int change_bits; | 
|---|
| 598 | bool no_mask; | 
|---|
| 599 | int ret; | 
|---|
| 600 |  | 
|---|
| 601 | if (!attr) | 
|---|
| 602 | return 0; | 
|---|
| 603 | ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, nla: attr, | 
|---|
| 604 | policy: bitset_policy, extack); | 
|---|
| 605 | if (ret < 0) | 
|---|
| 606 | return ret; | 
|---|
| 607 |  | 
|---|
| 608 | if (tb[ETHTOOL_A_BITSET_BITS]) | 
|---|
| 609 | return ethnl_update_bitset32_verbose(bitmap, nbits, attr, tb, | 
|---|
| 610 | names, extack, mod); | 
|---|
| 611 | ret = ethnl_compact_sanity_checks(nbits, nest: attr, tb, extack); | 
|---|
| 612 | if (ret < 0) | 
|---|
| 613 | return ret; | 
|---|
| 614 |  | 
|---|
| 615 | no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; | 
|---|
| 616 | change_bits = min_t(unsigned int, | 
|---|
| 617 | nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]), nbits); | 
|---|
| 618 | ethnl_bitmap32_update(dst: bitmap, nbits: change_bits, | 
|---|
| 619 | value: nla_data(nla: tb[ETHTOOL_A_BITSET_VALUE]), | 
|---|
| 620 | mask: no_mask ? NULL : | 
|---|
| 621 | nla_data(nla: tb[ETHTOOL_A_BITSET_MASK]), | 
|---|
| 622 | mod); | 
|---|
| 623 | if (no_mask && change_bits < nbits) | 
|---|
| 624 | ethnl_bitmap32_clear(dst: bitmap, start: change_bits, end: nbits, mod); | 
|---|
| 625 |  | 
|---|
| 626 | return 0; | 
|---|
| 627 | } | 
|---|
| 628 |  | 
|---|
| 629 | /** | 
|---|
| 630 | * ethnl_parse_bitset() - Compute effective value and mask from bitset nest | 
|---|
| 631 | * @val:     unsigned long based bitmap to put value into | 
|---|
| 632 | * @mask:    unsigned long based bitmap to put mask into | 
|---|
| 633 | * @nbits:   size of @val and @mask bitmaps | 
|---|
| 634 | * @attr:    nest attribute to parse and apply | 
|---|
| 635 | * @names:   array of bit names; may be null for compact format | 
|---|
| 636 | * @extack:  extack for error reporting | 
|---|
| 637 | * | 
|---|
| 638 | * Provide @nbits size long bitmaps for value and mask so that | 
|---|
| 639 | * x = (val & mask) | (x & ~mask) would modify any @nbits sized bitmap x | 
|---|
| 640 | * the same way ethnl_update_bitset() with the same bitset attribute would. | 
|---|
| 641 | * | 
|---|
| 642 | * Return:   negative error code on failure, 0 on success | 
|---|
| 643 | */ | 
|---|
| 644 | int ethnl_parse_bitset(unsigned long *val, unsigned long *mask, | 
|---|
| 645 | unsigned int nbits, const struct nlattr *attr, | 
|---|
| 646 | ethnl_string_array_t names, | 
|---|
| 647 | struct netlink_ext_ack *extack) | 
|---|
| 648 | { | 
|---|
| 649 | struct nlattr *tb[ARRAY_SIZE(bitset_policy)]; | 
|---|
| 650 | const struct nlattr *bit_attr; | 
|---|
| 651 | bool no_mask; | 
|---|
| 652 | int rem; | 
|---|
| 653 | int ret; | 
|---|
| 654 |  | 
|---|
| 655 | if (!attr) | 
|---|
| 656 | return 0; | 
|---|
| 657 | ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, nla: attr, | 
|---|
| 658 | policy: bitset_policy, extack); | 
|---|
| 659 | if (ret < 0) | 
|---|
| 660 | return ret; | 
|---|
| 661 | no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; | 
|---|
| 662 |  | 
|---|
| 663 | if (!tb[ETHTOOL_A_BITSET_BITS]) { | 
|---|
| 664 | unsigned int change_bits; | 
|---|
| 665 |  | 
|---|
| 666 | ret = ethnl_compact_sanity_checks(nbits, nest: attr, tb, extack); | 
|---|
| 667 | if (ret < 0) | 
|---|
| 668 | return ret; | 
|---|
| 669 |  | 
|---|
| 670 | change_bits = nla_get_u32(nla: tb[ETHTOOL_A_BITSET_SIZE]); | 
|---|
| 671 | if (change_bits > nbits) | 
|---|
| 672 | change_bits = nbits; | 
|---|
| 673 | bitmap_from_arr32(bitmap: val, buf: nla_data(nla: tb[ETHTOOL_A_BITSET_VALUE]), | 
|---|
| 674 | nbits: change_bits); | 
|---|
| 675 | if (change_bits < nbits) | 
|---|
| 676 | bitmap_clear(map: val, start: change_bits, nbits: nbits - change_bits); | 
|---|
| 677 | if (no_mask) { | 
|---|
| 678 | bitmap_fill(dst: mask, nbits); | 
|---|
| 679 | } else { | 
|---|
| 680 | bitmap_from_arr32(bitmap: mask, | 
|---|
| 681 | buf: nla_data(nla: tb[ETHTOOL_A_BITSET_MASK]), | 
|---|
| 682 | nbits: change_bits); | 
|---|
| 683 | if (change_bits < nbits) | 
|---|
| 684 | bitmap_clear(map: mask, start: change_bits, | 
|---|
| 685 | nbits: nbits - change_bits); | 
|---|
| 686 | } | 
|---|
| 687 |  | 
|---|
| 688 | return 0; | 
|---|
| 689 | } | 
|---|
| 690 |  | 
|---|
| 691 | if (tb[ETHTOOL_A_BITSET_VALUE]) { | 
|---|
| 692 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE], | 
|---|
| 693 | "value only allowed in compact bitset"); | 
|---|
| 694 | return -EINVAL; | 
|---|
| 695 | } | 
|---|
| 696 | if (tb[ETHTOOL_A_BITSET_MASK]) { | 
|---|
| 697 | NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], | 
|---|
| 698 | "mask only allowed in compact bitset"); | 
|---|
| 699 | return -EINVAL; | 
|---|
| 700 | } | 
|---|
| 701 |  | 
|---|
| 702 | bitmap_zero(dst: val, nbits); | 
|---|
| 703 | if (no_mask) | 
|---|
| 704 | bitmap_fill(dst: mask, nbits); | 
|---|
| 705 | else | 
|---|
| 706 | bitmap_zero(dst: mask, nbits); | 
|---|
| 707 |  | 
|---|
| 708 | nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) { | 
|---|
| 709 | unsigned int idx; | 
|---|
| 710 | bool bit_val; | 
|---|
| 711 |  | 
|---|
| 712 | ret = ethnl_parse_bit(index: &idx, val: &bit_val, nbits, bit_attr, no_mask, | 
|---|
| 713 | names, extack); | 
|---|
| 714 | if (ret < 0) | 
|---|
| 715 | return ret; | 
|---|
| 716 | if (bit_val) | 
|---|
| 717 | __set_bit(idx, val); | 
|---|
| 718 | if (!no_mask) | 
|---|
| 719 | __set_bit(idx, mask); | 
|---|
| 720 | } | 
|---|
| 721 |  | 
|---|
| 722 | return 0; | 
|---|
| 723 | } | 
|---|
| 724 |  | 
|---|
| 725 | #if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN) | 
|---|
| 726 |  | 
|---|
| 727 | /* 64-bit big endian architectures are the only case when u32 based bitmaps | 
|---|
| 728 | * and unsigned long based bitmaps have different memory layout so that we | 
|---|
| 729 | * cannot simply cast the latter to the former and need actual wrappers | 
|---|
| 730 | * converting the latter to the former. | 
|---|
| 731 | * | 
|---|
| 732 | * To reduce the number of slab allocations, the wrappers use fixed size local | 
|---|
| 733 | * variables for bitmaps up to ETHNL_SMALL_BITMAP_BITS bits which is the | 
|---|
| 734 | * majority of bitmaps used by ethtool. | 
|---|
| 735 | */ | 
|---|
| 736 | #define ETHNL_SMALL_BITMAP_BITS 128 | 
|---|
| 737 | #define ETHNL_SMALL_BITMAP_WORDS DIV_ROUND_UP(ETHNL_SMALL_BITMAP_BITS, 32) | 
|---|
| 738 |  | 
|---|
| 739 | int ethnl_bitset_size(const unsigned long *val, const unsigned long *mask, | 
|---|
| 740 | unsigned int nbits, ethnl_string_array_t names, | 
|---|
| 741 | bool compact) | 
|---|
| 742 | { | 
|---|
| 743 | u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS]; | 
|---|
| 744 | u32 small_val32[ETHNL_SMALL_BITMAP_WORDS]; | 
|---|
| 745 | u32 *mask32; | 
|---|
| 746 | u32 *val32; | 
|---|
| 747 | int ret; | 
|---|
| 748 |  | 
|---|
| 749 | if (nbits > ETHNL_SMALL_BITMAP_BITS) { | 
|---|
| 750 | unsigned int nwords = DIV_ROUND_UP(nbits, 32); | 
|---|
| 751 |  | 
|---|
| 752 | val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL); | 
|---|
| 753 | if (!val32) | 
|---|
| 754 | return -ENOMEM; | 
|---|
| 755 | mask32 = val32 + nwords; | 
|---|
| 756 | } else { | 
|---|
| 757 | val32 = small_val32; | 
|---|
| 758 | mask32 = small_mask32; | 
|---|
| 759 | } | 
|---|
| 760 |  | 
|---|
| 761 | bitmap_to_arr32(val32, val, nbits); | 
|---|
| 762 | if (mask) | 
|---|
| 763 | bitmap_to_arr32(mask32, mask, nbits); | 
|---|
| 764 | else | 
|---|
| 765 | mask32 = NULL; | 
|---|
| 766 | ret = ethnl_bitset32_size(val32, mask32, nbits, names, compact); | 
|---|
| 767 |  | 
|---|
| 768 | if (nbits > ETHNL_SMALL_BITMAP_BITS) | 
|---|
| 769 | kfree(val32); | 
|---|
| 770 |  | 
|---|
| 771 | return ret; | 
|---|
| 772 | } | 
|---|
| 773 |  | 
|---|
| 774 | int ethnl_put_bitset(struct sk_buff *skb, int attrtype, | 
|---|
| 775 | const unsigned long *val, const unsigned long *mask, | 
|---|
| 776 | unsigned int nbits, ethnl_string_array_t names, | 
|---|
| 777 | bool compact) | 
|---|
| 778 | { | 
|---|
| 779 | u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS]; | 
|---|
| 780 | u32 small_val32[ETHNL_SMALL_BITMAP_WORDS]; | 
|---|
| 781 | u32 *mask32; | 
|---|
| 782 | u32 *val32; | 
|---|
| 783 | int ret; | 
|---|
| 784 |  | 
|---|
| 785 | if (nbits > ETHNL_SMALL_BITMAP_BITS) { | 
|---|
| 786 | unsigned int nwords = DIV_ROUND_UP(nbits, 32); | 
|---|
| 787 |  | 
|---|
| 788 | val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL); | 
|---|
| 789 | if (!val32) | 
|---|
| 790 | return -ENOMEM; | 
|---|
| 791 | mask32 = val32 + nwords; | 
|---|
| 792 | } else { | 
|---|
| 793 | val32 = small_val32; | 
|---|
| 794 | mask32 = small_mask32; | 
|---|
| 795 | } | 
|---|
| 796 |  | 
|---|
| 797 | bitmap_to_arr32(val32, val, nbits); | 
|---|
| 798 | if (mask) | 
|---|
| 799 | bitmap_to_arr32(mask32, mask, nbits); | 
|---|
| 800 | else | 
|---|
| 801 | mask32 = NULL; | 
|---|
| 802 | ret = ethnl_put_bitset32(skb, attrtype, val32, mask32, nbits, names, | 
|---|
| 803 | compact); | 
|---|
| 804 |  | 
|---|
| 805 | if (nbits > ETHNL_SMALL_BITMAP_BITS) | 
|---|
| 806 | kfree(val32); | 
|---|
| 807 |  | 
|---|
| 808 | return ret; | 
|---|
| 809 | } | 
|---|
| 810 |  | 
|---|
| 811 | int ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits, | 
|---|
| 812 | const struct nlattr *attr, ethnl_string_array_t names, | 
|---|
| 813 | struct netlink_ext_ack *extack, bool *mod) | 
|---|
| 814 | { | 
|---|
| 815 | u32 small_bitmap32[ETHNL_SMALL_BITMAP_WORDS]; | 
|---|
| 816 | u32 *bitmap32 = small_bitmap32; | 
|---|
| 817 | bool u32_mod = false; | 
|---|
| 818 | int ret; | 
|---|
| 819 |  | 
|---|
| 820 | if (nbits > ETHNL_SMALL_BITMAP_BITS) { | 
|---|
| 821 | unsigned int dst_words = DIV_ROUND_UP(nbits, 32); | 
|---|
| 822 |  | 
|---|
| 823 | bitmap32 = kmalloc_array(dst_words, sizeof(u32), GFP_KERNEL); | 
|---|
| 824 | if (!bitmap32) | 
|---|
| 825 | return -ENOMEM; | 
|---|
| 826 | } | 
|---|
| 827 |  | 
|---|
| 828 | bitmap_to_arr32(bitmap32, bitmap, nbits); | 
|---|
| 829 | ret = ethnl_update_bitset32(bitmap32, nbits, attr, names, extack, | 
|---|
| 830 | &u32_mod); | 
|---|
| 831 | if (u32_mod) { | 
|---|
| 832 | bitmap_from_arr32(bitmap, bitmap32, nbits); | 
|---|
| 833 | *mod = true; | 
|---|
| 834 | } | 
|---|
| 835 |  | 
|---|
| 836 | if (nbits > ETHNL_SMALL_BITMAP_BITS) | 
|---|
| 837 | kfree(bitmap32); | 
|---|
| 838 |  | 
|---|
| 839 | return ret; | 
|---|
| 840 | } | 
|---|
| 841 |  | 
|---|
| 842 | #else | 
|---|
| 843 |  | 
|---|
| 844 | /* On little endian 64-bit and all 32-bit architectures, an unsigned long | 
|---|
| 845 | * based bitmap can be interpreted as u32 based one using a simple cast. | 
|---|
| 846 | */ | 
|---|
| 847 |  | 
|---|
| 848 | int ethnl_bitset_size(const unsigned long *val, const unsigned long *mask, | 
|---|
| 849 | unsigned int nbits, ethnl_string_array_t names, | 
|---|
| 850 | bool compact) | 
|---|
| 851 | { | 
|---|
| 852 | return ethnl_bitset32_size(val: (const u32 *)val, mask: (const u32 *)mask, nbits, | 
|---|
| 853 | names, compact); | 
|---|
| 854 | } | 
|---|
| 855 |  | 
|---|
| 856 | int ethnl_put_bitset(struct sk_buff *skb, int attrtype, | 
|---|
| 857 | const unsigned long *val, const unsigned long *mask, | 
|---|
| 858 | unsigned int nbits, ethnl_string_array_t names, | 
|---|
| 859 | bool compact) | 
|---|
| 860 | { | 
|---|
| 861 | return ethnl_put_bitset32(skb, attrtype, val: (const u32 *)val, | 
|---|
| 862 | mask: (const u32 *)mask, nbits, names, compact); | 
|---|
| 863 | } | 
|---|
| 864 |  | 
|---|
| 865 | int ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits, | 
|---|
| 866 | const struct nlattr *attr, ethnl_string_array_t names, | 
|---|
| 867 | struct netlink_ext_ack *extack, bool *mod) | 
|---|
| 868 | { | 
|---|
| 869 | return ethnl_update_bitset32(bitmap: (u32 *)bitmap, nbits, attr, names, extack, | 
|---|
| 870 | mod); | 
|---|
| 871 | } | 
|---|
| 872 |  | 
|---|
| 873 | #endif /* BITS_PER_LONG == 64 && defined(__BIG_ENDIAN) */ | 
|---|
| 874 |  | 
|---|