Skip to content

Commit

Permalink
Merge tag 'devprop-5.6-rc1' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/rafael/linux-pm

Pull device properties framework updates from Rafael Wysocki:
 "Add support for reference properties in sofrware nodes (Dmitry
  Torokhov) and a basic test for property entries along with fixes on
  top of it (Dmitry Torokhov, Qian Cai, Alan Maguire)"

* tag 'devprop-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  software node: introduce CONFIG_KUNIT_DRIVER_PE_TEST
  usb: dwc3: use proper initializers for property entries
  drivers/base/test: fix global-out-of-bounds error
  software node: add basic tests for property entries
  software node: remove separate handling of references
  platform/x86: intel_cht_int33fe: use inline reference properties
  software node: implement reference properties
  software node: allow embedding of small arrays into property_entry
  software node: replace is_array with is_inline
  • Loading branch information
Linus Torvalds committed Jan 27, 2020
2 parents 55816dc + aa811e3 commit 85c009e
Show file tree
Hide file tree
Showing 7 changed files with 662 additions and 155 deletions.
154 changes: 84 additions & 70 deletions drivers/base/swnode.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,7 @@ static const void *property_get_pointer(const struct property_entry *prop)
if (!prop->length)
return NULL;

if (prop->is_array)
return prop->pointer;

return &prop->value;
return prop->is_inline ? &prop->value : prop->pointer;
}

static const void *property_entry_find(const struct property_entry *props,
Expand Down Expand Up @@ -201,92 +198,91 @@ static int property_entry_read_string_array(const struct property_entry *props,

static void property_entry_free_data(const struct property_entry *p)
{
const void *pointer = property_get_pointer(p);
const char * const *src_str;
size_t i, nval;

if (p->is_array) {
if (p->type == DEV_PROP_STRING && p->pointer) {
src_str = p->pointer;
nval = p->length / sizeof(const char *);
for (i = 0; i < nval; i++)
kfree(src_str[i]);
}
kfree(pointer);
} else if (p->type == DEV_PROP_STRING) {
kfree(p->value.str);
if (p->type == DEV_PROP_STRING) {
src_str = property_get_pointer(p);
nval = p->length / sizeof(*src_str);
for (i = 0; i < nval; i++)
kfree(src_str[i]);
}

if (!p->is_inline)
kfree(p->pointer);

kfree(p->name);
}

static const char * const *
property_copy_string_array(const struct property_entry *src)
static bool property_copy_string_array(const char **dst_ptr,
const char * const *src_ptr,
size_t nval)
{
const char **d;
const char * const *src_str = src->pointer;
size_t nval = src->length / sizeof(*d);
int i;

d = kcalloc(nval, sizeof(*d), GFP_KERNEL);
if (!d)
return NULL;

for (i = 0; i < nval; i++) {
d[i] = kstrdup(src_str[i], GFP_KERNEL);
if (!d[i] && src_str[i]) {
dst_ptr[i] = kstrdup(src_ptr[i], GFP_KERNEL);
if (!dst_ptr[i] && src_ptr[i]) {
while (--i >= 0)
kfree(d[i]);
kfree(d);
return NULL;
kfree(dst_ptr[i]);
return false;
}
}

return d;
return true;
}

static int property_entry_copy_data(struct property_entry *dst,
const struct property_entry *src)
{
const void *pointer = property_get_pointer(src);
const void *new;

if (src->is_array) {
if (!src->length)
return -ENODATA;

if (src->type == DEV_PROP_STRING) {
new = property_copy_string_array(src);
if (!new)
return -ENOMEM;
} else {
new = kmemdup(pointer, src->length, GFP_KERNEL);
if (!new)
return -ENOMEM;
}
void *dst_ptr;
size_t nval;

/*
* Properties with no data should not be marked as stored
* out of line.
*/
if (!src->is_inline && !src->length)
return -ENODATA;

/*
* Reference properties are never stored inline as
* they are too big.
*/
if (src->type == DEV_PROP_REF && src->is_inline)
return -EINVAL;

dst->is_array = true;
dst->pointer = new;
} else if (src->type == DEV_PROP_STRING) {
new = kstrdup(src->value.str, GFP_KERNEL);
if (!new && src->value.str)
if (src->length <= sizeof(dst->value)) {
dst_ptr = &dst->value;
dst->is_inline = true;
} else {
dst_ptr = kmalloc(src->length, GFP_KERNEL);
if (!dst_ptr)
return -ENOMEM;
dst->pointer = dst_ptr;
}

dst->value.str = new;
if (src->type == DEV_PROP_STRING) {
nval = src->length / sizeof(const char *);
if (!property_copy_string_array(dst_ptr, pointer, nval)) {
if (!dst->is_inline)
kfree(dst->pointer);
return -ENOMEM;
}
} else {
dst->value = src->value;
memcpy(dst_ptr, pointer, src->length);
}

dst->length = src->length;
dst->type = src->type;
dst->name = kstrdup(src->name, GFP_KERNEL);
if (!dst->name)
goto out_free_data;
if (!dst->name) {
property_entry_free_data(dst);
return -ENOMEM;
}

return 0;

out_free_data:
property_entry_free_data(dst);
return -ENOMEM;
}

/**
Expand Down Expand Up @@ -483,31 +479,49 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
struct fwnode_reference_args *args)
{
struct swnode *swnode = to_swnode(fwnode);
const struct software_node_reference *ref;
const struct software_node_ref_args *ref_array;
const struct software_node_ref_args *ref;
const struct property_entry *prop;
struct fwnode_handle *refnode;
u32 nargs_prop_val;
int error;
int i;

if (!swnode || !swnode->node->references)
if (!swnode)
return -ENOENT;

for (ref = swnode->node->references; ref->name; ref++)
if (!strcmp(ref->name, propname))
break;
prop = property_entry_get(swnode->node->properties, propname);
if (!prop)
return -ENOENT;

if (prop->type != DEV_PROP_REF)
return -EINVAL;

if (!ref->name || index > (ref->nrefs - 1))
/*
* We expect that references are never stored inline, even
* single ones, as they are too big.
*/
if (prop->is_inline)
return -EINVAL;

if (index * sizeof(*ref) >= prop->length)
return -ENOENT;

refnode = software_node_fwnode(ref->refs[index].node);
ref_array = prop->pointer;
ref = &ref_array[index];

refnode = software_node_fwnode(ref->node);
if (!refnode)
return -ENOENT;

if (nargs_prop) {
prop = property_entry_get(swnode->node->properties, nargs_prop);
if (!prop)
return -EINVAL;
error = property_entry_read_int_array(swnode->node->properties,
nargs_prop, sizeof(u32),
&nargs_prop_val, 1);
if (error)
return error;

nargs = prop->value.u32_data;
nargs = nargs_prop_val;
}

if (nargs > NR_FWNODE_REFERENCE_ARGS)
Expand All @@ -517,7 +531,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
args->nargs = nargs;

for (i = 0; i < nargs; i++)
args->args[i] = ref->refs[index].args[i];
args->args[i] = ref->args[i];

return 0;
}
Expand Down
3 changes: 3 additions & 0 deletions drivers/base/test/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ config TEST_ASYNC_DRIVER_PROBE
The module name will be test_async_driver_probe.ko

If unsure say N.
config KUNIT_DRIVER_PE_TEST
bool "KUnit Tests for property entry API"
depends on KUNIT=y
2 changes: 2 additions & 0 deletions drivers/base/test/Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o

obj-$(CONFIG_KUNIT_DRIVER_PE_TEST) += property-entry-test.o
Loading

0 comments on commit 85c009e

Please sign in to comment.