devres: provide and use devm_krealloc()
Subsystems (5)
v8 cover devres: provide and use devm_krealloc()
From: Bartosz Golaszewski <[email protected]>
Regular krealloc() obviously can't work with managed memory. This series implements devm_krealloc() and adds two first users with hope that this helper will be adopted by other drivers currently using non-managed krealloc().
v1 -> v2:
- remove leftover call to hwmon_device_unregister() from pmbus_core.c
- add a patch extending devm_kmalloc() to handle zero size case
- use WARN_ON() instead of WARN_ONCE() in devm_krealloc() when passed a pointer to non-managed memory
- correctly handle the case when devm_krealloc() is passed a pointer to memory in .rodata (potentially returned by devm_kstrdup_const())
- correctly handle ZERO_SIZE_PTR passed as the ptr argument in devm_krealloc()
v2 -> v3:
- drop already applied patches
- collect Acks
- add an additional user in iio
v3 -> v4:
- add the kerneldoc for devm_krealloc()
- WARN() outside of spinlock
- rename local variable
v4 -> v5:
- tweak the kerneldoc
v5 -> v6:
- tweak the devres_lock handling in devm_krealloc()
v6 -> v7:
- rework devm_krealloc() to avoid calling krealloc() with spinlock taken
v7 -> v8:
- drop unnecessary explicit pointer casting in to_devres()
- check the return value of ksize() to make sure the pointer actually points to a dynamically allocated chunk
- add more comments to explain the locking strategy and resource handling
Bartosz Golaszewski (3):
devres: provide devm_krealloc()
hwmon: pmbus: use more devres helpers
iio: adc: xilinx-xadc: use devm_krealloc()
.../driver-api/driver-model/devres.rst | 1 + drivers/base/devres.c | 114 ++++++++++++++++++ drivers/hwmon/pmbus/pmbus_core.c | 28 ++--- drivers/iio/adc/xilinx-xadc-core.c | 16 +-- include/linux/device.h | 2 + 5 files changed, 134 insertions(+), 27 deletions(-)
Signature · 1 line
2.26.1
PATCH v8 1/3 devres: provide devm_krealloc()
From: Bartosz Golaszewski <[email protected]>
Implement the managed variant of krealloc(). This function works with all memory allocated by devm_kmalloc() (or devres functions using it implicitly like devm_kmemdup(), devm_kstrdup() etc.).
Managed realloc'ed chunks can be manually released with devm_kfree().
--- .../driver-api/driver-model/devres.rst | 1 + drivers/base/devres.c | 114 ++++++++++++++++++ include/linux/device.h | 2 + 3 files changed, 117 insertions(+)
Documentation/driver-api/driver-model/devres.rst +1 -0
@@ -354,6 +354,7 @@ MEM devm_kmalloc() devm_kmalloc_array() devm_kmemdup() + devm_krealloc() devm_kstrdup() devm_kvasprintf() devm_kzalloc()
drivers/base/devres.c +114 -0
132 lines not on this page · raw message
include/linux/device.h +2 -0
@@ -206,6 +206,8 @@ int devres_release_group(struct device *dev, void *id);/* managed devm_k.alloc/kfree for device drivers */ void *devm_kmalloc (struct device *dev,size_t size,gfp_t gfp) __malloc; +void *devm_krealloc (struct device *dev,void *ptr,size_t size, +gfp_t gfp)__must_check ; __printf(3, 0) char *devm_kvasprintf(struct device *dev, gfp_t gfp,const char *fmt,va_list ap) __malloc; __printf(3, 4) char *devm_kasprintf(struct device *dev, gfp_t gfp,
Signature · 1 line
2.26.1
Re: [PATCH v8 1/3] devres: provide devm_krealloc()
Quoted 10 lines · Bartosz Golaszewski
From: Bartosz Golaszewski <[email protected]>
Implement the managed variant of
krealloc(). This function works with all memory allocated bydevm_kmalloc()(or devres functions using it implicitly likedevm_kmemdup(),devm_kstrdup()etc.).Managed realloc'ed chunks can be manually released with
devm_kfree().Signed-off-by Bartosz Golaszewski <[email protected]>Reviewed-by Andy Shevchenko <[email protected]>
...
drivers/base/devres.c struct devres { · line 47
Quoted 9 lines
drivers/base/devres.c +9
-0+static struct devres *to_devres (void *data) +{ +return data -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +} + +static size_t devres_data_size (size_t total_size) +{ +return total_size -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +}
I'm fine with above, but here is a side note, perhaps
offsetof(struct devres, data)
will be more practical (no duplication of alignment and hence slightly better maintenance)? (Note, I didn't check if it provides the correct result)
Another side note: do we have existing users of these helpers?
Signature · 2 lines
With Best Regards, Andy Shevchenko
Re: [PATCH v8 1/3] devres: provide devm_krealloc()
Quoted 1 line · Andy Shevchenko
On Thu, Aug 20, 2020 at 08:51:08PM +0200, Bartosz Golaszewski wrote:
...
Quoted 12 lines
drivers/base/devres.c struct devres { · line 47
Quoted 9 lines
drivers/base/devres.c +9
-0+static struct devres *to_devres (void *data) +{ +return data -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +} + +static size_t devres_data_size (size_t total_size) +{ +return total_size -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +}I'm fine with above, but here is a side note, perhaps
Quoted 4 lines
offsetof(struct devres, data)will be more practical (no duplication of alignment and hence slightly better maintenance)? (Note, I didn't check if it provides the correct result)
And if the result is correct wouldn't be first a simple container_of() after all?
Quoted 1 line
Another side note: do we have existing users of these helpers?
Signature · 2 lines
With Best Regards, Andy Shevchenko
Re: [PATCH v8 1/3] devres: provide devm_krealloc()
In reply to Andy ShevchenkoQuoted 27 lines · Andy Shevchenko
Quoted 10 lines · Bartosz Golaszewski
From: Bartosz Golaszewski <[email protected]>
Implement the managed variant of
krealloc(). This function works with all memory allocated bydevm_kmalloc()(or devres functions using it implicitly likedevm_kmemdup(),devm_kstrdup()etc.).Managed realloc'ed chunks can be manually released with
devm_kfree().Signed-off-by Bartosz Golaszewski <[email protected]>Reviewed-by Andy Shevchenko <[email protected]>...
drivers/base/devres.c struct devres { · line 47
Quoted 9 lines
drivers/base/devres.c +9
-0+static struct devres *to_devres (void *data) +{ +return data -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +} + +static size_t devres_data_size (size_t total_size) +{ +return total_size -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +}I'm fine with above, but here is a side note, perhaps
Quoted 5 lines
offsetof(struct devres, data)will be more practical (no duplication of alignment and hence slightly better maintenance)? (Note, I didn't check if it provides the correct result)
Hi Andy,
The data pointer in struct devres is defined as:
u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];
And this value (assigned the value of ARCH_DMA_MINALIGN) varies from one arch to another. I wasn't really sure if offsetof() would work for every case so I went with something very explicit.
Quoted 2 lines
Another side note: do we have existing users of these helpers?
Which ones? Because I assume you're not referring to the ones I'm adding in this patch. :)
Bart
Re: [PATCH v8 1/3] devres: provide devm_krealloc()
Quoted 4 lines · Bartosz Golaszewski
Quoted 2 lines · Andy Shevchenko
Quoted 1 line · Bartosz Golaszewski
From: Bartosz Golaszewski <[email protected]>
...
Quoted 20 lines
Quoted 12 lines
drivers/base/devres.c struct devres { · line 47
Quoted 9 lines
drivers/base/devres.c +9
-0+static struct devres *to_devres (void *data) +{ +return data -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +} + +static size_t devres_data_size (size_t total_size) +{ +return total_size -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +}I'm fine with above, but here is a side note, perhaps
Quoted 5 lines
offsetof(struct devres, data)will be more practical (no duplication of alignment and hence slightly better maintenance)? (Note, I didn't check if it provides the correct result)
Hi Andy,
Quoted 7 lines
The data pointer in struct devres is defined as:
u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];And this value (assigned the value of
ARCH_DMA_MINALIGN) varies from one arch to another. I wasn't really sure ifoffsetof()would work for every case so I went with something very explicit.
I have checked with a small program simulating to_devres() with your variant, offsetof() and container_of().
The result is this: if MINALIGN < sizeof(long) and since struct is unpacked the offsetof(), and thus container_of(), gives correct result, while ALIGN() approach mistakenly moves pointer too back.
Test results
~~~~~~~~~~~~
sizeof(devres), ALIGN(data), resulting 3 pointers against NULL followed by actual pointer of allocated struct and 3 pointers against it.
% for i in 1 2 4; do gcc -O2 -Wall -DSZ=$i -o test test.c && ./test | head -n1; done
szof: 24 a: 24 0xffffffffffffffe8 0xffffffffffffffef 0xffffffffffffffef, 0x55a3aa91e2a0: 0x55a3aa91e299 0x55a3aa91e2a0 0x55a3aa91e2a0
szof: 24 a: 24 0xffffffffffffffe8 0xffffffffffffffee 0xffffffffffffffee, 0x563d7b88b2a0: 0x563d7b88b29a 0x563d7b88b2a0 0x563d7b88b2a0
szof: 24 a: 24 0xffffffffffffffe8 0xffffffffffffffec 0xffffffffffffffec, 0x557d08cf82a0: 0x557d08cf829c 0x557d08cf82a0 0x557d08cf82a0
% for i in 1 2 4; do gcc -m32 -O2 -Wall -DSZ=$i -o test test.c && ./test | head -n1; done
szof: 12 a: 12 0xfffffff4 0xfffffff7 0xfffffff7, 0x584301a0: 0x5843019d 0x584301a0 0x584301a0
szof: 12 a: 12 0xfffffff4 0xfffffff6 0xfffffff6, 0x57bd61a0: 0x57bd619e 0x57bd61a0 0x57bd61a0
szof: 12 a: 12 0xfffffff4 0xfffffff4 0xfffffff4, 0x56b491a0: 0x56b491a0 0x56b491a0 0x56b491a0
I think you need to change this to use container_of() and offsetof().
Quoted 4 lines
Quoted 1 line
Another side note: do we have existing users of these helpers?
Which ones? Because I assume you're not referring to the ones I'm adding in this patch. :)
Opposite, the ones you are introduced here. Meaning that we might convert existing user(s) in the separate change(s) later on.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Re: [PATCH v8 1/3] devres: provide devm_krealloc()
Quoted 4 lines · Andy Shevchenko
Quoted 3 lines · Bartosz Golaszewski
Quoted 1 line · Andy Shevchenko
On Thu, Aug 20, 2020 at 08:51:08PM +0200, Bartosz Golaszewski wrote:
...
Quoted 24 lines
Quoted 12 lines
Quoted 9 lines
drivers/base/devres.c struct devres { · line 47
Quoted 9 lines
drivers/base/devres.c +9
-0+static struct devres *to_devres (void *data) +{ +return data -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +} + +static size_t devres_data_size (size_t total_size) +{ +return total_size -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +}The data pointer in struct devres is defined as:
Quoted 5 lines
u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];And this value (assigned the value of
ARCH_DMA_MINALIGN) varies from one arch to another. I wasn't really sure ifoffsetof()would work for every case so I went with something very explicit.I have checked with a small program simulating
to_devres()with your variant,offsetof()andcontainer_of().The result is this: if MINALIGN < sizeof(long) and since struct is unpacked the
offsetof(), and thuscontainer_of(), gives correct result, whileALIGN()approach mistakenly moves pointer too back.
...
Quoted 1 line
I think you need to change this to use
container_of()andoffsetof().
To be clear, there is probably no real problem, except unlikely possible MINALIGN=4 on 64-bit arch, but for sake of the correctness.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Re: [PATCH v8 1/3] devres: provide devm_krealloc()
Quoted 34 lines · Andy Shevchenko
Quoted 4 lines · Andy Shevchenko
Quoted 3 lines · Bartosz Golaszewski
Quoted 1 line · Andy Shevchenko
On Thu, Aug 20, 2020 at 08:51:08PM +0200, Bartosz Golaszewski wrote:
...
Quoted 18 lines
Quoted 12 lines
Quoted 9 lines
drivers/base/devres.c struct devres { · line 47
Quoted 9 lines
drivers/base/devres.c +9
-0+static struct devres *to_devres (void *data) +{ +return data -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +} + +static size_t devres_data_size (size_t total_size) +{ +return total_size -ALIGN (sizeof (struct devres ),ARCH_KMALLOC_MINALIGN ); +}The data pointer in struct devres is defined as:
Quoted 5 lines
u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];And this value (assigned the value of
ARCH_DMA_MINALIGN) varies from one arch to another. I wasn't really sure ifoffsetof()would work for every case so I went with something very explicit.Quoted 6 lines
I have checked with a small program simulating
to_devres()with your variant,offsetof()andcontainer_of().The result is this: if MINALIGN < sizeof(long) and since struct is unpacked the
offsetof(), and thuscontainer_of(), gives correct result, whileALIGN()approach mistakenly moves pointer too back.
Quoted 7 lines
...
Quoted 1 line
I think you need to change this to use
container_of()andoffsetof().To be clear, there is probably no real problem, except unlikely possible MINALIGN=4 on 64-bit arch, but for sake of the correctness.
Thanks for taking the time to check it. I'll switch to container_of() for (hopefully) the last iteration.
Bart
PATCH v8 2/3 hwmon: pmbus: use more devres helpers
In reply to Bartosz GolaszewskiFrom: Bartosz Golaszewski <[email protected]>
Shrink pmbus code by using devm_hwmon_device_register_with_groups() and devm_krealloc() instead of their non-managed variants.
--- drivers/hwmon/pmbus/pmbus_core.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-)
drivers/hwmon/pmbus/pmbus_core.c +9 -19
@@ -1018,9 +1018,9 @@ static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr) {if (data->num_attributes >= data->max_attributes -1 ) {int new_max_attrs = data->max_attributes +PMBUS_ATTR_ALLOC_SIZE ; -void *new_attrs =krealloc (data->group.attrs, - new_max_attrs *sizeof (void *), -GFP_KERNEL ); +void *new_attrs =devm_krealloc (data->dev, data->group.attrs, + new_max_attrs *sizeof (void *), +GFP_KERNEL );if (!new_attrs)return -ENOMEM ; data->group.attrs = new_attrs;
@@ -2534,7 +2534,7 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, ret =pmbus_find_attributes (client, data);if (ret) - goto out_kfree; +return ret;/* * If there are no attributes, something is wrong.
@@ -2542,35 +2542,27 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,*/ if (!data->num_attributes) {dev_err (dev,"No attributes found\n" ); - ret = -ENODEV ; - goto out_kfree; +return -ENODEV ; } data->groups[0 ] = &data->group;memcpy (data->groups +1 , info->groups,sizeof (void *) * groups_num); - data->hwmon_dev =hwmon_device_register_with_groups (dev, client->name, - data, data->groups); + data->hwmon_dev =devm_hwmon_device_register_with_groups (dev, + client->name, data, data->groups);if (IS_ERR (data->hwmon_dev)) { - ret =PTR_ERR (data->hwmon_dev);dev_err (dev,"Failed to register hwmon device\n" ); - goto out_kfree; +return PTR_ERR (data->hwmon_dev); } ret =pmbus_regulator_register (data);if (ret) - goto out_unregister; +return ret; ret =pmbus_init_debugfs (client, data);if (ret)dev_warn (dev,"Failed to register debugfs\n" );return 0 ; - -out_unregister: -hwmon_device_unregister (data->hwmon_dev); -out_kfree: -kfree (data->group.attrs); -return ret; }EXPORT_SYMBOL_GPL (pmbus_do_probe);
@@ -2580,8 +2572,6 @@ int pmbus_do_remove(struct i2c_client *client)debugfs_remove_recursive (data->debugfs); -hwmon_device_unregister (data->hwmon_dev); -kfree (data->group.attrs);return 0 ; }EXPORT_SYMBOL_GPL (pmbus_do_remove);
Signature · 1 line
2.26.1
PATCH v8 3/3 iio: adc: xilinx-xadc: use devm_krealloc()
In reply to Bartosz GolaszewskiFrom: Bartosz Golaszewski <[email protected]>
Use the managed variant of krealloc() and shrink the code a bit.
--- drivers/iio/adc/xilinx-xadc-core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/iio/adc/xilinx-xadc-core.c +8 -8
@@ -1092,6 +1092,7 @@ MODULE_DEVICE_TABLE(of, xadc_of_match_table);static int xadc_parse_dt (struct iio_dev *indio_dev,struct device_node *np,unsigned int *conf) { +struct device *dev = indio_dev->dev.parent;struct xadc *xadc =iio_priv (indio_dev);struct iio_chan_spec *channels, *chan;struct device_node *chan_node, *child;
@@ -1136,7 +1137,8 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, *conf |=XADC_CONF0_MUX |XADC_CONF0_CHAN (ext_mux_chan); } - channels =kmemdup (xadc_channels,sizeof (xadc_channels),GFP_KERNEL ); + channels =devm_kmemdup (dev, xadc_channels, +sizeof (xadc_channels),GFP_KERNEL );if (!channels)return -ENOMEM ;
@@ -1172,8 +1174,9 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,of_node_put (chan_node); indio_dev->num_channels = num_channels; - indio_dev->channels =krealloc (channels,sizeof (*channels) * - num_channels,GFP_KERNEL ); + indio_dev->channels =devm_krealloc (dev, channels, +sizeof (*channels) * num_channels, +GFP_KERNEL );/* If we can't resize the channels array, just use the original */ if (!indio_dev->channels) indio_dev->channels = channels;
@@ -1225,14 +1228,14 @@ static int xadc_probe(struct platform_device *pdev) ret =xadc_parse_dt (indio_dev, pdev->dev.of_node, &conf0);if (ret) - goto err_device_free; +return ret;if (xadc->ops->flags &XADC_FLAGS_BUFFERED ) { ret =iio_triggered_buffer_setup (indio_dev, &iio_pollfunc_store_time, &xadc_trigger_handler, &xadc_buffer_ops);if (ret) - goto err_device_free; +return ret; xadc->convst_trigger =xadc_alloc_trigger (indio_dev,"convst" );if (IS_ERR (xadc->convst_trigger)) {
@@ -1350,8 +1353,6 @@ static int xadc_probe(struct platform_device *pdev) err_triggered_buffer_cleanup:if (xadc->ops->flags &XADC_FLAGS_BUFFERED )iio_triggered_buffer_cleanup (indio_dev); -err_device_free: -kfree (indio_dev->channels);return ret; }
@@ -1371,7 +1372,6 @@ static int xadc_remove(struct platform_device *pdev)cancel_delayed_work_sync (&xadc->zynq_unmask_work);clk_disable_unprepare (xadc->clk);kfree (xadc->data); -kfree (indio_dev->channels);return 0 ; }
Signature · 1 line
2.26.1