1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> 5 */ 6 7 #include "devl_internal.h" 8 9 struct devlink_linecard { 10 struct list_head list; 11 struct devlink *devlink; 12 unsigned int index; 13 const struct devlink_linecard_ops *ops; 14 void *priv; 15 enum devlink_linecard_state state; 16 struct mutex state_lock; /* Protects state */ 17 const char *type; 18 struct devlink_linecard_type *types; 19 unsigned int types_count; 20 u32 rel_index; 21 }; 22 23 unsigned int devlink_linecard_index(struct devlink_linecard *linecard) 24 { 25 return linecard->index; 26 } 27 28 static struct devlink_linecard * 29 devlink_linecard_get_by_index(struct devlink *devlink, 30 unsigned int linecard_index) 31 { 32 struct devlink_linecard *devlink_linecard; 33 34 list_for_each_entry(devlink_linecard, &devlink->linecard_list, list) { 35 if (devlink_linecard->index == linecard_index) 36 return devlink_linecard; 37 } 38 return NULL; 39 } 40 41 static bool devlink_linecard_index_exists(struct devlink *devlink, 42 unsigned int linecard_index) 43 { 44 return devlink_linecard_get_by_index(devlink, linecard_index); 45 } 46 47 static struct devlink_linecard * 48 devlink_linecard_get_from_attrs(struct devlink *devlink, struct nlattr **attrs) 49 { 50 if (attrs[DEVLINK_ATTR_LINECARD_INDEX]) { 51 u32 linecard_index = nla_get_u32(attrs[DEVLINK_ATTR_LINECARD_INDEX]); 52 struct devlink_linecard *linecard; 53 54 linecard = devlink_linecard_get_by_index(devlink, linecard_index); 55 if (!linecard) 56 return ERR_PTR(-ENODEV); 57 return linecard; 58 } 59 return ERR_PTR(-EINVAL); 60 } 61 62 static struct devlink_linecard * 63 devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info) 64 { 65 return devlink_linecard_get_from_attrs(devlink, info->attrs); 66 } 67 68 struct devlink_linecard_type { 69 const char *type; 70 const void *priv; 71 }; 72 73 static int devlink_nl_linecard_fill(struct sk_buff *msg, 74 struct devlink *devlink, 75 struct devlink_linecard *linecard, 76 enum devlink_command cmd, u32 portid, 77 u32 seq, int flags, 78 struct netlink_ext_ack *extack) 79 { 80 struct devlink_linecard_type *linecard_type; 81 struct nlattr *attr; 82 void *hdr; 83 int i; 84 85 hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); 86 if (!hdr) 87 return -EMSGSIZE; 88 89 if (devlink_nl_put_handle(msg, devlink)) 90 goto nla_put_failure; 91 if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index)) 92 goto nla_put_failure; 93 if (nla_put_u8(msg, DEVLINK_ATTR_LINECARD_STATE, linecard->state)) 94 goto nla_put_failure; 95 if (linecard->type && 96 nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, linecard->type)) 97 goto nla_put_failure; 98 99 if (linecard->types_count) { 100 attr = nla_nest_start(msg, 101 DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES); 102 if (!attr) 103 goto nla_put_failure; 104 for (i = 0; i < linecard->types_count; i++) { 105 linecard_type = &linecard->types[i]; 106 if (nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, 107 linecard_type->type)) { 108 nla_nest_cancel(msg, attr); 109 goto nla_put_failure; 110 } 111 } 112 nla_nest_end(msg, attr); 113 } 114 115 if (devlink_rel_devlink_handle_put(msg, devlink, 116 linecard->rel_index, 117 DEVLINK_ATTR_NESTED_DEVLINK, 118 NULL)) 119 goto nla_put_failure; 120 121 genlmsg_end(msg, hdr); 122 return 0; 123 124 nla_put_failure: 125 genlmsg_cancel(msg, hdr); 126 return -EMSGSIZE; 127 } 128 129 static void devlink_linecard_notify(struct devlink_linecard *linecard, 130 enum devlink_command cmd) 131 { 132 struct devlink *devlink = linecard->devlink; 133 struct sk_buff *msg; 134 int err; 135 136 WARN_ON(cmd != DEVLINK_CMD_LINECARD_NEW && 137 cmd != DEVLINK_CMD_LINECARD_DEL); 138 139 if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED)) 140 return; 141 142 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 143 if (!msg) 144 return; 145 146 err = devlink_nl_linecard_fill(msg, devlink, linecard, cmd, 0, 0, 0, 147 NULL); 148 if (err) { 149 nlmsg_free(msg); 150 return; 151 } 152 153 genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), 154 msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); 155 } 156 157 void devlink_linecards_notify_register(struct devlink *devlink) 158 { 159 struct devlink_linecard *linecard; 160 161 list_for_each_entry(linecard, &devlink->linecard_list, list) 162 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 163 } 164 165 void devlink_linecards_notify_unregister(struct devlink *devlink) 166 { 167 struct devlink_linecard *linecard; 168 169 list_for_each_entry_reverse(linecard, &devlink->linecard_list, list) 170 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL); 171 } 172 173 int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info) 174 { 175 struct devlink *devlink = info->user_ptr[0]; 176 struct devlink_linecard *linecard; 177 struct sk_buff *msg; 178 int err; 179 180 linecard = devlink_linecard_get_from_info(devlink, info); 181 if (IS_ERR(linecard)) 182 return PTR_ERR(linecard); 183 184 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 185 if (!msg) 186 return -ENOMEM; 187 188 mutex_lock(&linecard->state_lock); 189 err = devlink_nl_linecard_fill(msg, devlink, linecard, 190 DEVLINK_CMD_LINECARD_NEW, 191 info->snd_portid, info->snd_seq, 0, 192 info->extack); 193 mutex_unlock(&linecard->state_lock); 194 if (err) { 195 nlmsg_free(msg); 196 return err; 197 } 198 199 return genlmsg_reply(msg, info); 200 } 201 202 static int devlink_nl_linecard_get_dump_one(struct sk_buff *msg, 203 struct devlink *devlink, 204 struct netlink_callback *cb, 205 int flags) 206 { 207 struct devlink_nl_dump_state *state = devlink_dump_state(cb); 208 struct devlink_linecard *linecard; 209 int idx = 0; 210 int err = 0; 211 212 list_for_each_entry(linecard, &devlink->linecard_list, list) { 213 if (idx < state->idx) { 214 idx++; 215 continue; 216 } 217 mutex_lock(&linecard->state_lock); 218 err = devlink_nl_linecard_fill(msg, devlink, linecard, 219 DEVLINK_CMD_LINECARD_NEW, 220 NETLINK_CB(cb->skb).portid, 221 cb->nlh->nlmsg_seq, flags, 222 cb->extack); 223 mutex_unlock(&linecard->state_lock); 224 if (err) { 225 state->idx = idx; 226 break; 227 } 228 idx++; 229 } 230 231 return err; 232 } 233 234 int devlink_nl_linecard_get_dumpit(struct sk_buff *skb, 235 struct netlink_callback *cb) 236 { 237 return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one); 238 } 239 240 static struct devlink_linecard_type * 241 devlink_linecard_type_lookup(struct devlink_linecard *linecard, 242 const char *type) 243 { 244 struct devlink_linecard_type *linecard_type; 245 int i; 246 247 for (i = 0; i < linecard->types_count; i++) { 248 linecard_type = &linecard->types[i]; 249 if (!strcmp(type, linecard_type->type)) 250 return linecard_type; 251 } 252 return NULL; 253 } 254 255 static int devlink_linecard_type_set(struct devlink_linecard *linecard, 256 const char *type, 257 struct netlink_ext_ack *extack) 258 { 259 const struct devlink_linecard_ops *ops = linecard->ops; 260 struct devlink_linecard_type *linecard_type; 261 int err; 262 263 mutex_lock(&linecard->state_lock); 264 if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) { 265 NL_SET_ERR_MSG(extack, "Line card is currently being provisioned"); 266 err = -EBUSY; 267 goto out; 268 } 269 if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) { 270 NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned"); 271 err = -EBUSY; 272 goto out; 273 } 274 275 linecard_type = devlink_linecard_type_lookup(linecard, type); 276 if (!linecard_type) { 277 NL_SET_ERR_MSG(extack, "Unsupported line card type provided"); 278 err = -EINVAL; 279 goto out; 280 } 281 282 if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED && 283 linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) { 284 NL_SET_ERR_MSG(extack, "Line card already provisioned"); 285 err = -EBUSY; 286 /* Check if the line card is provisioned in the same 287 * way the user asks. In case it is, make the operation 288 * to return success. 289 */ 290 if (ops->same_provision && 291 ops->same_provision(linecard, linecard->priv, 292 linecard_type->type, 293 linecard_type->priv)) 294 err = 0; 295 goto out; 296 } 297 298 linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING; 299 linecard->type = linecard_type->type; 300 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 301 mutex_unlock(&linecard->state_lock); 302 err = ops->provision(linecard, linecard->priv, linecard_type->type, 303 linecard_type->priv, extack); 304 if (err) { 305 /* Provisioning failed. Assume the linecard is unprovisioned 306 * for future operations. 307 */ 308 mutex_lock(&linecard->state_lock); 309 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 310 linecard->type = NULL; 311 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 312 mutex_unlock(&linecard->state_lock); 313 } 314 return err; 315 316 out: 317 mutex_unlock(&linecard->state_lock); 318 return err; 319 } 320 321 static int devlink_linecard_type_unset(struct devlink_linecard *linecard, 322 struct netlink_ext_ack *extack) 323 { 324 int err; 325 326 mutex_lock(&linecard->state_lock); 327 if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) { 328 NL_SET_ERR_MSG(extack, "Line card is currently being provisioned"); 329 err = -EBUSY; 330 goto out; 331 } 332 if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) { 333 NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned"); 334 err = -EBUSY; 335 goto out; 336 } 337 if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) { 338 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 339 linecard->type = NULL; 340 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 341 err = 0; 342 goto out; 343 } 344 345 if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) { 346 NL_SET_ERR_MSG(extack, "Line card is not provisioned"); 347 err = 0; 348 goto out; 349 } 350 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING; 351 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 352 mutex_unlock(&linecard->state_lock); 353 err = linecard->ops->unprovision(linecard, linecard->priv, 354 extack); 355 if (err) { 356 /* Unprovisioning failed. Assume the linecard is unprovisioned 357 * for future operations. 358 */ 359 mutex_lock(&linecard->state_lock); 360 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 361 linecard->type = NULL; 362 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 363 mutex_unlock(&linecard->state_lock); 364 } 365 return err; 366 367 out: 368 mutex_unlock(&linecard->state_lock); 369 return err; 370 } 371 372 int devlink_nl_cmd_linecard_set_doit(struct sk_buff *skb, 373 struct genl_info *info) 374 { 375 struct netlink_ext_ack *extack = info->extack; 376 struct devlink *devlink = info->user_ptr[0]; 377 struct devlink_linecard *linecard; 378 int err; 379 380 linecard = devlink_linecard_get_from_info(devlink, info); 381 if (IS_ERR(linecard)) 382 return PTR_ERR(linecard); 383 384 if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) { 385 const char *type; 386 387 type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]); 388 if (strcmp(type, "")) { 389 err = devlink_linecard_type_set(linecard, type, extack); 390 if (err) 391 return err; 392 } else { 393 err = devlink_linecard_type_unset(linecard, extack); 394 if (err) 395 return err; 396 } 397 } 398 399 return 0; 400 } 401 402 static int devlink_linecard_types_init(struct devlink_linecard *linecard) 403 { 404 struct devlink_linecard_type *linecard_type; 405 unsigned int count; 406 int i; 407 408 count = linecard->ops->types_count(linecard, linecard->priv); 409 linecard->types = kmalloc_array(count, sizeof(*linecard_type), 410 GFP_KERNEL); 411 if (!linecard->types) 412 return -ENOMEM; 413 linecard->types_count = count; 414 415 for (i = 0; i < count; i++) { 416 linecard_type = &linecard->types[i]; 417 linecard->ops->types_get(linecard, linecard->priv, i, 418 &linecard_type->type, 419 &linecard_type->priv); 420 } 421 return 0; 422 } 423 424 static void devlink_linecard_types_fini(struct devlink_linecard *linecard) 425 { 426 kfree(linecard->types); 427 } 428 429 /** 430 * devl_linecard_create - Create devlink linecard 431 * 432 * @devlink: devlink 433 * @linecard_index: driver-specific numerical identifier of the linecard 434 * @ops: linecards ops 435 * @priv: user priv pointer 436 * 437 * Create devlink linecard instance with provided linecard index. 438 * Caller can use any indexing, even hw-related one. 439 * 440 * Return: Line card structure or an ERR_PTR() encoded error code. 441 */ 442 struct devlink_linecard * 443 devl_linecard_create(struct devlink *devlink, unsigned int linecard_index, 444 const struct devlink_linecard_ops *ops, void *priv) 445 { 446 struct devlink_linecard *linecard; 447 int err; 448 449 if (WARN_ON(!ops || !ops->provision || !ops->unprovision || 450 !ops->types_count || !ops->types_get)) 451 return ERR_PTR(-EINVAL); 452 453 if (devlink_linecard_index_exists(devlink, linecard_index)) 454 return ERR_PTR(-EEXIST); 455 456 linecard = kzalloc(sizeof(*linecard), GFP_KERNEL); 457 if (!linecard) 458 return ERR_PTR(-ENOMEM); 459 460 linecard->devlink = devlink; 461 linecard->index = linecard_index; 462 linecard->ops = ops; 463 linecard->priv = priv; 464 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 465 mutex_init(&linecard->state_lock); 466 467 err = devlink_linecard_types_init(linecard); 468 if (err) { 469 mutex_destroy(&linecard->state_lock); 470 kfree(linecard); 471 return ERR_PTR(err); 472 } 473 474 list_add_tail(&linecard->list, &devlink->linecard_list); 475 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 476 return linecard; 477 } 478 EXPORT_SYMBOL_GPL(devl_linecard_create); 479 480 /** 481 * devl_linecard_destroy - Destroy devlink linecard 482 * 483 * @linecard: devlink linecard 484 */ 485 void devl_linecard_destroy(struct devlink_linecard *linecard) 486 { 487 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL); 488 list_del(&linecard->list); 489 devlink_linecard_types_fini(linecard); 490 mutex_destroy(&linecard->state_lock); 491 kfree(linecard); 492 } 493 EXPORT_SYMBOL_GPL(devl_linecard_destroy); 494 495 /** 496 * devlink_linecard_provision_set - Set provisioning on linecard 497 * 498 * @linecard: devlink linecard 499 * @type: linecard type 500 * 501 * This is either called directly from the provision() op call or 502 * as a result of the provision() op call asynchronously. 503 */ 504 void devlink_linecard_provision_set(struct devlink_linecard *linecard, 505 const char *type) 506 { 507 mutex_lock(&linecard->state_lock); 508 WARN_ON(linecard->type && strcmp(linecard->type, type)); 509 linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED; 510 linecard->type = type; 511 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 512 mutex_unlock(&linecard->state_lock); 513 } 514 EXPORT_SYMBOL_GPL(devlink_linecard_provision_set); 515 516 /** 517 * devlink_linecard_provision_clear - Clear provisioning on linecard 518 * 519 * @linecard: devlink linecard 520 * 521 * This is either called directly from the unprovision() op call or 522 * as a result of the unprovision() op call asynchronously. 523 */ 524 void devlink_linecard_provision_clear(struct devlink_linecard *linecard) 525 { 526 mutex_lock(&linecard->state_lock); 527 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 528 linecard->type = NULL; 529 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 530 mutex_unlock(&linecard->state_lock); 531 } 532 EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear); 533 534 /** 535 * devlink_linecard_provision_fail - Fail provisioning on linecard 536 * 537 * @linecard: devlink linecard 538 * 539 * This is either called directly from the provision() op call or 540 * as a result of the provision() op call asynchronously. 541 */ 542 void devlink_linecard_provision_fail(struct devlink_linecard *linecard) 543 { 544 mutex_lock(&linecard->state_lock); 545 linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED; 546 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 547 mutex_unlock(&linecard->state_lock); 548 } 549 EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail); 550 551 /** 552 * devlink_linecard_activate - Set linecard active 553 * 554 * @linecard: devlink linecard 555 */ 556 void devlink_linecard_activate(struct devlink_linecard *linecard) 557 { 558 mutex_lock(&linecard->state_lock); 559 WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED); 560 linecard->state = DEVLINK_LINECARD_STATE_ACTIVE; 561 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 562 mutex_unlock(&linecard->state_lock); 563 } 564 EXPORT_SYMBOL_GPL(devlink_linecard_activate); 565 566 /** 567 * devlink_linecard_deactivate - Set linecard inactive 568 * 569 * @linecard: devlink linecard 570 */ 571 void devlink_linecard_deactivate(struct devlink_linecard *linecard) 572 { 573 mutex_lock(&linecard->state_lock); 574 switch (linecard->state) { 575 case DEVLINK_LINECARD_STATE_ACTIVE: 576 linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED; 577 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 578 break; 579 case DEVLINK_LINECARD_STATE_UNPROVISIONING: 580 /* Line card is being deactivated as part 581 * of unprovisioning flow. 582 */ 583 break; 584 default: 585 WARN_ON(1); 586 break; 587 } 588 mutex_unlock(&linecard->state_lock); 589 } 590 EXPORT_SYMBOL_GPL(devlink_linecard_deactivate); 591 592 static void devlink_linecard_rel_notify_cb(struct devlink *devlink, 593 u32 linecard_index) 594 { 595 struct devlink_linecard *linecard; 596 597 linecard = devlink_linecard_get_by_index(devlink, linecard_index); 598 if (!linecard) 599 return; 600 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 601 } 602 603 static void devlink_linecard_rel_cleanup_cb(struct devlink *devlink, 604 u32 linecard_index, u32 rel_index) 605 { 606 struct devlink_linecard *linecard; 607 608 linecard = devlink_linecard_get_by_index(devlink, linecard_index); 609 if (linecard && linecard->rel_index == rel_index) 610 linecard->rel_index = 0; 611 } 612 613 /** 614 * devlink_linecard_nested_dl_set - Attach/detach nested devlink 615 * instance to linecard. 616 * 617 * @linecard: devlink linecard 618 * @nested_devlink: devlink instance to attach or NULL to detach 619 */ 620 int devlink_linecard_nested_dl_set(struct devlink_linecard *linecard, 621 struct devlink *nested_devlink) 622 { 623 return devlink_rel_nested_in_add(&linecard->rel_index, 624 linecard->devlink->index, 625 linecard->index, 626 devlink_linecard_rel_notify_cb, 627 devlink_linecard_rel_cleanup_cb, 628 nested_devlink); 629 } 630 EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set); 631