Export APIs to copy device properties & more
Subsystems (4)
v2 cover Export APIs to copy device properties & more
Hi,
Here is the refreshed series exporting APIs to copy statically declared device properties. The reason is that we want to augment ACPI-based devices with properties, and drivers usually have a largish DMI table for multiple models, so it is desirable to mark everything as __initdata/__initconst, and then copy only the entry matching the device we are running on and discard the rest.
The last patch is not really about device property APIs, but rather allowing users to attach properties to i2c_board_info, and have them attached to instantiated device(s). The reason it is included is because it depends on device_add_properties() taking const pointer, which is patch #2.
If it seems useful I hope Rafael and Wolfram would figure a way to merge it :).
v2:
- addressed Andy's comments
- added property_entries_free()
- added patch to allow constify values of property arrays
- added i2c patch allowing to attach property to devices via board info
v1:
- initial posting
Dmitry Torokhov (4):
device property: export code duplicating array of property entries device property: allow to constify properties
driver property: constify property arrays values
i2c: allow specify device properties in i2c_board_info
drivers/base/property.c | 221 ++++++++++++++++++++++++++++++----------------- drivers/i2c/i2c-core.c | 16 +++- include/linux/i2c.h | 3 + include/linux/property.h | 19 ++-- 4 files changed, 170 insertions(+), 89 deletions(-)
Thanks.
Signature · 1 line
Dmitry
PATCH v2 1/4 device property: export code duplicating array of property entries
When augmenting ACPI-enumerated devices with additional property data based on DMI info, a module has often several potential property sets, with only one being active on a given box. In order to save memory it should be possible to mark everything and __initdata or __initconst, execute DMI match early, and duplicate relevant properties. Then kernel will discard the rest of them.
--- drivers/base/property.c | 137 +++++++++++++++++++++++++++++------------------ include/linux/property.h | 5 ++ 2 files changed, 90 insertions(+), 52 deletions(-)
drivers/base/property.c +85 -52
@@ -682,41 +682,8 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode, }EXPORT_SYMBOL_GPL (fwnode_property_match_string); -/** -* pset_free_set - releases memory allocated for copied property set -* @pset: Property set to release -* -* Function takes previously copied property set and releases all the -* memory allocated to it. -*/ -static void pset_free_set (struct property_set *pset) -{ -const struct property_entry *prop; -size_t i, nval; - -if (!pset) -return ; - -for (prop = pset->properties; prop->name; prop++) { -if (prop->is_array) { -if (prop->is_string && prop->pointer.str) { - nval = prop->length /sizeof (const char *); -for (i =0 ; i < nval; i++) -kfree (prop->pointer.str[i]); - } -kfree (prop->pointer.raw_data); - }else if (prop->is_string) { -kfree (prop->value.str); - } -kfree (prop->name); - } - -kfree (pset->properties); -kfree (pset); -} - -static int pset_copy_entry (struct property_entry *dst, -const struct property_entry *src) +static int property_entry_copy (struct property_entry *dst, +const struct property_entry *src) {const char **d, **s;size_t i, nval;
@@ -765,6 +732,84 @@ static int pset_copy_entry(struct property_entry *dst, }/** +* property_entries_dup - duplicate array of properties +* @properties: array of properties to copy +* +* This function creates a deep copy of the given NULL-terminated array +* of property entries. +*/ +struct property_entry * +property_entries_dup (const struct property_entry *properties) +{ +struct property_entry *p; +int i, n =0 ; + +while (properties[n].name) + n++; + + p =kcalloc (n +1 ,sizeof (*p),GFP_KERNEL ); +if (!p) +return ERR_PTR (-ENOMEM ); + +for (i =0 ; i < n; i++) { +int ret =property_entry_copy (&p[i], &properties[i]); +if (ret) { +kfree (p); +return ERR_PTR (ret); + } + } + +return p; +} +EXPORT_SYMBOL_GPL (property_entries_dup); + +/** +* property_entries_free - free previously allocated array of properties +* @properties: array of properties to destroy +* +* This function frees given NULL-terminated array of property entries, +* along with their data. +*/ +void property_entries_free (const struct property_entry *properties) +{ +const struct property_entry *p; +size_t i, nval; + +for (p = properties; p->name; p++) { +if (p->is_array) { +if (p->is_string && p->pointer.str) { + nval = p->length /sizeof (const char *); +for (i =0 ; i < nval; i++) +kfree (p->pointer.str[i]); + } +kfree (p->pointer.raw_data); + }else if (p->is_string) { +kfree (p->value.str); + } +kfree (p->name); + } + +kfree (properties); +} +EXPORT_SYMBOL_GPL (property_entries_free); + +/** +* pset_free_set - releases memory allocated for copied property set +* @pset: Property set to release +* +* Function takes previously copied property set and releases all the +* memory allocated to it. +*/ +static void pset_free_set (struct property_set *pset) +{ +if (!pset) +return ; + +property_entries_free (pset->properties); +kfree (pset); +} + +/** * pset_copy_set - copies property set * @pset: Property set to copy *
@@ -776,32 +821,20 @@ static int pset_copy_entry(struct property_entry *dst,*/ static struct property_set *pset_copy_set (const struct property_set *pset) { -const struct property_entry *entry; +struct property_entry *properties;struct property_set *p; -size_t i, n =0 ; p =kzalloc (sizeof (*p),GFP_KERNEL );if (!p)return ERR_PTR (-ENOMEM ); -while (pset->properties[n].name) - n++; - - p->properties =kcalloc (n +1 ,sizeof (*entry),GFP_KERNEL ); -if (!p->properties) { + properties =property_entries_dup (pset->properties); +if (IS_ERR (properties)) {kfree (p); -return ERR_PTR (-ENOMEM ); - } - -for (i =0 ; i < n; i++) { -int ret =pset_copy_entry (&p->properties[i], - &pset->properties[i]); -if (ret) { -pset_free_set (p); -return ERR_PTR (ret); - } +return ERR_CAST (properties); } + p->properties = properties;return p; }
include/linux/property.h +5 -0
@@ -241,6 +241,11 @@ struct property_entry { .name = _name_, \ } +struct property_entry * +property_entries_dup (const struct property_entry *properties); + +void property_entries_free (const struct property_entry *properties); +int device_add_properties (struct device *dev,struct property_entry *properties);void device_remove_properties (struct device *dev);
Signature · 1 line
2.11.0.483.g087da7b7c-goog
PATCH v2 2/4 device property: allow to constify properties
In reply to Dmitry TorokhovThere is no reason why statically defined properties should be modifiable, so let's make device_add_properties() and the rest of pset_*() functions to take const pointers to properties.
This will allow us to mark properties as const/__initconst at definition sites.
--- drivers/base/property.c | 35 ++++++++++++++++++----------------- include/linux/property.h | 2 +- 2 files changed, 19 insertions(+), 18 deletions(-)
drivers/base/property.c +18 -17
@@ -21,7 +21,7 @@struct property_set {struct fwnode_handle fwnode; -struct property_entry *properties; +const struct property_entry *properties; };static inline bool is_pset_node (struct fwnode_handle *fwnode)
@@ -35,10 +35,10 @@ static inline struct property_set *to_pset_node(struct fwnode_handle *fwnode) container_of(fwnode, struct property_set, fwnode) : NULL; } -static struct property_entry *pset_prop_get (struct property_set *pset, -const char *name) +static const struct property_entry *pset_prop_get (struct property_set *pset, +const char *name) { -struct property_entry *prop; +const struct property_entry *prop;if (!pset || !pset->properties)return NULL ;
@@ -50,11 +50,11 @@ static struct property_entry *pset_prop_get(struct property_set *pset,return NULL ; } -static void *pset_prop_find (struct property_set *pset,const char *propname, -size_t length) +static const void *pset_prop_find (struct property_set *pset, +const char *propname,size_t length) { -struct property_entry *prop; -void *pointer; +const struct property_entry *prop; +const void *pointer; prop =pset_prop_get (pset, propname);if (!prop)
@@ -74,7 +74,7 @@ static int pset_prop_read_u8_array(struct property_set *pset,const char *propname, u8 *values, size_t nval) { - void *pointer; + const void *pointer;size_t length = nval *sizeof (*values); pointer =pset_prop_find (pset, propname, length);
@@ -89,7 +89,7 @@ static int pset_prop_read_u16_array(struct property_set *pset,const char *propname, u16 *values, size_t nval) { - void *pointer; + const void *pointer;size_t length = nval *sizeof (*values); pointer =pset_prop_find (pset, propname, length);
@@ -104,7 +104,7 @@ static int pset_prop_read_u32_array(struct property_set *pset,const char *propname, u32 *values, size_t nval) { - void *pointer; + const void *pointer;size_t length = nval *sizeof (*values); pointer =pset_prop_find (pset, propname, length);
@@ -119,7 +119,7 @@ static int pset_prop_read_u64_array(struct property_set *pset,const char *propname, u64 *values, size_t nval) { - void *pointer; + const void *pointer;size_t length = nval *sizeof (*values); pointer =pset_prop_find (pset, propname, length);
@@ -133,7 +133,7 @@ static int pset_prop_read_u64_array(struct property_set *pset,static int pset_prop_count_elems_of_size (struct property_set *pset,const char *propname,size_t length) { -struct property_entry *prop; +const struct property_entry *prop; prop =pset_prop_get (pset, propname);if (!prop)
@@ -146,7 +146,7 @@ static int pset_prop_read_string_array(struct property_set *pset,const char *propname, const char **strings, size_t nval) { - void *pointer; + const void *pointer;size_t length = nval *sizeof (*strings); pointer =pset_prop_find (pset, propname, length);
@@ -160,8 +160,8 @@ static int pset_prop_read_string_array(struct property_set *pset,static int pset_prop_read_string (struct property_set *pset,const char *propname,const char **strings) { -struct property_entry *prop; -const char **pointer; +const struct property_entry *prop; +const char *const *pointer; prop =pset_prop_get (pset, propname);if (!prop)
@@ -880,7 +880,8 @@ EXPORT_SYMBOL_GPL(device_remove_properties);* @dev as its secondary firmware node. The function takes a copy of * @properties. */ -int device_add_properties (struct device *dev,struct property_entry *properties) +int device_add_properties (struct device *dev, +const struct property_entry *properties) {struct property_set *p, pset;
include/linux/property.h +1 -1
@@ -247,7 +247,7 @@ property_entries_dup(const struct property_entry *properties);void property_entries_free (const struct property_entry *properties);int device_add_properties (struct device *dev, -struct property_entry *properties); +const struct property_entry *properties);void device_remove_properties (struct device *dev);bool device_dma_supported (struct device *dev);
Signature · 1 line
2.11.0.483.g087da7b7c-goog
PATCH v2 3/4 driver property: constify property arrays values
In reply to Dmitry TorokhovData that is fed into property arrays should not be modified, so let's mark relevant pointers as const. This will allow us making source arrays as const/__initconst.
Also fix memory leaks on errors in property_entry_copy().
--- drivers/base/property.c | 65 +++++++++++++++++++++++++++++++++--------------- include/linux/property.h | 12 ++++----- 2 files changed, 51 insertions(+), 26 deletions(-)
drivers/base/property.c +45 -20
@@ -682,44 +682,65 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode, }EXPORT_SYMBOL_GPL (fwnode_property_match_string); +static int property_copy_string_array (struct property_entry *dst, +const struct property_entry *src) +{ +char **d; +size_t nval = src->length /sizeof (*d); +size_t i; + + d =kcalloc (nval,sizeof (*d),GFP_KERNEL ); +if (!d) +return -ENOMEM ; + +for (i =0 ; i < nval; i++) { + d[i] =kstrdup (src->pointer.str[i],GFP_KERNEL ); +if (!d[i] && src->pointer.str[i]) { +while (--i >=0 ) +kfree (d[i]); +return -ENOMEM ; + } + } + + dst->pointer.str = (void *)d; +return 0 ; +} +static int property_entry_copy (struct property_entry *dst,const struct property_entry *src) { -const char **d, **s; -size_t i, nval; +int error; dst->name =kstrdup (src->name,GFP_KERNEL );if (!dst->name)return -ENOMEM ;if (src->is_array) { -if (!src->length) -return -ENODATA ; +if (!src->length) { + error = -ENODATA ; + goto out_free_name; + }if (src->is_string) { - nval = src->length /sizeof (const char *); - dst->pointer.str =kcalloc (nval,sizeof (const char *), -GFP_KERNEL ); -if (!dst->pointer.str) -return -ENOMEM ; - - d = dst->pointer.str; - s = src->pointer.str; -for (i =0 ; i < nval; i++) { - d[i] =kstrdup (s[i],GFP_KERNEL ); -if (!d[i] && s[i]) -return -ENOMEM ; + error =property_copy_string_array (dst, src); +if (error) { + error = -ENOMEM ; + goto out_free_name; } }else { dst->pointer.raw_data =kmemdup (src->pointer.raw_data, src->length,GFP_KERNEL ); -if (!dst->pointer.raw_data) -return -ENOMEM ; +if (!dst->pointer.raw_data) { + error = -ENOMEM ; + goto out_free_name; + } } }else if (src->is_string) { dst->value.str =kstrdup (src->value.str,GFP_KERNEL ); -if (!dst->value.str && src->value.str) -return -ENOMEM ; +if (!dst->value.str && src->value.str) { + error = -ENOMEM ; + goto out_free_name; + } }else { dst->value.raw_data = src->value.raw_data; }
@@ -729,6 +750,10 @@ static int property_entry_copy(struct property_entry *dst, dst->is_string = src->is_string;return 0 ; + +out_free_name: +kfree (dst->name); +return error; }/**
include/linux/property.h +6 -6
@@ -160,12 +160,12 @@ struct property_entry {bool is_string;union {union { -void *raw_data; -u8 *u8_data; -u16 *u16_data; -u32 *u32_data; -u64 *u64_data; -const char **str; +const void *raw_data; +const u8 *u8_data; +const u16 *u16_data; +const u32 *u32_data; +const u64 *u64_data; +const char *const *str; } pointer;union {unsigned long long raw_data;
Signature · 1 line
2.11.0.483.g087da7b7c-goog
Re: [PATCH v2 3/4] driver property: constify property arrays values
Quoted 26 lines · Dmitry Torokhov
Data that is fed into property arrays should not be modified, so let's mark relevant pointers as const. This will allow us making source arrays as const/__initconst.
Also fix memory leaks on errors in
property_entry_copy().Signed-off-by Dmitry Torokhov <[email protected]>--- drivers/base/property.c | 65 +++++++++++++++++++++++++++++++++--------------- include/linux/property.h | 12 ++++----- 2 files changed, 51 insertions(+), 26 deletions(-)drivers/base/property.c +6
-0@@ -682,44 +682,65 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode, }EXPORT_SYMBOL_GPL (fwnode_property_match_string); +static int property_copy_string_array (struct property_entry *dst, +const struct property_entry *src) +{ +char **d; +size_t nval = src->length /sizeof (*d); +size_t i;
drivers/base/property.c int fwnode_property_match_string(struct fwnode_handle *fwnode, · line 700
Quoted 10 lines
drivers/base/property.c +10
-0+ + d =kcalloc (nval,sizeof (*d),GFP_KERNEL ); +if (!d) +return -ENOMEM ; + +for (i =0 ; i < nval; i++) { + d[i] =kstrdup (src->pointer.str[i],GFP_KERNEL ); +if (!d[i] && src->pointer.str[i]) { +while (--i >=0 ) +kfree (d[i]);
Should we free d as well here?
Quoted 7 lines
drivers/base/property.c +7
-0+return -ENOMEM ; + } + } + + dst->pointer.str = (void *)d; +return 0 ; +}
Re: [PATCH v2 3/4] driver property: constify property arrays values
Quoted 31 lines · Mika Westerberg
Quoted 30 lines · Dmitry Torokhov
Data that is fed into property arrays should not be modified, so let's mark relevant pointers as const. This will allow us making source arrays as const/__initconst.
Also fix memory leaks on errors in
property_entry_copy().Signed-off-by Dmitry Torokhov <[email protected]>--- drivers/base/property.c | 65 +++++++++++++++++++++++++++++++++--------------- include/linux/property.h | 12 ++++----- 2 files changed, 51 insertions(+), 26 deletions(-)drivers/base/property.c +10
-0@@ -682,44 +682,65 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode, }EXPORT_SYMBOL_GPL (fwnode_property_match_string); +static int property_copy_string_array (struct property_entry *dst, +const struct property_entry *src) +{ +char **d; +size_t nval = src->length /sizeof (*d); +size_t i; + + d =kcalloc (nval,sizeof (*d),GFP_KERNEL ); +if (!d) +return -ENOMEM ;
Quoted 8 lines
drivers/base/property.c int fwnode_property_match_string(struct fwnode_handle *fwnode, · line 700
Quoted 6 lines
drivers/base/property.c +6
-0+ +for (i =0 ; i < nval; i++) { + d[i] =kstrdup (src->pointer.str[i],GFP_KERNEL ); +if (!d[i] && src->pointer.str[i]) { +while (--i >=0 ) +kfree (d[i]);Should we free d as well here?
Yep.
Quoted 8 lines
Quoted 8 lines
drivers/base/property.c +7
-0+return -ENOMEM ; + } + } + + dst->pointer.str = (void *)d; +return 0 ; +}
Signature · 1 line
Dmitry
PATCH v2 4/4 i2c: allow specify device properties in i2c_board_info
In reply to Dmitry TorokhovWith many drivers converting to using generic device properties, it is useful to provide array of device properties when instantiating new i2c client via i2c_board_info and have them automatically added to the device in question.
--- drivers/i2c/i2c-core.c | 16 +++++++++++++++- include/linux/i2c.h | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-)
drivers/i2c/i2c-core.c +15 -1
@@ -1335,15 +1335,29 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.fwnode = info->fwnode;i2c_dev_set_name (adap, client); + +if (info->properties) { + status =device_add_properties (&client->dev, info->properties); +if (status) { +dev_err (&adap->dev, +"Failed to add properties to client %s: %d\n" , + client->name, status); + goto out_err; + } + } + status =device_register (&client->dev);if (status) - goto out_err; + goto out_free_props;dev_dbg (&adap->dev,"client [%s] registered with bus id %s\n" , client->name,dev_name (&client->dev));return client; +out_free_props: +if (info->properties) +device_remove_properties (&client->dev); out_err: dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x (%d)\n",
include/linux/i2c.h +3 -0
@@ -51,6 +51,7 @@ enum i2c_slave_event;typedef int (*i2c_slave_cb_t )(struct i2c_client *,enum i2c_slave_event ,u8 *);struct module ; +struct property_entry ;#if defined(CONFIG_I2C ) || defined(CONFIG_I2C_MODULE )/*
@@ -310,6 +311,7 @@ static inline int i2c_slave_event(struct i2c_client *client,* @archdata: copied into i2c_client.dev.archdata * @of_node: pointer to OpenFirmware device node * @fwnode: device node supplied by the platform firmware +* @properties: additional device properties for the device * @irq: stored in i2c_client.irq * * I2C doesn't actually support hardware probing, although controllers and
@@ -331,6 +333,7 @@ struct i2c_board_info {struct dev_archdata *archdata;struct device_node *of_node;struct fwnode_handle *fwnode; +const struct property_entry *properties;int irq; };
Signature · 1 line
2.11.0.483.g087da7b7c-goog
Re: [PATCH v2 4/4] i2c: allow specify device properties in i2c_board_info
Quoted 16 lines · Dmitry Torokhov
With many drivers converting to using generic device properties, it is useful to provide array of device properties when instantiating new i2c client via
i2c_board_infoand have them automatically added to the device in question.Signed-off-by Dmitry Torokhov <[email protected]>--- drivers/i2c/i2c-core.c | 16 +++++++++++++++- include/linux/i2c.h | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-)drivers/i2c/i2c-core.c +0
-0
drivers/i2c/i2c-core.c i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) · line 1340
Quoted 6 lines
drivers/i2c/i2c-core.c +3
-0@@ -1335,15 +1335,29 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.fwnode = info->fwnode;i2c_dev_set_name (adap, client); + +if (info->properties) { + status =device_add_properties (&client->dev, info->properties);
I don't remember anymore how it was but is the driver core supposed to free this automatically when the device is removed or should we deal this in the i2c-core instead?
Quoted 54 lines
drivers/i2c/i2c-core.c +12
-1+if (status) { +dev_err (&adap->dev, +"Failed to add properties to client %s: %d\n" , + client->name, status); + goto out_err; + } + } + status =device_register (&client->dev);if (status) - goto out_err; + goto out_free_props;dev_dbg (&adap->dev,"client [%s] registered with bus id %s\n" , client->name,dev_name (&client->dev));return client; +out_free_props: +if (info->properties) +device_remove_properties (&client->dev); out_err: dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x (%d)\n",include/linux/i2c.h +3
-0@@ -51,6 +51,7 @@ enum i2c_slave_event;typedef int (*i2c_slave_cb_t )(struct i2c_client *,enum i2c_slave_event ,u8 *);struct module ; +struct property_entry ;#if defined(CONFIG_I2C ) || defined(CONFIG_I2C_MODULE )/* @@ -310,6 +311,7 @@ static inline int i2c_slave_event(struct i2c_client *client,* @archdata: copied into i2c_client.dev.archdata * @of_node: pointer to OpenFirmware device node * @fwnode: device node supplied by the platform firmware +* @properties: additional device properties for the device * @irq: stored in i2c_client.irq * * I2C doesn't actually support hardware probing, although controllers and @@ -331,6 +333,7 @@ struct i2c_board_info {struct dev_archdata *archdata;struct device_node *of_node;struct fwnode_handle *fwnode; +const struct property_entry *properties;int irq; };Signature · 1 line
2.11.0.483.g087da7b7c-goog
Re: [PATCH v2 4/4] i2c: allow specify device properties in i2c_board_info
Quoted 19 lines · Mika Westerberg
Quoted 18 lines · Dmitry Torokhov
With many drivers converting to using generic device properties, it is useful to provide array of device properties when instantiating new i2c client via
i2c_board_infoand have them automatically added to the device in question.Signed-off-by Dmitry Torokhov <[email protected]>--- drivers/i2c/i2c-core.c | 16 +++++++++++++++- include/linux/i2c.h | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-)drivers/i2c/i2c-core.c +0
-0@@ -1335,15 +1335,29 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.fwnode = info->fwnode;
Quoted 9 lines
drivers/i2c/i2c-core.c i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) · line 1340
Quoted 4 lines
drivers/i2c/i2c-core.c +3
-0i2c_dev_set_name (adap, client); + +if (info->properties) { + status =device_add_properties (&client->dev, info->properties);I don't remember anymore how it was but is the driver core supposed to free this automatically when the device is removed or should we deal this in the i2c-core instead?
It is done as part of device_del().
Thanks.
Signature · 1 line
Dmitry