Forum | Documentation | Website | Blog

Skip to content
Snippets Groups Projects
Commit 3691b0f9 authored by Pantelis Antoniou's avatar Pantelis Antoniou Committed by Robert Nelson
Browse files

of: overlay: add per overlay sysfs attributes


* A per overlay can_remove sysfs attribute that reports whether
the overlay can be removed or not due to another overlapping overlay.

* A target sysfs attribute listing the target of each fragment,
in a group named after the name of the fragment.

Signed-off-by: default avatarPantelis Antoniou <pantelis.antoniou@konsulko.com>
[geert: Setup ovinfo[cnt].info for symbols]
[geert: Spelling s/changset/changeset/]
[geert: Rebase on top of commit 39a751a4

 ("of: change overlay apply input data from unflattened to FDT")
[geert: Use "%pOF" instead of of_node_full_name()]
Signed-off-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
parent b54250e6
Branches
Tags
No related merge requests found
......@@ -25,6 +25,20 @@
#include "of_private.h"
/* fwd. decl */
struct overlay_changeset;
struct fragment;
/* an attribute for each fragment */
struct fragment_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct fragment_attribute *fattr,
char *buf);
ssize_t (*store)(struct kobject *kobj, struct fragment_attribute *fattr,
const char *buf, size_t count);
struct fragment *fragment;
};
/**
* struct target - info about current target node as recursing through overlay
* @np: node where current level of overlay will be applied
......@@ -47,12 +61,18 @@ struct target {
/**
* struct fragment - info about fragment nodes in overlay expanded device tree
* @info: info node that contains the target and overlay
* @target: target of the overlay operation
* @overlay: pointer to the __overlay__ node
*/
struct fragment {
struct device_node *overlay;
struct device_node *target;
struct overlay_changeset *ovcs;
struct device_node *info;
struct attribute_group attr_group;
struct attribute *attrs[2];
struct fragment_attribute target_attr;
};
/**
......@@ -73,6 +93,7 @@ struct overlay_changeset {
struct device_node *overlay_tree;
int count;
struct fragment *fragments;
const struct attribute_group **attr_groups;
bool symbols_fragment;
struct of_changeset cset;
struct kobject kobj;
......@@ -106,6 +127,7 @@ static int devicetree_corrupt(void)
static int build_changeset_next_level(struct overlay_changeset *ovcs,
struct target *target, const struct device_node *overlay_node);
static int overlay_removal_is_ok(struct overlay_changeset *ovcs);
/*
* of_resolve_phandles() finds the largest phandle in the live tree.
......@@ -727,6 +749,16 @@ static struct device_node *find_target(struct device_node *info_node)
return NULL;
}
static ssize_t target_show(struct kobject *kobj,
struct fragment_attribute *fattr, char *buf)
{
struct fragment *fragment = fattr->fragment;
return snprintf(buf, PAGE_SIZE, "%pOF\n", fragment->target);
}
static const struct fragment_attribute target_template_attr = __ATTR_RO(target);
/**
* init_overlay_changeset() - initialize overlay changeset from overlay tree
* @ovcs: Overlay changeset to build
......@@ -746,7 +778,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
struct device_node *node, *overlay_node;
struct fragment *fragment;
struct fragment *fragments;
int cnt, id, ret;
int i, cnt, id, ret;
/*
* Warn for some issues. Can not return -EINVAL for these until
......@@ -811,6 +843,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
goto err_free_fragments;
}
fragment->info = of_node_get(node);
cnt++;
}
......@@ -831,6 +864,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
goto err_free_fragments;
}
fragment->info = of_node_get(node);
cnt++;
}
......@@ -844,6 +878,34 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
ovcs->count = cnt;
ovcs->fragments = fragments;
ovcs->attr_groups = kcalloc(cnt + 1, sizeof(struct attribute_group *),
GFP_KERNEL);
if (ovcs->attr_groups == NULL) {
ret = -ENOMEM;
goto err_free_fragments;
}
for (i = 0; i < cnt; i++) {
fragment = &ovcs->fragments[i];
ovcs->attr_groups[i] = &fragment->attr_group;
fragment->target_attr = target_template_attr;
/* make lockdep happy */
sysfs_attr_init(&fragment->target_attr.attr);
fragment->target_attr.fragment = fragment;
fragment->attrs[0] = &fragment->target_attr.attr;
fragment->attrs[1] = NULL;
/* NOTE: direct reference to the full_name */
fragment->attr_group.name =
kbasename(fragment->info->full_name);
fragment->attr_group.attrs = fragment->attrs;
}
ovcs->attr_groups[i] = NULL;
return 0;
err_free_fragments:
......@@ -866,9 +928,12 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
if (ovcs->id)
idr_remove(&ovcs_idr, ovcs->id);
kfree(ovcs->attr_groups);
for (i = 0; i < ovcs->count; i++) {
of_node_put(ovcs->fragments[i].target);
of_node_put(ovcs->fragments[i].overlay);
of_node_put(ovcs->fragments[i].info);
}
kfree(ovcs->fragments);
kobject_put(&ovcs->kobj);
......@@ -922,8 +987,25 @@ static const struct attribute *overlay_global_attrs[] = {
NULL
};
static ssize_t can_remove_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct overlay_changeset *ovcs = kobj_to_ovcs(kobj);
return snprintf(buf, PAGE_SIZE, "%d\n", overlay_removal_is_ok(ovcs));
}
static struct kobj_attribute can_remove_attr = __ATTR_RO(can_remove);
static struct attribute *overlay_changeset_attrs[] = {
&can_remove_attr.attr,
NULL
};
static struct kobj_type overlay_changeset_ktype = {
.release = overlay_changeset_release,
.sysfs_ops = &kobj_sysfs_ops, /* default kobj sysfs ops */
.default_attrs = overlay_changeset_attrs,
};
static struct kset *ov_kset;
......@@ -1046,15 +1128,14 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree,
if (ret != 0) {
pr_err("%s: kobject_add() failed for tree@%s\n", __func__,
tree->full_name);
ret_tmp = 0;
ret_revert = __of_changeset_revert_entries(&ovcs->cset,
&ret_tmp);
if (ret_revert) {
pr_debug("overlay changeset revert error %d\n",
ret_revert);
devicetree_state_flags |= DTSF_REVERT_FAIL;
}
goto err_free_overlay_changeset;
goto err_revert;
}
ret = sysfs_create_groups(&ovcs->kobj, ovcs->attr_groups);
if (ret != 0) {
pr_err("%s: sysfs_create_groups() failed for tree@%s\n",
__func__, tree->full_name);
goto err_revert;
}
ret = __of_changeset_apply_notify(&ovcs->cset);
......@@ -1075,6 +1156,14 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree,
goto out_unlock;
err_revert:
ret_tmp = 0;
ret_revert = __of_changeset_revert_entries(&ovcs->cset, &ret_tmp);
if (ret_revert) {
pr_debug("overlay changeset revert error %d\n", ret_revert);
devicetree_state_flags |= DTSF_REVERT_FAIL;
}
err_free_tree:
kfree(fdt);
kfree(tree);
......@@ -1299,6 +1388,8 @@ int of_overlay_remove(int *ovcs_id)
list_del(&ovcs->ovcs_list);
sysfs_remove_groups(&ovcs->kobj, ovcs->attr_groups);
ret_apply = 0;
ret = __of_changeset_revert_entries(&ovcs->cset, &ret_apply);
if (ret) {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment