software node: allow embedding of small arrays into property_entry
We should not conflate whether a property data is an array or a single value with where it is stored (embedded into property_entry structure or out-of-line). All single-value properties are in effect 1-element arrays, and we can figure the amount of data stored in a property by examining its length and the data type. And arrays can be as easily stored in property entry instances as single values are, provided that we have enough space (we have up to 8 bytes). We can embed: - up to 8 bytes from U8 arrays - up to 4 words - up to 2 double words - one U64 value - one (on 64 bit architectures) or 2 (on 32 bit) strings. This change also has an effect of switching properties with small amount of data to embed it instead of keeping it separate when copying such properties. Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Родитель
e6bff4665c
Коммит
996b0830f9
|
@ -198,93 +198,84 @@ 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_inline) {
|
||||
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;
|
||||
void *dst_ptr;
|
||||
size_t nval;
|
||||
|
||||
if (!src->is_inline) {
|
||||
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;
|
||||
}
|
||||
|
||||
dst->pointer = new;
|
||||
} else if (src->type == DEV_PROP_STRING) {
|
||||
new = kstrdup(src->value.str, GFP_KERNEL);
|
||||
if (!new && src->value.str)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Properties with no data should not be marked as stored
|
||||
* out of line.
|
||||
*/
|
||||
if (!src->is_inline && !src->length)
|
||||
return -ENODATA;
|
||||
|
||||
if (src->length <= sizeof(dst->value)) {
|
||||
dst_ptr = &dst->value;
|
||||
dst->is_inline = true;
|
||||
dst->value.str = new;
|
||||
} else {
|
||||
dst->is_inline = true;
|
||||
dst->value = src->value;
|
||||
dst_ptr = kmalloc(src->length, GFP_KERNEL);
|
||||
if (!dst_ptr)
|
||||
return -ENOMEM;
|
||||
dst->pointer = dst_ptr;
|
||||
}
|
||||
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -484,6 +475,8 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
|
|||
const struct software_node_reference *ref;
|
||||
const struct property_entry *prop;
|
||||
struct fwnode_handle *refnode;
|
||||
u32 nargs_prop_val;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
if (!swnode || !swnode->node->references)
|
||||
|
@ -501,11 +494,13 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
|
|||
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)
|
||||
|
|
|
@ -240,11 +240,11 @@ struct property_entry {
|
|||
union {
|
||||
const void *pointer;
|
||||
union {
|
||||
u8 u8_data;
|
||||
u16 u16_data;
|
||||
u32 u32_data;
|
||||
u64 u64_data;
|
||||
const char *str;
|
||||
u8 u8_data[sizeof(u64) / sizeof(u8)];
|
||||
u16 u16_data[sizeof(u64) / sizeof(u16)];
|
||||
u32 u32_data[sizeof(u64) / sizeof(u32)];
|
||||
u64 u64_data[sizeof(u64) / sizeof(u64)];
|
||||
const char *str[sizeof(u64) / sizeof(char *)];
|
||||
} value;
|
||||
};
|
||||
};
|
||||
|
@ -256,7 +256,7 @@ struct property_entry {
|
|||
*/
|
||||
|
||||
#define __PROPERTY_ENTRY_ELEMENT_SIZE(_elem_) \
|
||||
sizeof(((struct property_entry *)NULL)->value._elem_)
|
||||
sizeof(((struct property_entry *)NULL)->value._elem_[0])
|
||||
|
||||
#define __PROPERTY_ENTRY_ARRAY_LEN(_name_, _elem_, _Type_, _val_, _len_)\
|
||||
(struct property_entry) { \
|
||||
|
@ -294,7 +294,7 @@ struct property_entry {
|
|||
.length = __PROPERTY_ENTRY_ELEMENT_SIZE(_elem_), \
|
||||
.is_inline = true, \
|
||||
.type = DEV_PROP_##_Type_, \
|
||||
{ .value = { ._elem_ = _val_ } }, \
|
||||
{ .value = { ._elem_[0] = _val_ } }, \
|
||||
}
|
||||
|
||||
#define PROPERTY_ENTRY_U8(_name_, _val_) \
|
||||
|
|
Загрузка…
Ссылка в новой задаче