aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/of/dynamic.c
diff options
context:
space:
mode:
authorPantelis Antoniou <pantelis.antoniou@konsulko.com>2014-07-04 12:58:49 -0400
committerGrant Likely <grant.likely@linaro.org>2014-07-23 19:29:15 -0400
commit201c910bd6898d81d4ac6685d0f421b7e10f3c5d (patch)
treeaec8c406908e71c7ad89750c6e9f4f8d0c094747 /drivers/of/dynamic.c
parent259092a35c7e11f1d4616b0f5b3ba7b851fe4fa6 (diff)
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>
Diffstat (limited to 'drivers/of/dynamic.c')
-rw-r--r--drivers/of/dynamic.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 7bd5501736a6..c1002b7be786 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -314,3 +314,347 @@ struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags)
314 kfree(node); 314 kfree(node);
315 return NULL; 315 return NULL;
316} 316}
317
318static 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
326static 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
355static inline void __of_changeset_entry_dump(struct of_changeset_entry *ce)
356{
357 /* empty */
358}
359#endif
360
361static 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
386static 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
416static 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
508static 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 */
523void 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 */
537void 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 */
557int 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 */
596int 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 */
639int 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}