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 (!__devl_is_registered(devlink) || !devlink_nl_notify_need(devlink)) 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 devlink_nl_notify_send(devlink, msg); 154 } 155 156 void devlink_linecards_notify_register(struct devlink *devlink) 157 { 158 struct devlink_linecard *linecard; 159 160 list_for_each_entry(linecard, &devlink->linecard_list, list) 161 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 162 } 163 164 void devlink_linecards_notify_unregister(struct devlink *devlink) 165 { 166 struct devlink_linecard *linecard; 167 168 list_for_each_entry_reverse(linecard, &devlink->linecard_list, list) 169 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL); 170 } 171 172 int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info) 173 { 174 struct devlink *devlink = info->user_ptr[0]; 175 struct devlink_linecard *linecard; 176 struct sk_buff *msg; 177 int err; 178 179 linecard = devlink_linecard_get_from_info(devlink, info); 180 if (IS_ERR(linecard)) 181 return PTR_ERR(linecard); 182 183 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 184 if (!msg) 185 return -ENOMEM; 186 187 mutex_lock(&linecard->state_lock); 188 err = devlink_nl_linecard_fill(msg, devlink, linecard, 189 DEVLINK_CMD_LINECARD_NEW, 190 info->snd_portid, info->snd_seq, 0, 191 info->extack); 192 mutex_unlock(&linecard->state_lock); 193 if (err) { 194 nlmsg_free(msg); 195 return err; 196 } 197 198 return genlmsg_reply(msg, info); 199 } 200 201 static int devlink_nl_linecard_get_dump_one(struct sk_buff *msg, 202 struct devlink *devlink, 203 struct netlink_callback *cb, 204 int flags) 205 { 206 struct devlink_nl_dump_state *state = devlink_dump_state(cb); 207 struct devlink_linecard *linecard; 208 int idx = 0; 209 int err = 0; 210 211 list_for_each_entry(linecard, &devlink->linecard_list, list) { 212 if (idx < state->idx) { 213 idx++; 214 continue; 215 } 216 mutex_lock(&linecard->state_lock); 217 err = devlink_nl_linecard_fill(msg, devlink, linecard, 218 DEVLINK_CMD_LINECARD_NEW, 219 NETLINK_CB(cb->skb).portid, 220 cb->nlh->nlmsg_seq, flags, 221 cb->extack); 222 mutex_unlock(&linecard->state_lock); 223 if (err) { 224 state->idx = idx; 225 break; 226 } 227 idx++; 228 } 229 230 return err; 231 } 232 233 int devlink_nl_linecard_get_dumpit(struct sk_buff *skb, 234 struct netlink_callback *cb) 235 { 236 return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one); 237 } 238 239 static struct devlink_linecard_type * 240 devlink_linecard_type_lookup(struct devlink_linecard *linecard, 241 const char *type) 242 { 243 struct devlink_linecard_type *linecard_type; 244 int i; 245 246 for (i = 0; i < linecard->types_count; i++) { 247 linecard_type = &linecard->types[i]; 248 if (!strcmp(type, linecard_type->type)) 249 return linecard_type; 250 } 251 return NULL; 252 } 253 254 static int devlink_linecard_type_set(struct devlink_linecard *linecard, 255 const char *type, 256 struct netlink_ext_ack *extack) 257 { 258 const struct devlink_linecard_ops *ops = linecard->ops; 259 struct devlink_linecard_type *linecard_type; 260 int err; 261 262 mutex_lock(&linecard->state_lock); 263 if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) { 264 NL_SET_ERR_MSG(extack, "Line card is currently being provisioned"); 265 err = -EBUSY; 266 goto out; 267 } 268 if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) { 269 NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned"); 270 err = -EBUSY; 271 goto out; 272 } 273 274 linecard_type = devlink_linecard_type_lookup(linecard, type); 275 if (!linecard_type) { 276 NL_SET_ERR_MSG(extack, "Unsupported line card type provided"); 277 err = -EINVAL; 278 goto out; 279 } 280 281 if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED && 282 linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) { 283 NL_SET_ERR_MSG(extack, "Line card already provisioned"); 284 err = -EBUSY; 285 /* Check if the line card is provisioned in the same 286 * way the user asks. In case it is, make the operation 287 * to return success. 288 */ 289 if (ops->same_provision && 290 ops->same_provision(linecard, linecard->priv, 291 linecard_type->type, 292 linecard_type->priv)) 293 err = 0; 294 goto out; 295 } 296 297 linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING; 298 linecard->type = linecard_type->type; 299 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 300 mutex_unlock(&linecard->state_lock); 301 err = ops->provision(linecard, linecard->priv, linecard_type->type, 302 linecard_type->priv, extack); 303 if (err) { 304 /* Provisioning failed. Assume the linecard is unprovisioned 305 * for future operations. 306 */ 307 mutex_lock(&linecard->state_lock); 308 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 309 linecard->type = NULL; 310 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 311 mutex_unlock(&linecard->state_lock); 312 } 313 return err; 314 315 out: 316 mutex_unlock(&linecard->state_lock); 317 return err; 318 } 319 320 static int devlink_linecard_type_unset(struct devlink_linecard *linecard, 321 struct netlink_ext_ack *extack) 322 { 323 int err; 324 325 mutex_lock(&linecard->state_lock); 326 if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) { 327 NL_SET_ERR_MSG(extack, "Line card is currently being provisioned"); 328 err = -EBUSY; 329 goto out; 330 } 331 if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) { 332 NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned"); 333 err = -EBUSY; 334 goto out; 335 } 336 if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) { 337 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 338 linecard->type = NULL; 339 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 340 err = 0; 341 goto out; 342 } 343 344 if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) { 345 NL_SET_ERR_MSG(extack, "Line card is not provisioned"); 346 err = 0; 347 goto out; 348 } 349 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING; 350 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 351 mutex_unlock(&linecard->state_lock); 352 err = linecard->ops->unprovision(linecard, linecard->priv, 353 extack); 354 if (err) { 355 /* Unprovisioning failed. Assume the linecard is unprovisioned 356 * for future operations. 357 */ 358 mutex_lock(&linecard->state_lock); 359 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 360 linecard->type = NULL; 361 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 362 mutex_unlock(&linecard->state_lock); 363 } 364 return err; 365 366 out: 367 mutex_unlock(&linecard->state_lock); 368 return err; 369 } 370 371 int devlink_nl_linecard_set_doit(struct sk_buff *skb, struct genl_info *info) 372 { 373 struct netlink_ext_ack *extack = info->extack; 374 struct devlink *devlink = info->user_ptr[0]; 375 struct devlink_linecard *linecard; 376 int err; 377 378 linecard = devlink_linecard_get_from_info(devlink, info); 379 if (IS_ERR(linecard)) 380 return PTR_ERR(linecard); 381 382 if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) { 383 const char *type; 384 385 type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]); 386 if (strcmp(type, "")) { 387 err = devlink_linecard_type_set(linecard, type, extack); 388 if (err) 389 return err; 390 } else { 391 err = devlink_linecard_type_unset(linecard, extack); 392 if (err) 393 return err; 394 } 395 } 396 397 return 0; 398 } 399 400 static int devlink_linecard_types_init(struct devlink_linecard *linecard) 401 { 402 struct devlink_linecard_type *linecard_type; 403 unsigned int count; 404 int i; 405 406 count = linecard->ops->types_count(linecard, linecard->priv); 407 linecard->types = kmalloc_array(count, sizeof(*linecard_type), 408 GFP_KERNEL); 409 if (!linecard->types) 410 return -ENOMEM; 411 linecard->types_count = count; 412 413 for (i = 0; i < count; i++) { 414 linecard_type = &linecard->types[i]; 415 linecard->ops->types_get(linecard, linecard->priv, i, 416 &linecard_type->type, 417 &linecard_type->priv); 418 } 419 return 0; 420 } 421 422 static void devlink_linecard_types_fini(struct devlink_linecard *linecard) 423 { 424 kfree(linecard->types); 425 } 426 427 /** 428 * devl_linecard_create - Create devlink linecard 429 * 430 * @devlink: devlink 431 * @linecard_index: driver-specific numerical identifier of the linecard 432 * @ops: linecards ops 433 * @priv: user priv pointer 434 * 435 * Create devlink linecard instance with provided linecard index. 436 * Caller can use any indexing, even hw-related one. 437 * 438 * Return: Line card structure or an ERR_PTR() encoded error code. 439 */ 440 struct devlink_linecard * 441 devl_linecard_create(struct devlink *devlink, unsigned int linecard_index, 442 const struct devlink_linecard_ops *ops, void *priv) 443 { 444 struct devlink_linecard *linecard; 445 int err; 446 447 if (WARN_ON(!ops || !ops->provision || !ops->unprovision || 448 !ops->types_count || !ops->types_get)) 449 return ERR_PTR(-EINVAL); 450 451 if (devlink_linecard_index_exists(devlink, linecard_index)) 452 return ERR_PTR(-EEXIST); 453 454 linecard = kzalloc(sizeof(*linecard), GFP_KERNEL); 455 if (!linecard) 456 return ERR_PTR(-ENOMEM); 457 458 linecard->devlink = devlink; 459 linecard->index = linecard_index; 460 linecard->ops = ops; 461 linecard->priv = priv; 462 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 463 mutex_init(&linecard->state_lock); 464 465 err = devlink_linecard_types_init(linecard); 466 if (err) { 467 mutex_destroy(&linecard->state_lock); 468 kfree(linecard); 469 return ERR_PTR(err); 470 } 471 472 list_add_tail(&linecard->list, &devlink->linecard_list); 473 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 474 return linecard; 475 } 476 EXPORT_SYMBOL_GPL(devl_linecard_create); 477 478 /** 479 * devl_linecard_destroy - Destroy devlink linecard 480 * 481 * @linecard: devlink linecard 482 */ 483 void devl_linecard_destroy(struct devlink_linecard *linecard) 484 { 485 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL); 486 list_del(&linecard->list); 487 devlink_linecard_types_fini(linecard); 488 mutex_destroy(&linecard->state_lock); 489 kfree(linecard); 490 } 491 EXPORT_SYMBOL_GPL(devl_linecard_destroy); 492 493 /** 494 * devlink_linecard_provision_set - Set provisioning on linecard 495 * 496 * @linecard: devlink linecard 497 * @type: linecard type 498 * 499 * This is either called directly from the provision() op call or 500 * as a result of the provision() op call asynchronously. 501 */ 502 void devlink_linecard_provision_set(struct devlink_linecard *linecard, 503 const char *type) 504 { 505 mutex_lock(&linecard->state_lock); 506 WARN_ON(linecard->type && strcmp(linecard->type, type)); 507 linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED; 508 linecard->type = type; 509 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 510 mutex_unlock(&linecard->state_lock); 511 } 512 EXPORT_SYMBOL_GPL(devlink_linecard_provision_set); 513 514 /** 515 * devlink_linecard_provision_clear - Clear provisioning on linecard 516 * 517 * @linecard: devlink linecard 518 * 519 * This is either called directly from the unprovision() op call or 520 * as a result of the unprovision() op call asynchronously. 521 */ 522 void devlink_linecard_provision_clear(struct devlink_linecard *linecard) 523 { 524 mutex_lock(&linecard->state_lock); 525 linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 526 linecard->type = NULL; 527 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 528 mutex_unlock(&linecard->state_lock); 529 } 530 EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear); 531 532 /** 533 * devlink_linecard_provision_fail - Fail provisioning on linecard 534 * 535 * @linecard: devlink linecard 536 * 537 * This is either called directly from the provision() op call or 538 * as a result of the provision() op call asynchronously. 539 */ 540 void devlink_linecard_provision_fail(struct devlink_linecard *linecard) 541 { 542 mutex_lock(&linecard->state_lock); 543 linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED; 544 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 545 mutex_unlock(&linecard->state_lock); 546 } 547 EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail); 548 549 /** 550 * devlink_linecard_activate - Set linecard active 551 * 552 * @linecard: devlink linecard 553 */ 554 void devlink_linecard_activate(struct devlink_linecard *linecard) 555 { 556 mutex_lock(&linecard->state_lock); 557 WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED); 558 linecard->state = DEVLINK_LINECARD_STATE_ACTIVE; 559 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 560 mutex_unlock(&linecard->state_lock); 561 } 562 EXPORT_SYMBOL_GPL(devlink_linecard_activate); 563 564 /** 565 * devlink_linecard_deactivate - Set linecard inactive 566 * 567 * @linecard: devlink linecard 568 */ 569 void devlink_linecard_deactivate(struct devlink_linecard *linecard) 570 { 571 mutex_lock(&linecard->state_lock); 572 switch (linecard->state) { 573 case DEVLINK_LINECARD_STATE_ACTIVE: 574 linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED; 575 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 576 break; 577 case DEVLINK_LINECARD_STATE_UNPROVISIONING: 578 /* Line card is being deactivated as part 579 * of unprovisioning flow. 580 */ 581 break; 582 default: 583 WARN_ON(1); 584 break; 585 } 586 mutex_unlock(&linecard->state_lock); 587 } 588 EXPORT_SYMBOL_GPL(devlink_linecard_deactivate); 589 590 static void devlink_linecard_rel_notify_cb(struct devlink *devlink, 591 u32 linecard_index) 592 { 593 struct devlink_linecard *linecard; 594 595 linecard = devlink_linecard_get_by_index(devlink, linecard_index); 596 if (!linecard) 597 return; 598 devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 599 } 600 601 static void devlink_linecard_rel_cleanup_cb(struct devlink *devlink, 602 u32 linecard_index, u32 rel_index) 603 { 604 struct devlink_linecard *linecard; 605 606 linecard = devlink_linecard_get_by_index(devlink, linecard_index); 607 if (linecard && linecard->rel_index == rel_index) 608 linecard->rel_index = 0; 609 } 610 611 /** 612 * devlink_linecard_nested_dl_set - Attach/detach nested devlink 613 * instance to linecard. 614 * 615 * @linecard: devlink linecard 616 * @nested_devlink: devlink instance to attach or NULL to detach 617 */ 618 int devlink_linecard_nested_dl_set(struct devlink_linecard *linecard, 619 struct devlink *nested_devlink) 620 { 621 return devlink_rel_nested_in_add(&linecard->rel_index, 622 linecard->devlink->index, 623 linecard->index, 624 devlink_linecard_rel_notify_cb, 625 devlink_linecard_rel_cleanup_cb, 626 nested_devlink); 627 } 628 EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set); 629
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.