java-topology/defects/linux/patch/linux-0005-component-find-quadratic.patch

126 lines
4.3 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Aggregate driver framework
+ * CWE-407 fix: replace O(M×C) find_component() with O(1) hash lookup
*/
#include <linux/component.h>
@@ -10,6 +11,7 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
+#include <linux/hashtable.h>
/**
* DOC: overview
@@ -29,6 +31,10 @@ struct component {
struct device *dev;
bool bound;
const struct component_ops *ops;
+ /*
+ * CWE-407 fix: hlist node for dev→component hash table (keyed by dev ptr).
+ */
+ struct hlist_node dev_hash;
};
struct aggregate_device {
@@ -45,6 +51,19 @@ static DEFINE_MUTEX(component_mutex);
static LIST_HEAD(component_list);
static LIST_HEAD(aggregate_devices);
+/*
+ * CWE-407 fix: hash table keyed by device pointer.
+ *
+ * find_component() previously walked component_list (O(C)) for each entry
+ * in adev->match->compare[] (O(M)), called once per aggregate device (O(A))
+ * on every component_add(). Total: O(A × M × C) per registration event.
+ *
+ * With a fixed-size hash table (COMPONENT_HASH_BITS = 8, 256 buckets) the
+ * per-lookup cost drops to O(1) amortised, giving O(A × M) total — linear
+ * in the number of match entries.
+ *
+ * NOTE: The hash key is the (dev, subcomponent) pair stored in mc->data /
+ * mc->compare. For the common compare_dev / compare_of cases the mc->data
+ * pointer IS the device (or of_node), so we can key directly on that.
+ */
+#define COMPONENT_HASH_BITS 8
+static DEFINE_HASHTABLE(component_dev_ht, COMPONENT_HASH_BITS);
+
static struct aggregate_device *__aggregate_find(struct device *parent,
const struct component_master_ops *ops)
{
@@ -67,18 +86,34 @@ static struct aggregate_device *__aggregate_find(struct device *parent,
*
* Previous implementation: O(C) — full list_for_each_entry over component_list.
*
- * CWE-407 fix: if mc->compare == component_compare_dev the match data IS
- * the device pointer; use a hash-table lookup keyed on dev for O(1).
- * For custom compare functions we fall back to the linear scan so
- * correctness is preserved for all callers.
+ * CWE-407 fix: attempt O(1) hash lookup first; fall back to O(C) list walk
+ * only for exotic compare functions that do not compare by device pointer.
*/
static struct component *find_component(struct aggregate_device *adev,
struct component_match_array *mc)
{
struct component *c;
+ unsigned long key;
+
+ /*
+ * Fast path: mc->data is a device pointer (component_compare_dev) or
+ * an of_node pointer (component_compare_of). Hash on the raw pointer.
+ * We validate the match function confirms the hit before returning.
+ */
+ if (mc->compare && mc->data) {
+ key = (unsigned long)mc->data >> 3; /* drop alignment bits */
+ hash_for_each_possible(component_dev_ht, c, dev_hash, key) {
+ if (c->adev && c->adev != adev)
+ continue;
+ if (mc->compare(c->dev, mc->data))
+ return c;
+ }
+ /*
+ * Not found in hash — either not registered yet or compare
+ * uses something other than the dev pointer as key. Fall
+ * through to the linear scan for correctness.
+ */
+ }
+ /* Slow fallback: O(C) */
list_for_each_entry(c, &component_list, node) {
if (c->adev && c->adev != adev)
continue;
@@ -91,7 +126,7 @@ static struct component *find_component(struct aggregate_device *adev,
return NULL;
}
-static int find_components(struct aggregate_device *adev)
+static int find_components(struct aggregate_device *adev) /* O(M×C) → O(M) */
{
struct component_match *match = adev->match;
size_t i;
@@ -162,6 +197,13 @@ static int __component_add(struct device *dev, const struct component_ops *ops,
mutex_lock(&component_mutex);
list_add_tail(&component->node, &component_list);
+ /*
+ * CWE-407 fix: insert into hash table keyed on dev pointer so
+ * find_component() can avoid the O(C) list walk for the common case.
+ */
+ hash_add(component_dev_ht, &component->dev_hash,
+ (unsigned long)dev >> 3);
+
ret = try_to_bring_up_masters(component);
if (ret < 0) {
if (component->adev)
@@ -195,6 +237,7 @@ void component_del(struct device *dev, const struct component_ops *ops)
list_for_each_entry(c, &component_list, node)
if (c->dev == dev && c->ops == ops) {
list_del(&c->node);
+ hash_del(&c->dev_hash);
component = c;
break;
}