[PATCH] powerpc: Add/remove/update properties in firmware device tree
Add support for updating and removing device tree properties. Since we hand out pointers to properties with gay abandon, we can't just free the property storage. Instead we move deleted, or the old copy of an updated property, to a "dead properties" list. Also note, its not feasable to kref device tree properties. we call get_property() all over the kernel in a wild variety of contexts. One consequence of this change is that we now take a read_lock(&devtree_lock) when doing get_property(). Signed-off-by: Dave Boutcher <sleddog@us.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
Родитель
898b5395e9
Коммит
088186ded4
|
@ -1627,6 +1627,11 @@ static void of_node_release(struct kref *kref)
|
||||||
kfree(prop->value);
|
kfree(prop->value);
|
||||||
kfree(prop);
|
kfree(prop);
|
||||||
prop = next;
|
prop = next;
|
||||||
|
|
||||||
|
if (!prop) {
|
||||||
|
prop = node->deadprops;
|
||||||
|
node->deadprops = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
kfree(node->intrs);
|
kfree(node->intrs);
|
||||||
kfree(node->full_name);
|
kfree(node->full_name);
|
||||||
|
@ -1783,13 +1788,16 @@ unsigned char *get_property(struct device_node *np, const char *name,
|
||||||
{
|
{
|
||||||
struct property *pp;
|
struct property *pp;
|
||||||
|
|
||||||
|
read_lock(&devtree_lock);
|
||||||
for (pp = np->properties; pp != 0; pp = pp->next)
|
for (pp = np->properties; pp != 0; pp = pp->next)
|
||||||
if (strcmp(pp->name, name) == 0) {
|
if (strcmp(pp->name, name) == 0) {
|
||||||
if (lenp != 0)
|
if (lenp != 0)
|
||||||
*lenp = pp->length;
|
*lenp = pp->length;
|
||||||
return pp->value;
|
break;
|
||||||
}
|
}
|
||||||
return NULL;
|
read_unlock(&devtree_lock);
|
||||||
|
|
||||||
|
return pp ? pp->value : NULL;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(get_property);
|
EXPORT_SYMBOL(get_property);
|
||||||
|
|
||||||
|
@ -1823,4 +1831,82 @@ int prom_add_property(struct device_node* np, struct property* prop)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a property from a node. Note that we don't actually
|
||||||
|
* remove it, since we have given out who-knows-how-many pointers
|
||||||
|
* to the data using get-property. Instead we just move the property
|
||||||
|
* to the "dead properties" list, so it won't be found any more.
|
||||||
|
*/
|
||||||
|
int prom_remove_property(struct device_node *np, struct property *prop)
|
||||||
|
{
|
||||||
|
struct property **next;
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
write_lock(&devtree_lock);
|
||||||
|
next = &np->properties;
|
||||||
|
while (*next) {
|
||||||
|
if (*next == prop) {
|
||||||
|
/* found the node */
|
||||||
|
*next = prop->next;
|
||||||
|
prop->next = np->deadprops;
|
||||||
|
np->deadprops = prop;
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next = &(*next)->next;
|
||||||
|
}
|
||||||
|
write_unlock(&devtree_lock);
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PROC_DEVICETREE
|
||||||
|
/* try to remove the proc node as well */
|
||||||
|
if (np->pde)
|
||||||
|
proc_device_tree_remove_prop(np->pde, prop);
|
||||||
|
#endif /* CONFIG_PROC_DEVICETREE */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update a property in a node. Note that we don't actually
|
||||||
|
* remove it, since we have given out who-knows-how-many pointers
|
||||||
|
* to the data using get-property. Instead we just move the property
|
||||||
|
* to the "dead properties" list, and add the new property to the
|
||||||
|
* property list
|
||||||
|
*/
|
||||||
|
int prom_update_property(struct device_node *np,
|
||||||
|
struct property *newprop,
|
||||||
|
struct property *oldprop)
|
||||||
|
{
|
||||||
|
struct property **next;
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
write_lock(&devtree_lock);
|
||||||
|
next = &np->properties;
|
||||||
|
while (*next) {
|
||||||
|
if (*next == oldprop) {
|
||||||
|
/* found the node */
|
||||||
|
newprop->next = oldprop->next;
|
||||||
|
*next = newprop;
|
||||||
|
oldprop->next = np->deadprops;
|
||||||
|
np->deadprops = oldprop;
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next = &(*next)->next;
|
||||||
|
}
|
||||||
|
write_unlock(&devtree_lock);
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PROC_DEVICETREE
|
||||||
|
/* try to add to proc as well if it was initialized */
|
||||||
|
if (np->pde)
|
||||||
|
proc_device_tree_update_prop(np->pde, newprop, oldprop);
|
||||||
|
#endif /* CONFIG_PROC_DEVICETREE */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -87,6 +87,7 @@ struct device_node {
|
||||||
char *full_name;
|
char *full_name;
|
||||||
|
|
||||||
struct property *properties;
|
struct property *properties;
|
||||||
|
struct property *deadprops; /* removed properties */
|
||||||
struct device_node *parent;
|
struct device_node *parent;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
struct device_node *sibling;
|
struct device_node *sibling;
|
||||||
|
@ -164,6 +165,10 @@ extern int prom_n_size_cells(struct device_node* np);
|
||||||
extern int prom_n_intr_cells(struct device_node* np);
|
extern int prom_n_intr_cells(struct device_node* np);
|
||||||
extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
|
extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
|
||||||
extern int prom_add_property(struct device_node* np, struct property* prop);
|
extern int prom_add_property(struct device_node* np, struct property* prop);
|
||||||
|
extern int prom_remove_property(struct device_node *np, struct property *prop);
|
||||||
|
extern int prom_update_property(struct device_node *np,
|
||||||
|
struct property *newprop,
|
||||||
|
struct property *oldprop);
|
||||||
|
|
||||||
#ifdef CONFIG_PPC32
|
#ifdef CONFIG_PPC32
|
||||||
/*
|
/*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче