1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2 /* 3 * Copyright (c) 2017, Mellanox Technologies inc. All rights reserved. 4 */ 5 #include <rdma/uverbs_ioctl.h> 6 #include <rdma/rdma_user_ioctl.h> 7 #include <linux/bitops.h> 8 #include "rdma_core.h" 9 #include "uverbs.h" 10 11 static int ib_uverbs_notsupp(struct uverbs_attr_bundle *attrs) 12 { 13 return -EOPNOTSUPP; 14 } 15 16 static void *uapi_add_elm(struct uverbs_api *uapi, u32 key, size_t alloc_size) 17 { 18 void *elm; 19 int rc; 20 21 if (key == UVERBS_API_KEY_ERR) 22 return ERR_PTR(-EOVERFLOW); 23 24 elm = kzalloc(alloc_size, GFP_KERNEL); 25 rc = radix_tree_insert(&uapi->radix, key, elm); 26 if (rc) { 27 kfree(elm); 28 return ERR_PTR(rc); 29 } 30 31 return elm; 32 } 33 34 static void *uapi_add_get_elm(struct uverbs_api *uapi, u32 key, 35 size_t alloc_size, bool *exists) 36 { 37 void *elm; 38 39 elm = uapi_add_elm(uapi, key, alloc_size); 40 if (!IS_ERR(elm)) { 41 *exists = false; 42 return elm; 43 } 44 45 if (elm != ERR_PTR(-EEXIST)) 46 return elm; 47 48 elm = radix_tree_lookup(&uapi->radix, key); 49 if (WARN_ON(!elm)) 50 return ERR_PTR(-EINVAL); 51 *exists = true; 52 return elm; 53 } 54 55 static int uapi_create_write(struct uverbs_api *uapi, 56 struct ib_device *ibdev, 57 const struct uapi_definition *def, 58 u32 obj_key, 59 u32 *cur_method_key) 60 { 61 struct uverbs_api_write_method *method_elm; 62 u32 method_key = obj_key; 63 bool exists; 64 65 if (def->write.is_ex) 66 method_key |= uapi_key_write_ex_method(def->write.command_num); 67 else 68 method_key |= uapi_key_write_method(def->write.command_num); 69 70 method_elm = uapi_add_get_elm(uapi, method_key, sizeof(*method_elm), 71 &exists); 72 if (IS_ERR(method_elm)) 73 return PTR_ERR(method_elm); 74 75 if (WARN_ON(exists && (def->write.is_ex != method_elm->is_ex))) 76 return -EINVAL; 77 78 method_elm->is_ex = def->write.is_ex; 79 method_elm->handler = def->func_write; 80 if (def->write.is_ex) 81 method_elm->disabled = !(ibdev->uverbs_ex_cmd_mask & 82 BIT_ULL(def->write.command_num)); 83 else 84 method_elm->disabled = !(ibdev->uverbs_cmd_mask & 85 BIT_ULL(def->write.command_num)); 86 87 if (!def->write.is_ex && def->func_write) { 88 method_elm->has_udata = def->write.has_udata; 89 method_elm->has_resp = def->write.has_resp; 90 method_elm->req_size = def->write.req_size; 91 method_elm->resp_size = def->write.resp_size; 92 } 93 94 *cur_method_key = method_key; 95 return 0; 96 } 97 98 static int uapi_merge_method(struct uverbs_api *uapi, 99 struct uverbs_api_object *obj_elm, u32 obj_key, 100 const struct uverbs_method_def *method, 101 bool is_driver) 102 { 103 u32 method_key = obj_key | uapi_key_ioctl_method(method->id); 104 struct uverbs_api_ioctl_method *method_elm; 105 unsigned int i; 106 bool exists; 107 108 if (!method->attrs) 109 return 0; 110 111 method_elm = uapi_add_get_elm(uapi, method_key, sizeof(*method_elm), 112 &exists); 113 if (IS_ERR(method_elm)) 114 return PTR_ERR(method_elm); 115 if (exists) { 116 /* 117 * This occurs when a driver uses ADD_UVERBS_ATTRIBUTES_SIMPLE 118 */ 119 if (WARN_ON(method->handler)) 120 return -EINVAL; 121 } else { 122 WARN_ON(!method->handler); 123 rcu_assign_pointer(method_elm->handler, method->handler); 124 if (method->handler != uverbs_destroy_def_handler) 125 method_elm->driver_method = is_driver; 126 } 127 128 for (i = 0; i != method->num_attrs; i++) { 129 const struct uverbs_attr_def *attr = (*method->attrs)[i]; 130 struct uverbs_api_attr *attr_slot; 131 132 if (!attr) 133 continue; 134 135 /* 136 * ENUM_IN contains the 'ids' pointer to the driver's .rodata, 137 * so if it is specified by a driver then it always makes this 138 * into a driver method. 139 */ 140 if (attr->attr.type == UVERBS_ATTR_TYPE_ENUM_IN) 141 method_elm->driver_method |= is_driver; 142 143 /* 144 * Like other uobject based things we only support a single 145 * uobject being NEW'd or DESTROY'd 146 */ 147 if (attr->attr.type == UVERBS_ATTR_TYPE_IDRS_ARRAY) { 148 u8 access = attr->attr.u2.objs_arr.access; 149 150 if (WARN_ON(access == UVERBS_ACCESS_NEW || 151 access == UVERBS_ACCESS_DESTROY)) 152 return -EINVAL; 153 } 154 155 attr_slot = 156 uapi_add_elm(uapi, method_key | uapi_key_attr(attr->id), 157 sizeof(*attr_slot)); 158 /* Attributes are not allowed to be modified by drivers */ 159 if (IS_ERR(attr_slot)) 160 return PTR_ERR(attr_slot); 161 162 attr_slot->spec = attr->attr; 163 } 164 165 return 0; 166 } 167 168 static int uapi_merge_obj_tree(struct uverbs_api *uapi, 169 const struct uverbs_object_def *obj, 170 bool is_driver) 171 { 172 struct uverbs_api_object *obj_elm; 173 unsigned int i; 174 u32 obj_key; 175 bool exists; 176 int rc; 177 178 obj_key = uapi_key_obj(obj->id); 179 obj_elm = uapi_add_get_elm(uapi, obj_key, sizeof(*obj_elm), &exists); 180 if (IS_ERR(obj_elm)) 181 return PTR_ERR(obj_elm); 182 183 if (obj->type_attrs) { 184 if (WARN_ON(obj_elm->type_attrs)) 185 return -EINVAL; 186 187 obj_elm->id = obj->id; 188 obj_elm->type_attrs = obj->type_attrs; 189 obj_elm->type_class = obj->type_attrs->type_class; 190 /* 191 * Today drivers are only permitted to use idr_class 192 * types. They cannot use FD types because we currently have 193 * no way to revoke the fops pointer after device 194 * disassociation. 195 */ 196 if (WARN_ON(is_driver && 197 obj->type_attrs->type_class != &uverbs_idr_class)) 198 return -EINVAL; 199 } 200 201 if (!obj->methods) 202 return 0; 203 204 for (i = 0; i != obj->num_methods; i++) { 205 const struct uverbs_method_def *method = (*obj->methods)[i]; 206 207 if (!method) 208 continue; 209 210 rc = uapi_merge_method(uapi, obj_elm, obj_key, method, 211 is_driver); 212 if (rc) 213 return rc; 214 } 215 216 return 0; 217 } 218 219 static int uapi_disable_elm(struct uverbs_api *uapi, 220 const struct uapi_definition *def, 221 u32 obj_key, 222 u32 method_key) 223 { 224 bool exists; 225 226 if (def->scope == UAPI_SCOPE_OBJECT) { 227 struct uverbs_api_object *obj_elm; 228 229 obj_elm = uapi_add_get_elm( 230 uapi, obj_key, sizeof(*obj_elm), &exists); 231 if (IS_ERR(obj_elm)) 232 return PTR_ERR(obj_elm); 233 obj_elm->disabled = 1; 234 return 0; 235 } 236 237 if (def->scope == UAPI_SCOPE_METHOD && 238 uapi_key_is_ioctl_method(method_key)) { 239 struct uverbs_api_ioctl_method *method_elm; 240 241 method_elm = uapi_add_get_elm(uapi, method_key, 242 sizeof(*method_elm), &exists); 243 if (IS_ERR(method_elm)) 244 return PTR_ERR(method_elm); 245 method_elm->disabled = 1; 246 return 0; 247 } 248 249 if (def->scope == UAPI_SCOPE_METHOD && 250 (uapi_key_is_write_method(method_key) || 251 uapi_key_is_write_ex_method(method_key))) { 252 struct uverbs_api_write_method *write_elm; 253 254 write_elm = uapi_add_get_elm(uapi, method_key, 255 sizeof(*write_elm), &exists); 256 if (IS_ERR(write_elm)) 257 return PTR_ERR(write_elm); 258 write_elm->disabled = 1; 259 return 0; 260 } 261 262 WARN_ON(true); 263 return -EINVAL; 264 } 265 266 static int uapi_merge_def(struct uverbs_api *uapi, struct ib_device *ibdev, 267 const struct uapi_definition *def_list, 268 bool is_driver) 269 { 270 const struct uapi_definition *def = def_list; 271 u32 cur_obj_key = UVERBS_API_KEY_ERR; 272 u32 cur_method_key = UVERBS_API_KEY_ERR; 273 bool exists; 274 int rc; 275 276 if (!def_list) 277 return 0; 278 279 for (;; def++) { 280 switch ((enum uapi_definition_kind)def->kind) { 281 case UAPI_DEF_CHAIN: 282 rc = uapi_merge_def(uapi, ibdev, def->chain, is_driver); 283 if (rc) 284 return rc; 285 continue; 286 287 case UAPI_DEF_CHAIN_OBJ_TREE: 288 if (WARN_ON(def->object_start.object_id != 289 def->chain_obj_tree->id)) 290 return -EINVAL; 291 292 cur_obj_key = uapi_key_obj(def->object_start.object_id); 293 rc = uapi_merge_obj_tree(uapi, def->chain_obj_tree, 294 is_driver); 295 if (rc) 296 return rc; 297 continue; 298 299 case UAPI_DEF_END: 300 return 0; 301 302 case UAPI_DEF_IS_SUPPORTED_DEV_FN: { 303 void **ibdev_fn = 304 (void *)(&ibdev->ops) + def->needs_fn_offset; 305 306 if (*ibdev_fn) 307 continue; 308 rc = uapi_disable_elm( 309 uapi, def, cur_obj_key, cur_method_key); 310 if (rc) 311 return rc; 312 continue; 313 } 314 315 case UAPI_DEF_IS_SUPPORTED_FUNC: 316 if (def->func_is_supported(ibdev)) 317 continue; 318 rc = uapi_disable_elm( 319 uapi, def, cur_obj_key, cur_method_key); 320 if (rc) 321 return rc; 322 continue; 323 324 case UAPI_DEF_OBJECT_START: { 325 struct uverbs_api_object *obj_elm; 326 327 cur_obj_key = uapi_key_obj(def->object_start.object_id); 328 obj_elm = uapi_add_get_elm(uapi, cur_obj_key, 329 sizeof(*obj_elm), &exists); 330 if (IS_ERR(obj_elm)) 331 return PTR_ERR(obj_elm); 332 continue; 333 } 334 335 case UAPI_DEF_WRITE: 336 rc = uapi_create_write( 337 uapi, ibdev, def, cur_obj_key, &cur_method_key); 338 if (rc) 339 return rc; 340 continue; 341 } 342 WARN_ON(true); 343 return -EINVAL; 344 } 345 } 346 347 static int 348 uapi_finalize_ioctl_method(struct uverbs_api *uapi, 349 struct uverbs_api_ioctl_method *method_elm, 350 u32 method_key) 351 { 352 struct radix_tree_iter iter; 353 unsigned int num_attrs = 0; 354 unsigned int max_bkey = 0; 355 bool single_uobj = false; 356 void __rcu **slot; 357 358 method_elm->destroy_bkey = UVERBS_API_ATTR_BKEY_LEN; 359 radix_tree_for_each_slot (slot, &uapi->radix, &iter, 360 uapi_key_attrs_start(method_key)) { 361 struct uverbs_api_attr *elm = 362 rcu_dereference_protected(*slot, true); 363 u32 attr_key = iter.index & UVERBS_API_ATTR_KEY_MASK; 364 u32 attr_bkey = uapi_bkey_attr(attr_key); 365 u8 type = elm->spec.type; 366 367 if (uapi_key_attr_to_ioctl_method(iter.index) != 368 uapi_key_attr_to_ioctl_method(method_key)) 369 break; 370 371 if (elm->spec.mandatory) 372 __set_bit(attr_bkey, method_elm->attr_mandatory); 373 374 if (elm->spec.is_udata) 375 method_elm->has_udata = true; 376 377 if (type == UVERBS_ATTR_TYPE_IDR || 378 type == UVERBS_ATTR_TYPE_FD) { 379 u8 access = elm->spec.u.obj.access; 380 381 /* 382 * Verbs specs may only have one NEW/DESTROY, we don't 383 * have the infrastructure to abort multiple NEW's or 384 * cope with multiple DESTROY failure. 385 */ 386 if (access == UVERBS_ACCESS_NEW || 387 access == UVERBS_ACCESS_DESTROY) { 388 if (WARN_ON(single_uobj)) 389 return -EINVAL; 390 391 single_uobj = true; 392 if (WARN_ON(!elm->spec.mandatory)) 393 return -EINVAL; 394 } 395 396 if (access == UVERBS_ACCESS_DESTROY) 397 method_elm->destroy_bkey = attr_bkey; 398 } 399 400 max_bkey = max(max_bkey, attr_bkey); 401 num_attrs++; 402 } 403 404 method_elm->key_bitmap_len = max_bkey + 1; 405 WARN_ON(method_elm->key_bitmap_len > UVERBS_API_ATTR_BKEY_LEN); 406 407 uapi_compute_bundle_size(method_elm, num_attrs); 408 return 0; 409 } 410 411 static int uapi_finalize(struct uverbs_api *uapi) 412 { 413 const struct uverbs_api_write_method **data; 414 unsigned long max_write_ex = 0; 415 unsigned long max_write = 0; 416 struct radix_tree_iter iter; 417 void __rcu **slot; 418 int rc; 419 int i; 420 421 radix_tree_for_each_slot (slot, &uapi->radix, &iter, 0) { 422 struct uverbs_api_ioctl_method *method_elm = 423 rcu_dereference_protected(*slot, true); 424 425 if (uapi_key_is_ioctl_method(iter.index)) { 426 rc = uapi_finalize_ioctl_method(uapi, method_elm, 427 iter.index); 428 if (rc) 429 return rc; 430 } 431 432 if (uapi_key_is_write_method(iter.index)) 433 max_write = max(max_write, 434 iter.index & UVERBS_API_ATTR_KEY_MASK); 435 if (uapi_key_is_write_ex_method(iter.index)) 436 max_write_ex = 437 max(max_write_ex, 438 iter.index & UVERBS_API_ATTR_KEY_MASK); 439 } 440 441 uapi->notsupp_method.handler = ib_uverbs_notsupp; 442 uapi->num_write = max_write + 1; 443 uapi->num_write_ex = max_write_ex + 1; 444 data = kmalloc_array(uapi->num_write + uapi->num_write_ex, 445 sizeof(*uapi->write_methods), GFP_KERNEL); 446 for (i = 0; i != uapi->num_write + uapi->num_write_ex; i++) 447 data[i] = &uapi->notsupp_method; 448 uapi->write_methods = data; 449 uapi->write_ex_methods = data + uapi->num_write; 450 451 radix_tree_for_each_slot (slot, &uapi->radix, &iter, 0) { 452 if (uapi_key_is_write_method(iter.index)) 453 uapi->write_methods[iter.index & 454 UVERBS_API_ATTR_KEY_MASK] = 455 rcu_dereference_protected(*slot, true); 456 if (uapi_key_is_write_ex_method(iter.index)) 457 uapi->write_ex_methods[iter.index & 458 UVERBS_API_ATTR_KEY_MASK] = 459 rcu_dereference_protected(*slot, true); 460 } 461 462 return 0; 463 } 464 465 static void uapi_remove_range(struct uverbs_api *uapi, u32 start, u32 last) 466 { 467 struct radix_tree_iter iter; 468 void __rcu **slot; 469 470 radix_tree_for_each_slot (slot, &uapi->radix, &iter, start) { 471 if (iter.index > last) 472 return; 473 kfree(rcu_dereference_protected(*slot, true)); 474 radix_tree_iter_delete(&uapi->radix, &iter, slot); 475 } 476 } 477 478 static void uapi_remove_object(struct uverbs_api *uapi, u32 obj_key) 479 { 480 uapi_remove_range(uapi, obj_key, 481 obj_key | UVERBS_API_METHOD_KEY_MASK | 482 UVERBS_API_ATTR_KEY_MASK); 483 } 484 485 static void uapi_remove_method(struct uverbs_api *uapi, u32 method_key) 486 { 487 uapi_remove_range(uapi, method_key, 488 method_key | UVERBS_API_ATTR_KEY_MASK); 489 } 490 491 492 static u32 uapi_get_obj_id(struct uverbs_attr_spec *spec) 493 { 494 if (spec->type == UVERBS_ATTR_TYPE_IDR || 495 spec->type == UVERBS_ATTR_TYPE_FD) 496 return spec->u.obj.obj_type; 497 if (spec->type == UVERBS_ATTR_TYPE_IDRS_ARRAY) 498 return spec->u2.objs_arr.obj_type; 499 return UVERBS_API_KEY_ERR; 500 } 501 502 static void uapi_key_okay(u32 key) 503 { 504 unsigned int count = 0; 505 506 if (uapi_key_is_object(key)) 507 count++; 508 if (uapi_key_is_ioctl_method(key)) 509 count++; 510 if (uapi_key_is_write_method(key)) 511 count++; 512 if (uapi_key_is_write_ex_method(key)) 513 count++; 514 if (uapi_key_is_attr(key)) 515 count++; 516 WARN(count != 1, "Bad count %d key=%x", count, key); 517 } 518 519 static void uapi_finalize_disable(struct uverbs_api *uapi) 520 { 521 struct radix_tree_iter iter; 522 u32 starting_key = 0; 523 bool scan_again = false; 524 void __rcu **slot; 525 526 again: 527 radix_tree_for_each_slot (slot, &uapi->radix, &iter, starting_key) { 528 uapi_key_okay(iter.index); 529 530 if (uapi_key_is_object(iter.index)) { 531 struct uverbs_api_object *obj_elm = 532 rcu_dereference_protected(*slot, true); 533 534 if (obj_elm->disabled) { 535 /* Have to check all the attrs again */ 536 scan_again = true; 537 starting_key = iter.index; 538 uapi_remove_object(uapi, iter.index); 539 goto again; 540 } 541 continue; 542 } 543 544 if (uapi_key_is_ioctl_method(iter.index)) { 545 struct uverbs_api_ioctl_method *method_elm = 546 rcu_dereference_protected(*slot, true); 547 548 if (method_elm->disabled) { 549 starting_key = iter.index; 550 uapi_remove_method(uapi, iter.index); 551 goto again; 552 } 553 continue; 554 } 555 556 if (uapi_key_is_write_method(iter.index) || 557 uapi_key_is_write_ex_method(iter.index)) { 558 struct uverbs_api_write_method *method_elm = 559 rcu_dereference_protected(*slot, true); 560 561 if (method_elm->disabled) { 562 kfree(method_elm); 563 radix_tree_iter_delete(&uapi->radix, &iter, slot); 564 } 565 continue; 566 } 567 568 if (uapi_key_is_attr(iter.index)) { 569 struct uverbs_api_attr *attr_elm = 570 rcu_dereference_protected(*slot, true); 571 const struct uverbs_api_object *tmp_obj; 572 u32 obj_key; 573 574 /* 575 * If the method has a mandatory object handle 576 * attribute which relies on an object which is not 577 * present then the entire method is uncallable. 578 */ 579 if (!attr_elm->spec.mandatory) 580 continue; 581 obj_key = uapi_get_obj_id(&attr_elm->spec); 582 if (obj_key == UVERBS_API_KEY_ERR) 583 continue; 584 tmp_obj = uapi_get_object(uapi, obj_key); 585 if (IS_ERR(tmp_obj)) { 586 if (PTR_ERR(tmp_obj) == -ENOMSG) 587 continue; 588 } else { 589 if (!tmp_obj->disabled) 590 continue; 591 } 592 593 starting_key = iter.index; 594 uapi_remove_method( 595 uapi, 596 iter.index & (UVERBS_API_OBJ_KEY_MASK | 597 UVERBS_API_METHOD_KEY_MASK)); 598 goto again; 599 } 600 601 WARN_ON(false); 602 } 603 604 if (!scan_again) 605 return; 606 scan_again = false; 607 starting_key = 0; 608 goto again; 609 } 610 611 void uverbs_destroy_api(struct uverbs_api *uapi) 612 { 613 if (!uapi) 614 return; 615 616 uapi_remove_range(uapi, 0, U32_MAX); 617 kfree(uapi->write_methods); 618 kfree(uapi); 619 } 620 621 static const struct uapi_definition uverbs_core_api[] = { 622 UAPI_DEF_CHAIN(uverbs_def_obj_counters), 623 UAPI_DEF_CHAIN(uverbs_def_obj_cq), 624 UAPI_DEF_CHAIN(uverbs_def_obj_device), 625 UAPI_DEF_CHAIN(uverbs_def_obj_dm), 626 UAPI_DEF_CHAIN(uverbs_def_obj_flow_action), 627 UAPI_DEF_CHAIN(uverbs_def_obj_intf), 628 UAPI_DEF_CHAIN(uverbs_def_obj_mr), 629 UAPI_DEF_CHAIN(uverbs_def_write_intf), 630 {}, 631 }; 632 633 struct uverbs_api *uverbs_alloc_api(struct ib_device *ibdev) 634 { 635 struct uverbs_api *uapi; 636 int rc; 637 638 uapi = kzalloc(sizeof(*uapi), GFP_KERNEL); 639 if (!uapi) 640 return ERR_PTR(-ENOMEM); 641 642 INIT_RADIX_TREE(&uapi->radix, GFP_KERNEL); 643 uapi->driver_id = ibdev->driver_id; 644 645 rc = uapi_merge_def(uapi, ibdev, uverbs_core_api, false); 646 if (rc) 647 goto err; 648 rc = uapi_merge_def(uapi, ibdev, ibdev->driver_def, true); 649 if (rc) 650 goto err; 651 652 uapi_finalize_disable(uapi); 653 rc = uapi_finalize(uapi); 654 if (rc) 655 goto err; 656 657 return uapi; 658 err: 659 if (rc != -ENOMEM) 660 dev_err(&ibdev->dev, 661 "Setup of uverbs_api failed, kernel parsing tree description is not valid (%d)??\n", 662 rc); 663 664 uverbs_destroy_api(uapi); 665 return ERR_PTR(rc); 666 } 667 668 /* 669 * The pre version is done before destroying the HW objects, it only blocks 670 * off method access. All methods that require the ib_dev or the module data 671 * must test one of these assignments prior to continuing. 672 */ 673 void uverbs_disassociate_api_pre(struct ib_uverbs_device *uverbs_dev) 674 { 675 struct uverbs_api *uapi = uverbs_dev->uapi; 676 struct radix_tree_iter iter; 677 void __rcu **slot; 678 679 rcu_assign_pointer(uverbs_dev->ib_dev, NULL); 680 681 radix_tree_for_each_slot (slot, &uapi->radix, &iter, 0) { 682 if (uapi_key_is_ioctl_method(iter.index)) { 683 struct uverbs_api_ioctl_method *method_elm = 684 rcu_dereference_protected(*slot, true); 685 686 if (method_elm->driver_method) 687 rcu_assign_pointer(method_elm->handler, NULL); 688 } 689 } 690 691 synchronize_srcu(&uverbs_dev->disassociate_srcu); 692 } 693 694 /* 695 * Called when a driver disassociates from the ib_uverbs_device. The 696 * assumption is that the driver module will unload after. Replace everything 697 * related to the driver with NULL as a safety measure. 698 */ 699 void uverbs_disassociate_api(struct uverbs_api *uapi) 700 { 701 struct radix_tree_iter iter; 702 void __rcu **slot; 703 704 radix_tree_for_each_slot (slot, &uapi->radix, &iter, 0) { 705 if (uapi_key_is_object(iter.index)) { 706 struct uverbs_api_object *object_elm = 707 rcu_dereference_protected(*slot, true); 708 709 /* 710 * Some type_attrs are in the driver module. We don't 711 * bother to keep track of which since there should be 712 * no use of this after disassociate. 713 */ 714 object_elm->type_attrs = NULL; 715 } else if (uapi_key_is_attr(iter.index)) { 716 struct uverbs_api_attr *elm = 717 rcu_dereference_protected(*slot, true); 718 719 if (elm->spec.type == UVERBS_ATTR_TYPE_ENUM_IN) 720 elm->spec.u2.enum_def.ids = NULL; 721 } 722 } 723 } 724