On Mon, Oct 15, 2018 at 7:28 PM <frowand.list@xxxxxxxxx> wrote: Hi Frank, Thanks for all your work on this! > From: Frank Rowand <frank.rowand@xxxxxxxx> > > When an overlay is applied or removed, the live devicetree visible in > /proc/device-tree/, aka /sys/firmware/devicetree/base/, reflects the > changes. There is no method for user space to determine whether the > live devicetree was modified by overlay actions. > > Provide a sysfs file, /sys/firmware/devicetree/tree_version, to allow > user space to determine if the live devicetree has remained unchanged > while a series of one or more accesses of /proc/device-tree/ occur. > > The use of both dynamic devicetree modifications and overlay apply and > removal are not supported during the same boot cycle. Thus non-overlay > dynamic modifications are not reflected in the value of tree_version. > > Example shell use: > > done=1 > Suggest a few comments, such as: + # Keep trying until we can make it through the loop without live tree being changed + # by an overlay during the 'critical region' > while [ $done = 1 ] ; do Nit: doesn't $done mean 'not done' in this script (assuming True is 1)? > > pre_version=$(cat /sys/firmware/devicetree/tree_version) Suggest: + # loop while live tree is locked for overlay changes > while [ $(( ${pre_version} & 1 )) != 0 ] ; do > # echo is optional, sleep value can be tuned > echo "${pre_version} locked, sleeping 2 seconds" > sleep 2; > pre_version=$(cat /sys/firmware/devicetree/tree_version) > version=${pre_version} Unused 'version' variable > done > > # 'critical region' > # access /proc/device-tree/ one or more times > > post_version=$(cat /sys/firmware/devicetree/tree_version) > + # Final check that DT wasn't possibly overlaid during the critical region > if [ ${post_version} = ${pre_version} ] ; then > done=0 This threw me off for a moment as I was figuring at first that setting done = 0 meant that we weren't done, so keep looping. > fi > > done > > Signed-off-by: Frank Rowand <frank.rowand@xxxxxxxx> > --- > > updates since v1: > - removed unneeded variable "version" from shell script > - explicitly state that an odd value of tree_version means the > devicetree is locked for an overlay update not just in the > source, but also in Documentation/ABI/testing/sysfs-firmware-ofw > - document that tree_version is reported as an ascii number, the > internal representation, and the resultant wrap around behavior > > Documentation/ABI/testing/sysfs-firmware-ofw | 67 +++++++++++++++++++++++++--- > drivers/of/base.c | 66 +++++++++++++++++++++++++++ > drivers/of/of_private.h | 2 + > drivers/of/overlay.c | 12 +++++ > 4 files changed, 140 insertions(+), 7 deletions(-) > > diff --git a/Documentation/ABI/testing/sysfs-firmware-ofw b/Documentation/ABI/testing/sysfs-firmware-ofw > index edcab3ccfcc0..d2346eb61924 100644 > --- a/Documentation/ABI/testing/sysfs-firmware-ofw > +++ b/Documentation/ABI/testing/sysfs-firmware-ofw > @@ -1,4 +1,10 @@ > -What: /sys/firmware/devicetree/* > +What: /sys/firmware/devicetree/ > +Date: November 2013 > +Contact: Frank Rowand <frowand.list@xxxxxxxxx>, devicetree@xxxxxxxxxxxxxxx > +Description: > + Top level Open Firmware, aka devicetree, sysfs directory. > + > +What: /sys/firmware/devicetree/base > Date: November 2013 > Contact: Grant Likely <grant.likely@xxxxxxx>, devicetree@xxxxxxxxxxxxxxx > Description: > @@ -6,11 +12,6 @@ Description: > hardware, the device tree structure will be exposed in this > directory. > > - It is possible for multiple device-tree directories to exist. > - Some device drivers use a separate detached device tree which > - have no attachment to the system tree and will appear in a > - different subdirectory under /sys/firmware/devicetree. > - > Userspace must not use the /sys/firmware/devicetree/base > path directly, but instead should follow /proc/device-tree > symlink. It is possible that the absolute path will change > @@ -20,13 +21,65 @@ Description: > filesystem support, and has largely the same semantics and > should be compatible with existing userspace. > > - The contents of /sys/firmware/devicetree/ is a > + The /sys/firmware/devicetree/base directory is the root > + node of the devicetree. > + > + The contents of /sys/firmware/devicetree/base is a > hierarchy of directories, one per device tree node. The > directory name is the resolved path component name (node > name plus address). Properties are represented as files > in the directory. The contents of each file is the exact > binary data from the device tree. > > +What: /sys/firmware/devicetree/tree_version > +Date: October 2018 > +KernelVersion: 4.20 > +Contact: Frank Rowand <frowand.list@xxxxxxxxx>, devicetree@xxxxxxxxxxxxxxx > +Description: > + When an overlay is applied or removed, the live devicetree > + visible in /proc/device-tree/, aka > + /sys/firmware/devicetree/base/, reflects the changes. > + > + tree_version provides a way for user space to determine if the > + live devicetree has remained unchanged while a series of one > + or more accesses of /proc/device-tree/ occur. If tree_version > + is odd then the devicetree is locked for an overlay update. This explains the intention very clearly and directly. I think it would help to give more of an idea of its behavior. I initially had to look at the code to understand what kind of values to expect and whether the tree_version somehow actually reflected some version # that I hadn't known about, hidden in DT somewhere. It would be helpful to have it spelled out what granularity this counter has and some other things about its behavior, somehow explaining that * tree_version is a counter that is incremented and never decremented * tree_version is incremented twice per change * that the granularity is an overlay, tree_version is not counting changeset changes or something else * tree_version is updated (incremented) for both applied and for removed overlays Such as: tree_version is incremented twice for each overlay that is applied or removed (and never decremented). When the tree is locked for processing an overlay, tree_version is incremented once and its value becomes odd. Then when the overlay is done being applied or removed, the tree is unlocked and tree_version is incremented again, becoming an even value. > + > + The contents of tree_version is the ascii representation of > + a kernel unsigned 32 bit int variable, thus when incremented > + from 4294967295 it will wrap around to 0. > + > + The use of both dynamic devicetree modifications and overlay > + apply and removal are not supported during the same boot > + cycle. Thus non-overlay dynamic modifications are not > + reflected in the value of tree_version. > + > + Example shell use of tree_version: > + Same comments as in the header about adding some comments and possibly reconsidering the 'done' variable. > + done=1 > + > + while [ $done = 1 ] ; do > + > + pre_version=$(cat /sys/firmware/devicetree/tree_version) > + while [ $(( ${pre_version} & 1 )) != 0 ] ; do > + # echo is optional, sleep value can be tuned > + echo "${pre_version} tree locked, sleeping 2 seconds" > + sleep 2; > + pre_version=$(cat /sys/firmware/devicetree/tree_version) > + done > + > + # 'critical region' > + # access /proc/device-tree/ one or more times > + > + post_version=$(cat /sys/firmware/devicetree/tree_version) > + > + if [ ${post_version} = ${pre_version} ] ; then > + done=0 > + fi > + > + done > + > + > What: /sys/firmware/fdt > Date: February 2015 > KernelVersion: 3.19 > diff --git a/drivers/of/base.c b/drivers/of/base.c > index 466e3c8582f0..ec0428e47577 100644 > --- a/drivers/of/base.c > +++ b/drivers/of/base.c > @@ -151,9 +151,71 @@ int of_free_phandle_cache(void) > late_initcall_sync(of_free_phandle_cache); > #endif > > +#define show_one(object) \ > + static ssize_t show_##object \ > + (struct kobject *kobj, struct attribute *attr, char *buf) \ > + { \ > + return sprintf(buf, "%u\n", object); \ > + } > + > +struct global_attr { > + struct attribute attr; > + ssize_t (*show)(struct kobject *kobj, > + struct attribute *attr, char *buf); > + ssize_t (*store)(struct kobject *a, struct attribute *b, > + const char *c, size_t count); > +}; > + > +#define define_one_global_ro(_name) \ > +static struct global_attr attr_##_name = \ > +__ATTR(_name, 0444, show_##_name, NULL) > + > +/* > + * tree_version must be unsigned so that at maximum value it wraps > + * from an odd value (4294967295) to an even value (0). > + */ > +static u32 tree_version; > + > +show_one(tree_version); > + > +define_one_global_ro(tree_version); > + > +static struct attribute *of_attributes[] = { > + &attr_tree_version.attr, > + NULL > +}; > + > +static const struct attribute_group of_attr_group = { > + .attrs = of_attributes, > +}; > + > +/* > + * internal documentation > + * tree_version_increment() - increment base version > + * > + * Before starting overlay apply or overlay remove, call this function. > + * After completing overlay apply or overlay remove, call this function. > + * > + * caller must hold of_mutex > + * > + * Userspace can use the value of this variable to determine whether the > + * devicetree has changed while accessing the devicetree. An odd value > + * means a modification is underway. An even value means no modification > + * is underway. > + * > + * The use of both (1) dynamic devicetree modifications and (2) overlay apply > + * and removal are not supported during the same boot cycle. Thus non-overlay > + * dynamic modifications are not reflected in the value of tree_version. > + */ > +void tree_version_increment(void) > +{ > + tree_version++; > +} > + > void __init of_core_init(void) > { > struct device_node *np; > + int ret; > > of_populate_phandle_cache(); > > @@ -172,6 +234,10 @@ void __init of_core_init(void) > /* Symlink in /proc as required by userspace ABI */ > if (of_root) > proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base"); > + > + ret = sysfs_create_group(&of_kset->kobj, &of_attr_group); > + if (ret) > + pr_err("sysfs_create_group of_attr_group failed: %d\n", ret); > } > > static struct property *__of_find_property(const struct device_node *np, > diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h > index 216175d11d3d..adf89f6bad81 100644 > --- a/drivers/of/of_private.h > +++ b/drivers/of/of_private.h > @@ -31,6 +31,8 @@ struct alias_prop { > extern struct list_head aliases_lookup; > extern struct kset *of_kset; > > +void tree_version_increment(void); > + > #if defined(CONFIG_OF_DYNAMIC) > extern int of_property_notify(int action, struct device_node *np, > struct property *prop, struct property *old_prop); > diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c > index eda57ef12fd0..53b1810b1e03 100644 > --- a/drivers/of/overlay.c > +++ b/drivers/of/overlay.c > @@ -770,6 +770,9 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree, > of_overlay_mutex_lock(); > mutex_lock(&of_mutex); > > + /* live tree may change after this point, user space synchronization */ > + tree_version_increment(); > + > ret = of_resolve_phandles(tree); > if (ret) > goto err_free_tree; > @@ -832,6 +835,9 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree, > free_overlay_changeset(ovcs); > > out_unlock: > + /* live tree change complete, user space synchronization */ > + tree_version_increment(); > + > mutex_unlock(&of_mutex); > of_overlay_mutex_unlock(); > > @@ -1028,6 +1034,9 @@ int of_overlay_remove(int *ovcs_id) > > mutex_lock(&of_mutex); > > + /* live tree may change after this point, user space synchronization */ > + tree_version_increment(); > + > ovcs = idr_find(&ovcs_idr, *ovcs_id); > if (!ovcs) { > ret = -ENODEV; > @@ -1083,6 +1092,9 @@ int of_overlay_remove(int *ovcs_id) > free_overlay_changeset(ovcs); > > out_unlock: > + /* live tree change complete, user space synchronization */ > + tree_version_increment(); > + > mutex_unlock(&of_mutex); > > out: > -- Thanks! Alan > Frank Rowand <frank.rowand@xxxxxxxx> >