Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

of: Transactional DT support.

Introducing DT transactional support.

A DT transaction is a method which allows one to apply changes
in the live tree, in such a way that either the full set of changes
take effect, or the state of the tree can be rolled-back to the
state it was before it was attempted. An applied transaction
can be rolled-back at any time.

Documentation is in
Documentation/devicetree/changesets.txt

Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
[glikely: Removed device notifiers and reworked to be more consistent]
Signed-off-by: Grant Likely <grant.likely@linaro.org>

authored by

Pantelis Antoniou and committed by
Grant Likely
201c910b 259092a3

+530
+40
Documentation/devicetree/changesets.txt
··· 1 + A DT changeset is a method which allows one to apply changes 2 + in the live tree in such a way that either the full set of changes 3 + will be applied, or none of them will be. If an error occurs partway 4 + through applying the changeset, then the tree will be rolled back to the 5 + previous state. A changeset can also be removed after it has been 6 + applied. 7 + 8 + When a changeset is applied, all of the changes get applied to the tree 9 + at once before emitting OF_RECONFIG notifiers. This is so that the 10 + receiver sees a complete and consistent state of the tree when it 11 + receives the notifier. 12 + 13 + The sequence of a changeset is as follows. 14 + 15 + 1. of_changeset_init() - initializes a changeset 16 + 17 + 2. A number of DT tree change calls, of_changeset_attach_node(), 18 + of_changeset_detach_node(), of_changeset_add_property(), 19 + of_changeset_remove_property, of_changeset_update_property() to prepare 20 + a set of changes. No changes to the active tree are made at this point. 21 + All the change operations are recorded in the of_changeset 'entries' 22 + list. 23 + 24 + 3. mutex_lock(of_mutex) - starts a changeset; The global of_mutex 25 + ensures there can only be one editor at a time. 26 + 27 + 4. of_changeset_apply() - Apply the changes to the tree. Either the 28 + entire changeset will get applied, or if there is an error the tree will 29 + be restored to the previous state 30 + 31 + 5. mutex_unlock(of_mutex) - All operations complete, release the mutex 32 + 33 + If a successfully applied changeset needs to be removed, it can be done 34 + with the following sequence. 35 + 36 + 1. mutex_lock(of_mutex) 37 + 38 + 2. of_changeset_revert() 39 + 40 + 3. mutex_unlock(of_mutex)
+344
drivers/of/dynamic.c
··· 314 314 kfree(node); 315 315 return NULL; 316 316 } 317 + 318 + static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) 319 + { 320 + of_node_put(ce->np); 321 + list_del(&ce->node); 322 + kfree(ce); 323 + } 324 + 325 + #ifdef DEBUG 326 + static void __of_changeset_entry_dump(struct of_changeset_entry *ce) 327 + { 328 + switch (ce->action) { 329 + case OF_RECONFIG_ADD_PROPERTY: 330 + pr_debug("%p: %s %s/%s\n", 331 + ce, "ADD_PROPERTY ", ce->np->full_name, 332 + ce->prop->name); 333 + break; 334 + case OF_RECONFIG_REMOVE_PROPERTY: 335 + pr_debug("%p: %s %s/%s\n", 336 + ce, "REMOVE_PROPERTY", ce->np->full_name, 337 + ce->prop->name); 338 + break; 339 + case OF_RECONFIG_UPDATE_PROPERTY: 340 + pr_debug("%p: %s %s/%s\n", 341 + ce, "UPDATE_PROPERTY", ce->np->full_name, 342 + ce->prop->name); 343 + break; 344 + case OF_RECONFIG_ATTACH_NODE: 345 + pr_debug("%p: %s %s\n", 346 + ce, "ATTACH_NODE ", ce->np->full_name); 347 + break; 348 + case OF_RECONFIG_DETACH_NODE: 349 + pr_debug("%p: %s %s\n", 350 + ce, "DETACH_NODE ", ce->np->full_name); 351 + break; 352 + } 353 + } 354 + #else 355 + static inline void __of_changeset_entry_dump(struct of_changeset_entry *ce) 356 + { 357 + /* empty */ 358 + } 359 + #endif 360 + 361 + static void __of_changeset_entry_invert(struct of_changeset_entry *ce, 362 + struct of_changeset_entry *rce) 363 + { 364 + memcpy(rce, ce, sizeof(*rce)); 365 + 366 + switch (ce->action) { 367 + case OF_RECONFIG_ATTACH_NODE: 368 + rce->action = OF_RECONFIG_DETACH_NODE; 369 + break; 370 + case OF_RECONFIG_DETACH_NODE: 371 + rce->action = OF_RECONFIG_ATTACH_NODE; 372 + break; 373 + case OF_RECONFIG_ADD_PROPERTY: 374 + rce->action = OF_RECONFIG_REMOVE_PROPERTY; 375 + break; 376 + case OF_RECONFIG_REMOVE_PROPERTY: 377 + rce->action = OF_RECONFIG_ADD_PROPERTY; 378 + break; 379 + case OF_RECONFIG_UPDATE_PROPERTY: 380 + rce->old_prop = ce->prop; 381 + rce->prop = ce->old_prop; 382 + break; 383 + } 384 + } 385 + 386 + static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert) 387 + { 388 + struct of_changeset_entry ce_inverted; 389 + int ret; 390 + 391 + if (revert) { 392 + __of_changeset_entry_invert(ce, &ce_inverted); 393 + ce = &ce_inverted; 394 + } 395 + 396 + switch (ce->action) { 397 + case OF_RECONFIG_ATTACH_NODE: 398 + case OF_RECONFIG_DETACH_NODE: 399 + ret = of_reconfig_notify(ce->action, ce->np); 400 + break; 401 + case OF_RECONFIG_ADD_PROPERTY: 402 + case OF_RECONFIG_REMOVE_PROPERTY: 403 + case OF_RECONFIG_UPDATE_PROPERTY: 404 + ret = of_property_notify(ce->action, ce->np, ce->prop, ce->old_prop); 405 + break; 406 + default: 407 + pr_err("%s: invalid devicetree changeset action: %i\n", __func__, 408 + (int)ce->action); 409 + return; 410 + } 411 + 412 + if (ret) 413 + pr_err("%s: notifier error @%s\n", __func__, ce->np->full_name); 414 + } 415 + 416 + static int __of_changeset_entry_apply(struct of_changeset_entry *ce) 417 + { 418 + struct property *old_prop, **propp; 419 + unsigned long flags; 420 + int ret = 0; 421 + 422 + __of_changeset_entry_dump(ce); 423 + 424 + raw_spin_lock_irqsave(&devtree_lock, flags); 425 + switch (ce->action) { 426 + case OF_RECONFIG_ATTACH_NODE: 427 + __of_attach_node(ce->np); 428 + break; 429 + case OF_RECONFIG_DETACH_NODE: 430 + __of_detach_node(ce->np); 431 + break; 432 + case OF_RECONFIG_ADD_PROPERTY: 433 + /* If the property is in deadprops then it must be removed */ 434 + for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { 435 + if (*propp == ce->prop) { 436 + *propp = ce->prop->next; 437 + ce->prop->next = NULL; 438 + break; 439 + } 440 + } 441 + 442 + ret = __of_add_property(ce->np, ce->prop); 443 + if (ret) { 444 + pr_err("%s: add_property failed @%s/%s\n", 445 + __func__, ce->np->full_name, 446 + ce->prop->name); 447 + break; 448 + } 449 + break; 450 + case OF_RECONFIG_REMOVE_PROPERTY: 451 + ret = __of_remove_property(ce->np, ce->prop); 452 + if (ret) { 453 + pr_err("%s: remove_property failed @%s/%s\n", 454 + __func__, ce->np->full_name, 455 + ce->prop->name); 456 + break; 457 + } 458 + break; 459 + 460 + case OF_RECONFIG_UPDATE_PROPERTY: 461 + /* If the property is in deadprops then it must be removed */ 462 + for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { 463 + if (*propp == ce->prop) { 464 + *propp = ce->prop->next; 465 + ce->prop->next = NULL; 466 + break; 467 + } 468 + } 469 + 470 + ret = __of_update_property(ce->np, ce->prop, &old_prop); 471 + if (ret) { 472 + pr_err("%s: update_property failed @%s/%s\n", 473 + __func__, ce->np->full_name, 474 + ce->prop->name); 475 + break; 476 + } 477 + break; 478 + default: 479 + ret = -EINVAL; 480 + } 481 + raw_spin_unlock_irqrestore(&devtree_lock, flags); 482 + 483 + if (ret) 484 + return ret; 485 + 486 + switch (ce->action) { 487 + case OF_RECONFIG_ATTACH_NODE: 488 + __of_attach_node_sysfs(ce->np); 489 + break; 490 + case OF_RECONFIG_DETACH_NODE: 491 + __of_detach_node_sysfs(ce->np); 492 + break; 493 + case OF_RECONFIG_ADD_PROPERTY: 494 + /* ignore duplicate names */ 495 + __of_add_property_sysfs(ce->np, ce->prop); 496 + break; 497 + case OF_RECONFIG_REMOVE_PROPERTY: 498 + __of_remove_property_sysfs(ce->np, ce->prop); 499 + break; 500 + case OF_RECONFIG_UPDATE_PROPERTY: 501 + __of_update_property_sysfs(ce->np, ce->prop, ce->old_prop); 502 + break; 503 + } 504 + 505 + return 0; 506 + } 507 + 508 + static inline int __of_changeset_entry_revert(struct of_changeset_entry *ce) 509 + { 510 + struct of_changeset_entry ce_inverted; 511 + 512 + __of_changeset_entry_invert(ce, &ce_inverted); 513 + return __of_changeset_entry_apply(&ce_inverted); 514 + } 515 + 516 + /** 517 + * of_changeset_init - Initialize a changeset for use 518 + * 519 + * @ocs: changeset pointer 520 + * 521 + * Initialize a changeset structure 522 + */ 523 + void of_changeset_init(struct of_changeset *ocs) 524 + { 525 + memset(ocs, 0, sizeof(*ocs)); 526 + INIT_LIST_HEAD(&ocs->entries); 527 + } 528 + 529 + /** 530 + * of_changeset_destroy - Destroy a changeset 531 + * 532 + * @ocs: changeset pointer 533 + * 534 + * Destroys a changeset. Note that if a changeset is applied, 535 + * its changes to the tree cannot be reverted. 536 + */ 537 + void of_changeset_destroy(struct of_changeset *ocs) 538 + { 539 + struct of_changeset_entry *ce, *cen; 540 + 541 + list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node) 542 + __of_changeset_entry_destroy(ce); 543 + } 544 + 545 + /** 546 + * of_changeset_apply - Applies a changeset 547 + * 548 + * @ocs: changeset pointer 549 + * 550 + * Applies a changeset to the live tree. 551 + * Any side-effects of live tree state changes are applied here on 552 + * sucess, like creation/destruction of devices and side-effects 553 + * like creation of sysfs properties and directories. 554 + * Returns 0 on success, a negative error value in case of an error. 555 + * On error the partially applied effects are reverted. 556 + */ 557 + int of_changeset_apply(struct of_changeset *ocs) 558 + { 559 + struct of_changeset_entry *ce; 560 + int ret; 561 + 562 + /* perform the rest of the work */ 563 + pr_debug("of_changeset: applying...\n"); 564 + list_for_each_entry(ce, &ocs->entries, node) { 565 + ret = __of_changeset_entry_apply(ce); 566 + if (ret) { 567 + pr_err("%s: Error applying changeset (%d)\n", __func__, ret); 568 + list_for_each_entry_continue_reverse(ce, &ocs->entries, node) 569 + __of_changeset_entry_revert(ce); 570 + return ret; 571 + } 572 + } 573 + pr_debug("of_changeset: applied, emitting notifiers.\n"); 574 + 575 + /* drop the global lock while emitting notifiers */ 576 + mutex_unlock(&of_mutex); 577 + list_for_each_entry(ce, &ocs->entries, node) 578 + __of_changeset_entry_notify(ce, 0); 579 + mutex_lock(&of_mutex); 580 + pr_debug("of_changeset: notifiers sent.\n"); 581 + 582 + return 0; 583 + } 584 + 585 + /** 586 + * of_changeset_revert - Reverts an applied changeset 587 + * 588 + * @ocs: changeset pointer 589 + * 590 + * Reverts a changeset returning the state of the tree to what it 591 + * was before the application. 592 + * Any side-effects like creation/destruction of devices and 593 + * removal of sysfs properties and directories are applied. 594 + * Returns 0 on success, a negative error value in case of an error. 595 + */ 596 + int of_changeset_revert(struct of_changeset *ocs) 597 + { 598 + struct of_changeset_entry *ce; 599 + int ret; 600 + 601 + pr_debug("of_changeset: reverting...\n"); 602 + list_for_each_entry_reverse(ce, &ocs->entries, node) { 603 + ret = __of_changeset_entry_revert(ce); 604 + if (ret) { 605 + pr_err("%s: Error reverting changeset (%d)\n", __func__, ret); 606 + list_for_each_entry_continue(ce, &ocs->entries, node) 607 + __of_changeset_entry_apply(ce); 608 + return ret; 609 + } 610 + } 611 + pr_debug("of_changeset: reverted, emitting notifiers.\n"); 612 + 613 + /* drop the global lock while emitting notifiers */ 614 + mutex_unlock(&of_mutex); 615 + list_for_each_entry_reverse(ce, &ocs->entries, node) 616 + __of_changeset_entry_notify(ce, 1); 617 + mutex_lock(&of_mutex); 618 + pr_debug("of_changeset: notifiers sent.\n"); 619 + 620 + return 0; 621 + } 622 + 623 + /** 624 + * of_changeset_action - Perform a changeset action 625 + * 626 + * @ocs: changeset pointer 627 + * @action: action to perform 628 + * @np: Pointer to device node 629 + * @prop: Pointer to property 630 + * 631 + * On action being one of: 632 + * + OF_RECONFIG_ATTACH_NODE 633 + * + OF_RECONFIG_DETACH_NODE, 634 + * + OF_RECONFIG_ADD_PROPERTY 635 + * + OF_RECONFIG_REMOVE_PROPERTY, 636 + * + OF_RECONFIG_UPDATE_PROPERTY 637 + * Returns 0 on success, a negative error value in case of an error. 638 + */ 639 + int of_changeset_action(struct of_changeset *ocs, unsigned long action, 640 + struct device_node *np, struct property *prop) 641 + { 642 + struct of_changeset_entry *ce; 643 + 644 + ce = kzalloc(sizeof(*ce), GFP_KERNEL); 645 + if (!ce) { 646 + pr_err("%s: Failed to allocate\n", __func__); 647 + return -ENOMEM; 648 + } 649 + /* get a reference to the node */ 650 + ce->action = action; 651 + ce->np = of_node_get(np); 652 + ce->prop = prop; 653 + 654 + if (action == OF_RECONFIG_UPDATE_PROPERTY && prop) 655 + ce->old_prop = of_find_property(np, prop->name, NULL); 656 + 657 + /* add it to the list */ 658 + list_add_tail(&ce->node, &ocs->entries); 659 + return 0; 660 + }
+9
drivers/of/of_private.h
··· 81 81 extern void __of_detach_node(struct device_node *np); 82 82 extern void __of_detach_node_sysfs(struct device_node *np); 83 83 84 + /* iterators for transactions, used for overlays */ 85 + /* forward iterator */ 86 + #define for_each_transaction_entry(_oft, _te) \ 87 + list_for_each_entry(_te, &(_oft)->te_list, node) 88 + 89 + /* reverse iterator */ 90 + #define for_each_transaction_entry_reverse(_oft, _te) \ 91 + list_for_each_entry_reverse(_te, &(_oft)->te_list, node) 92 + 84 93 #endif /* _LINUX_OF_PRIVATE_H */
+51
drivers/of/selftest.c
··· 293 293 #endif 294 294 } 295 295 296 + static void __init of_selftest_changeset(void) 297 + { 298 + #ifdef CONFIG_OF_DYNAMIC 299 + struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" }; 300 + struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" }; 301 + struct property *ppremove; 302 + struct device_node *n1, *n2, *n21, *nremove, *parent; 303 + struct of_changeset chgset; 304 + 305 + of_changeset_init(&chgset); 306 + n1 = __of_node_alloc("/testcase-data/changeset/n1", GFP_KERNEL); 307 + selftest(n1, "testcase setup failure\n"); 308 + n2 = __of_node_alloc("/testcase-data/changeset/n2", GFP_KERNEL); 309 + selftest(n2, "testcase setup failure\n"); 310 + n21 = __of_node_alloc("/testcase-data/changeset/n2/n21", GFP_KERNEL); 311 + selftest(n21, "testcase setup failure %p\n", n21); 312 + nremove = of_find_node_by_path("/testcase-data/changeset/node-remove"); 313 + selftest(nremove, "testcase setup failure\n"); 314 + ppadd = __of_prop_dup(&padd, GFP_KERNEL); 315 + selftest(ppadd, "testcase setup failure\n"); 316 + ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL); 317 + selftest(ppupdate, "testcase setup failure\n"); 318 + parent = nremove->parent; 319 + n1->parent = parent; 320 + n2->parent = parent; 321 + n21->parent = n2; 322 + n2->child = n21; 323 + ppremove = of_find_property(parent, "prop-remove", NULL); 324 + selftest(ppremove, "failed to find removal prop"); 325 + 326 + of_changeset_init(&chgset); 327 + selftest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n"); 328 + selftest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n"); 329 + selftest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n"); 330 + selftest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n"); 331 + selftest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n"); 332 + selftest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n"); 333 + selftest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n"); 334 + mutex_lock(&of_mutex); 335 + selftest(!of_changeset_apply(&chgset), "apply failed\n"); 336 + mutex_unlock(&of_mutex); 337 + 338 + mutex_lock(&of_mutex); 339 + selftest(!of_changeset_revert(&chgset), "revert failed\n"); 340 + mutex_unlock(&of_mutex); 341 + 342 + of_changeset_destroy(&chgset); 343 + #endif 344 + } 345 + 296 346 static void __init of_selftest_parse_interrupts(void) 297 347 { 298 348 struct device_node *np; ··· 611 561 of_selftest_parse_phandle_with_args(); 612 562 of_selftest_property_match_string(); 613 563 of_selftest_property_copy(); 564 + of_selftest_changeset(); 614 565 of_selftest_parse_interrupts(); 615 566 of_selftest_parse_interrupts_extended(); 616 567 of_selftest_match_node();
+10
drivers/of/testcase-data/testcases.dtsi
··· 1 + / { 2 + testcase-data { 3 + changeset { 4 + prop-update = "hello"; 5 + prop-remove = "world"; 6 + node-remove { 7 + }; 8 + }; 9 + }; 10 + }; 1 11 #include "tests-phandle.dtsi" 2 12 #include "tests-interrupts.dtsi" 3 13 #include "tests-match.dtsi"
+76
include/linux/of.h
··· 786 786 #define OF_DECLARE_2(table, name, compat, fn) \ 787 787 _OF_DECLARE(table, name, compat, fn, of_init_fn_2) 788 788 789 + /** 790 + * struct of_changeset_entry - Holds a changeset entry 791 + * 792 + * @node: list_head for the log list 793 + * @action: notifier action 794 + * @np: pointer to the device node affected 795 + * @prop: pointer to the property affected 796 + * @old_prop: hold a pointer to the original property 797 + * 798 + * Every modification of the device tree during a changeset 799 + * is held in a list of of_changeset_entry structures. 800 + * That way we can recover from a partial application, or we can 801 + * revert the changeset 802 + */ 803 + struct of_changeset_entry { 804 + struct list_head node; 805 + unsigned long action; 806 + struct device_node *np; 807 + struct property *prop; 808 + struct property *old_prop; 809 + }; 810 + 811 + /** 812 + * struct of_changeset - changeset tracker structure 813 + * 814 + * @entries: list_head for the changeset entries 815 + * 816 + * changesets are a convenient way to apply bulk changes to the 817 + * live tree. In case of an error, changes are rolled-back. 818 + * changesets live on after initial application, and if not 819 + * destroyed after use, they can be reverted in one single call. 820 + */ 821 + struct of_changeset { 822 + struct list_head entries; 823 + }; 824 + 825 + #ifdef CONFIG_OF_DYNAMIC 826 + extern void of_changeset_init(struct of_changeset *ocs); 827 + extern void of_changeset_destroy(struct of_changeset *ocs); 828 + extern int of_changeset_apply(struct of_changeset *ocs); 829 + extern int of_changeset_revert(struct of_changeset *ocs); 830 + extern int of_changeset_action(struct of_changeset *ocs, 831 + unsigned long action, struct device_node *np, 832 + struct property *prop); 833 + 834 + static inline int of_changeset_attach_node(struct of_changeset *ocs, 835 + struct device_node *np) 836 + { 837 + return of_changeset_action(ocs, OF_RECONFIG_ATTACH_NODE, np, NULL); 838 + } 839 + 840 + static inline int of_changeset_detach_node(struct of_changeset *ocs, 841 + struct device_node *np) 842 + { 843 + return of_changeset_action(ocs, OF_RECONFIG_DETACH_NODE, np, NULL); 844 + } 845 + 846 + static inline int of_changeset_add_property(struct of_changeset *ocs, 847 + struct device_node *np, struct property *prop) 848 + { 849 + return of_changeset_action(ocs, OF_RECONFIG_ADD_PROPERTY, np, prop); 850 + } 851 + 852 + static inline int of_changeset_remove_property(struct of_changeset *ocs, 853 + struct device_node *np, struct property *prop) 854 + { 855 + return of_changeset_action(ocs, OF_RECONFIG_REMOVE_PROPERTY, np, prop); 856 + } 857 + 858 + static inline int of_changeset_update_property(struct of_changeset *ocs, 859 + struct device_node *np, struct property *prop) 860 + { 861 + return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop); 862 + } 863 + #endif 864 + 789 865 #endif /* _LINUX_OF_H */