linux-embedded.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH PREVIEW RFC 0/6] Add support for boot-time caching
@ 2025-09-23 14:23 acampanella-thegoodpenguin
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit acampanella-thegoodpenguin
                   ` (6 more replies)
  0 siblings, 7 replies; 16+ messages in thread
From: acampanella-thegoodpenguin @ 2025-09-23 14:23 UTC (permalink / raw)
  To: linux-embedded

This patch series provides a simple key-value cache for storing boot-time
configuration data. The goal is to allow kernel components and drivers to
cache time-consuming-to-compute values during boot, enabling faster
subsequent boots by avoiding redundant initialization work.

During kernel boot, many subsystems perform expensive operations like:

- Hardware detection and configuration
- Performance tuning calculations
- Resource allocation decisions
- Driver initialization parameters

These operations often produce the same results across boots on the same
hardware, making them good candidates for caching.

Signed-off-by: acampanella-thegoodpenguin <acampanella@thegoodpenguin.co.uk>
---
Andrew Murray (1):
      crypto: use bootcache to cache fastest algorithm

Marc Kelly (3):
      base: bootcache: Add bootcache test backend
      base: bootcache: Add bootcache memory backend
      dt-bindings: bootcache: Add bindings for bootcache backend

acampanella-thegoodpenguin (2):
      base: bootcache: initial commit
      raid6: Add bootcache

 .../bootcache/linux,bootcache-backend-memory.yaml  |  67 +++++++
 MAINTAINERS                                        |   9 +
 crypto/xor.c                                       |  29 ++-
 drivers/base/Kconfig                               |  32 +++
 drivers/base/Makefile                              |   3 +
 drivers/base/bootcache.c                           | 179 +++++++++++++++++
 drivers/base/bootcache_backend_memory.c            | 220 +++++++++++++++++++++
 drivers/base/bootcache_backend_test.c              | 119 +++++++++++
 include/linux/bootcache.h                          | 219 ++++++++++++++++++++
 lib/raid6/algos.c                                  |  21 ++
 10 files changed, 897 insertions(+), 1 deletion(-)
---
base-commit: c17b750b3ad9f45f2b6f7e6f7f4679844244f0b9
change-id: 20250915-bootcache-1bcf0ee0d7b0

Best regards,
-- 
acampanella-thegoodpenguin <acampanella@thegoodpenguin.co.uk>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit
  2025-09-23 14:23 [PATCH PREVIEW RFC 0/6] Add support for boot-time caching acampanella-thegoodpenguin
@ 2025-09-23 14:23 ` acampanella-thegoodpenguin
  2025-09-29 23:38   ` Bird, Tim
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 2/6] raid6: Add bootcache acampanella-thegoodpenguin
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 16+ messages in thread
From: acampanella-thegoodpenguin @ 2025-09-23 14:23 UTC (permalink / raw)
  To: linux-embedded

bootcache provides boot-time key-value cache to help improve
boot performance by allowing drivers to cache expensive computations.

Simple API are provided:
- bootcache_get_u16()/bootcache_set_u16() - retrieve/store u16 values
- bootcache_get_u32()/bootcache_set_u32() - retrieve/store u32 values
- bootcache_get_u64()/bootcache_set_u64() - retrieve/store u64 values
- bootcache_get_string()/bootcache_set_string() - retrieve/store strings
- bootcache_register_backend() - Backend registration
- bootcache_add_entry() - Add cache entry into framework

Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
Signed-off-by: Andrea Campanella <acampanella@thegoodpenguin.co.uk>
---
 MAINTAINERS               |   6 ++
 drivers/base/Kconfig      |  11 +++
 drivers/base/Makefile     |   1 +
 drivers/base/bootcache.c  | 179 +++++++++++++++++++++++++++++++++++++
 include/linux/bootcache.h | 219 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 416 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index daf520a13bdf6a991c0160a96620f40308c29ee0..4bf3766b30bbf75214911bd4ce5256a066f05726 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4418,6 +4418,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/iio/imu/bosch,bmi323.yaml
 F:	drivers/iio/imu/bmi323/
 
+BOOT CACHE
+M:	Andrea Campanella <acampanella@thegoodpenguin.co.uk>
+S:	Maintained
+F:	drivers/base/bootcache.c
+F:	include/linux/bootcache.h
+
 BPF JIT for ARC
 M:	Shahab Vahedi <list+bpf@vahedi.org>
 L:	bpf@vger.kernel.org
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 064eb52ff7e2d4d8745e9c39882b41dc4cf02a89..da02f95948d880da83c3025addc1e111dbce339a 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -73,6 +73,17 @@ config DEVTMPFS_SAFE
 	  with the PROT_EXEC flag. This can break, for example, non-KMS
 	  video drivers.
 
+config BOOTCACHE
+	bool "Boot-time cache for the kernel"
+	help
+	  Enable a simple key-value cache subsystem for storing boot-time
+	  configuration data. This allows drivers and kernel subsystems to
+	  cache expensive computations during boot, potentially improving
+	  boot performance on subsequent reboots by avoiding redundant
+	  hardware detection and initialization work
+
+	  If unsure, say N.
+
 config STANDALONE
 	bool "Select only drivers that don't need compile-time external firmware"
 	default y
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 8074a10183dcb720a6b820b8476b230716b37f01..10a16e6c2ea1ad778fb7793583b9ee54d2498b2b 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -8,6 +8,7 @@ obj-y			:= component.o core.o bus.o dd.o syscore.o \
 			   topology.o container.o property.o cacheinfo.o \
 			   swnode.o faux.o
 obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
+obj-$(CONFIG_BOOTCACHE)    += bootcache.o
 obj-$(CONFIG_DEVTMPFS)	+= devtmpfs.o
 obj-y			+= power/
 obj-$(CONFIG_ISA_BUS_API)	+= isa.o
diff --git a/drivers/base/bootcache.c b/drivers/base/bootcache.c
new file mode 100644
index 0000000000000000000000000000000000000000..d74ead796b0f50ca9a90e84e7230b9ad6ca896d8
--- /dev/null
+++ b/drivers/base/bootcache.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/hashtable.h>
+#include <linux/string.h>
+#include <linux/stringhash.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/bootcache.h>
+
+static DEFINE_HASHTABLE(bootcache_table, BOOTCACHE_HASH_BITS);
+static DEFINE_SPINLOCK(bootcache_lock);
+static struct kobject *bootcache_kobj;
+static struct bootcache_info *bootcache_backend;
+static bool bootcache_initilized;
+
+int bootcache_register_backend(struct bootcache_info *bci)
+{
+	int ret;
+
+	if (!bci)
+		return -EINVAL;
+	if (!bci->name)
+		return -EINVAL;
+
+	/* If we're not ready, tell backend to try again later */
+	if (!bootcache_initilized)
+		return -EPROBE_DEFER;
+
+	if (bootcache_backend) {
+		pr_warn("bootcache: Backend '%s' is already registered, cannot register '%s'\n",
+		bootcache_backend->name, bci->name);
+		return -EBUSY;
+	}
+	pr_info("bootcache: Registering backend '%s'\n",
+		bci->name);
+
+	/* Have the backend load and populate the cache store */
+	ret = bci->load_cache();
+
+	if (ret)
+		goto failed_initilize;
+
+	bootcache_backend = bci;
+	return 0;
+
+failed_initilize:
+	return ret;
+}
+EXPORT_SYMBOL(bootcache_register_backend);
+
+int bootcache_get(const char *name, void *buf, size_t *len)
+{
+	struct bootcache_entry *entry;
+	u32 hash;
+	int ret = -ENOENT;
+
+	if (!name || !buf || !len)
+		return -EINVAL;
+
+	hash = full_name_hash(NULL, name, strlen(name));
+
+	spin_lock(&bootcache_lock);
+	hash_for_each_possible(bootcache_table, entry, node, hash) {
+		if (strcmp(entry->key, name) == 0) {
+			if (*len < entry->len) {
+				*len = entry->len;
+				ret = -ENOSPC;
+				goto unlock;
+			}
+			memcpy(buf, entry->data, entry->len);
+			*len = entry->len;
+			ret = 0;
+			goto unlock;
+		}
+	}
+
+unlock:
+	spin_unlock(&bootcache_lock);
+	return ret;
+}
+EXPORT_SYMBOL(bootcache_get);
+
+int bootcache_add_entry(struct bootcache_entry *entry)
+{
+	u32 hash;
+	struct bootcache_entry *existing_entry;
+	int ret = 0;
+
+	hash = full_name_hash(NULL, entry->key, strlen(entry->key));
+
+	spin_lock(&bootcache_lock);
+
+	hash_for_each_possible(bootcache_table, existing_entry, node, hash) {
+		if (strcmp(existing_entry->key, entry->key) == 0) {
+			ret = -EEXIST;  // Key already exists
+			goto unlock;
+		}
+	}
+
+	hash_add(bootcache_table, &entry->node, hash);
+
+unlock:
+	spin_unlock(&bootcache_lock);
+	return ret;
+}
+EXPORT_SYMBOL(bootcache_add_entry);
+
+int bootcache_set(const char *name, const void *data, size_t len)
+{
+	struct bootcache_entry *new_entry;
+	u32 hash;
+	int ret = 0;
+
+	if (!name || !data || !len)
+		return -EINVAL;
+
+	new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
+	if (!new_entry)
+		return -ENOMEM;
+
+	new_entry->key = kstrdup(name, GFP_KERNEL);
+	if (!new_entry->key) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	new_entry->data = kmemdup(data, len, GFP_KERNEL);
+	if (!new_entry->data) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	new_entry->len = len;
+	ret = bootcache_add_entry(new_entry);
+	if (!ret)
+		return 0;
+
+free:
+	kfree(new_entry->data);
+	kfree(new_entry->key);
+	kfree(new_entry);
+	return ret;
+}
+EXPORT_SYMBOL(bootcache_set);
+
+static ssize_t writeout_store(struct kobject *kobj, struct kobj_attribute *attr,
+			    const char *buf, size_t count)
+{
+	/*Implement persistent storage backend */
+	return count;
+}
+
+static struct kobj_attribute writeout_attr = __ATTR_WO(writeout);
+
+static int __init bootcache_init(void)
+{
+	int ret;
+
+	pr_info("bootcache: backend loaded\n");
+
+	/* Create /sys/kernel/bootcache/writeout */
+	bootcache_kobj = kobject_create_and_add("bootcache", kernel_kobj);
+	if (!bootcache_kobj)
+		return -ENOMEM;
+
+	ret = sysfs_create_file(bootcache_kobj, &writeout_attr.attr);
+	if (ret) {
+		kobject_put(bootcache_kobj);
+		return ret;
+	}
+	bootcache_initilized = true;
+	return 0;
+}
+core_initcall(bootcache_init);
diff --git a/include/linux/bootcache.h b/include/linux/bootcache.h
new file mode 100644
index 0000000000000000000000000000000000000000..52c07cf09bb87fc6c305485e5409bd235ede4e6e
--- /dev/null
+++ b/include/linux/bootcache.h
@@ -0,0 +1,219 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_BOOTCACHE_H
+#define _LINUX_BOOTCACHE_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_BOOTCACHE
+
+#define BOOTCACHE_HASH_BITS 6  /* 64 buckets */
+
+struct bootcache_entry {
+	struct hlist_node node;
+	char *key;
+	void *data;
+	size_t len;
+};
+
+/**
+ * struct bootcache_info - Structure for registering a boot cache backend.
+ *
+ * @name:   The name of the backend.
+ *
+ * Callbacks:
+ * @load_cache: Callback function to read and populate the framework from the cache.
+ */
+
+struct bootcache_info {
+	const char *name;
+	/* Callback Function Pointers */
+	int (*load_cache)(void);
+};
+
+/**
+ * bootcache_add_entry - Add an entry directly into the hash table
+ * @entry: bootcache_entry structure
+ *
+ * Returns: 0 on success, entry was added, do not free it.
+ *          1 on success but an existing entry was updated,
+ *            free to deallocate entry.
+ */
+int bootcache_add_entry(struct bootcache_entry *entry);
+
+/**
+ * bootcache_register_backend - Register a backend provider with the framework
+ * @bci: bootcache_info structure
+ *
+ * Returns: 0 on success, -EPROBE_DEFER if the frontend is not ready,
+ *          -EBUSY if another backend is already registered,
+ *          -EINVAL on invalid registration information.
+ */
+int bootcache_register_backend(struct bootcache_info *bci);
+
+/**
+ * bootcache_get - Retrieve arbitrary data from the cache
+ * @name: Key to look up
+ * @buf: Buffer to store retrieved data
+ * @len: On input, size of buffer; on output, actual data size
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters,
+ *          -ENOENT if not found, -ENOSPC if buffer too small
+ */
+int bootcache_get(const char *name, void *buf, size_t *len);
+
+/**
+ * bootcache_set - Store arbitrary data in the cache
+ * @name: Key to store under
+ * @data: Data to store
+ * @len: Length of data
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
+ */
+int bootcache_set(const char *name, const void *data, size_t len);
+
+#else /* !CONFIG_BOOTCACHE */
+
+static inline int bootcache_get(const char *name, void *buf, size_t *len)
+{
+	return -ENOENT;
+}
+
+static inline int bootcache_set(const char *name, const void *data, size_t len)
+{
+	return -EOPNOTSUPP;
+}
+
+/**
+ * bootcache_get_u16 - Retrieve a u16 value from the cache
+ * @name: Key to look up
+ * @out_val: Pointer to store the retrieved value
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters, -ENOENT if not found
+ */
+static inline int bootcache_get_u16(const char *name, u16 *out_val)
+{
+	size_t len = sizeof(u16);
+
+	if (IS_ENABLED(CONFIG_BOOTCACHE))
+		return bootcache_get(name, out_val, &len);
+	else
+		return -ENOENT;
+}
+
+/**
+ * bootcache_set_u16 - Store a u16 value in the cache
+ * @name: Key to store under
+ * @val: Value to store
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
+ */
+static inline int bootcache_set_u16(const char *name, u16 val)
+{
+	if (IS_ENABLED(CONFIG_BOOTCACHE))
+		return bootcache_set(name, &val, sizeof(u16));
+	else
+		return -EOPNOTSUPP;
+}
+
+/**
+ * bootcache_get_u32 - Retrieve a u32 value from the cache
+ * @name: Key to look up
+ * @out_val: Pointer to store the retrieved value
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters, -ENOENT if not found
+ */
+static inline int bootcache_get_u32(const char *name, u32 *out_val)
+{
+	size_t len = sizeof(u32);
+
+	if (IS_ENABLED(CONFIG_BOOTCACHE))
+		return bootcache_get(name, out_val, &len);
+	else
+		return -ENOENT;
+}
+
+/**
+ * bootcache_set_u32 - Store a u32 value in the cache
+ * @name: Key to store under
+ * @val: Value to store
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
+ */
+static inline int bootcache_set_u32(const char *name, u32 val)
+{
+	if (IS_ENABLED(CONFIG_BOOTCACHE))
+		return bootcache_set(name, &val, sizeof(u32));
+	else
+		return -EOPNOTSUPP;
+}
+
+/**
+ * bootcache_get_u64 - Retrieve a u64 value from the cache
+ * @name: Key to look up
+ * @out_val: Pointer to store the retrieved value
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters, -ENOENT if not found
+ */
+static inline int bootcache_get_u64(const char *name, u64 *out_val)
+{
+	size_t len = sizeof(u64);
+
+	if (IS_ENABLED(CONFIG_BOOTCACHE))
+		return bootcache_get(name, out_val, &len);
+	else
+		return -ENOENT;
+}
+
+/**
+ * bootcache_set_u64 - Store a u64 value in the cache
+ * @name: Key to store under
+ * @val: Value to store
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
+ */
+static inline int bootcache_set_u64(const char *name, u64 val)
+{
+	if (IS_ENABLED(CONFIG_BOOTCACHE))
+		return bootcache_set(name, &val, sizeof(u64));
+	else
+		return -EOPNOTSUPP;
+}
+
+/**
+ * bootcache_get_string - Retrieve a string from the cache
+ * @name: Key to look up
+ * @buf: Buffer to store retrieved string
+ * @buflen: Size of buffer
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters,
+ *          -ENOENT if not found, -ENOSPC if buffer too small
+ */
+static inline int bootcache_get_string(const char *name, char *buf, size_t buflen)
+{
+	size_t len = buflen;
+
+	if (IS_ENABLED(CONFIG_BOOTCACHE))
+		return bootcache_get(name, buf, &len);
+	else
+		return -ENOENT;
+}
+
+/**
+ * bootcache_set_string - Store a string in the cache
+ * @name: Key to store under
+ * @str: Null-terminated string to store
+ *
+ * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
+ */
+static inline int bootcache_set_string(const char *name, const char *str)
+{
+	if (IS_ENABLED(CONFIG_BOOTCACHE)) {
+		if (!str)
+			return -EINVAL;
+		return bootcache_set(name, str, strlen(str) + 1);
+	} else {
+		return -EOPNOTSUPP;
+	}
+}
+
+#endif /* _LINUX_BOOTCACHE_H */

-- 
2.48.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH PREVIEW RFC 2/6] raid6: Add bootcache
  2025-09-23 14:23 [PATCH PREVIEW RFC 0/6] Add support for boot-time caching acampanella-thegoodpenguin
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit acampanella-thegoodpenguin
@ 2025-09-23 14:23 ` acampanella-thegoodpenguin
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm acampanella-thegoodpenguin
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: acampanella-thegoodpenguin @ 2025-09-23 14:23 UTC (permalink / raw)
  To: linux-embedded

Check for previously cached results before benchmark

Signed-off-by: Andrea Campanella <acampanella@thegoodpenguin.co.uk>
---
 lib/raid6/algos.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 799e0e5eac26db3d10c07e79f46537af4ec6f182..fc69d94afdb4fc153626a3e21c506eda57540618 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/gfp.h>
 #endif
+#include <linux/bootcache.h>
 
 struct raid6_calls raid6_call;
 EXPORT_SYMBOL_GPL(raid6_call);
@@ -159,6 +160,24 @@ static inline const struct raid6_calls *raid6_choose_gen(
 	int start = (disks>>1)-1, stop = disks-3;	/* work on the second half of the disks */
 	const struct raid6_calls *const *algo;
 	const struct raid6_calls *best;
+	char cached_algo_name[32];
+
+	/* Try to get cached algorithm by name */
+	if (bootcache_get_string("raid6_best_algo", cached_algo_name,
+		sizeof(cached_algo_name)) == 0) {
+		/* Find algorithm by name */
+		for (algo = raid6_algos; *algo; algo++) {
+			if (strcmp((*algo)->name, cached_algo_name) == 0) {
+				if (!(*algo)->valid || (*algo)->valid()) {
+					raid6_call = *algo;
+					pr_info("raid6: using algorithm %s (from cache)\n",
+						(*algo)->name);
+					return best;
+				}
+				break;
+			}
+		}
+	}
 
 	for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) {
 		if (!best || (*algo)->priority >= best->priority) {
@@ -198,6 +217,8 @@ static inline const struct raid6_calls *raid6_choose_gen(
 		goto out;
 	}
 
+	bootcache_set_string("raid6_best_algo", best->name);
+
 	raid6_call = *best;
 
 	if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {

-- 
2.48.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm
  2025-09-23 14:23 [PATCH PREVIEW RFC 0/6] Add support for boot-time caching acampanella-thegoodpenguin
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit acampanella-thegoodpenguin
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 2/6] raid6: Add bootcache acampanella-thegoodpenguin
@ 2025-09-23 14:23 ` acampanella-thegoodpenguin
  2025-09-29 23:48   ` Bird, Tim
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 4/6] base: bootcache: Add bootcache test backend acampanella-thegoodpenguin
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 16+ messages in thread
From: acampanella-thegoodpenguin @ 2025-09-23 14:23 UTC (permalink / raw)
  To: linux-embedded

From: Andrew Murray <amurray@thegoodpenguin.co.uk>

During boot xor_blocks may determine the fastest xor algorithm
by using do_xor_speed to perform a speed test on available
algorithms. This process can increase the overall boot time.

Let's make use of bootcache to cache the result of the speed
test for subsequent boots.

Signed-off-by: Andrew Murray <amurray@thegoodpenguin.co.uk>
---
 crypto/xor.c | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/crypto/xor.c b/crypto/xor.c
index f39621a57bb33c4015c06dff00e03a07716618f6..3457df0414064758a1923752e91642d2237af7b3 100644
--- a/crypto/xor.c
+++ b/crypto/xor.c
@@ -14,6 +14,7 @@
 #include <linux/raid/xor.h>
 #include <linux/jiffies.h>
 #include <linux/preempt.h>
+#include <linux/bootcache.h>
 #include <asm/xor.h>
 
 #ifndef XOR_SELECT_TEMPLATE
@@ -54,13 +55,13 @@ EXPORT_SYMBOL(xor_blocks);
 /* Set of all registered templates.  */
 static struct xor_block_template *__initdata template_list;
 
-#ifndef MODULE
 static void __init do_xor_register(struct xor_block_template *tmpl)
 {
 	tmpl->next = template_list;
 	template_list = tmpl;
 }
 
+#ifndef MODULE
 static int __init register_xor_blocks(void)
 {
 	active_template = XOR_SELECT_TEMPLATE(NULL);
@@ -79,6 +80,21 @@ static int __init register_xor_blocks(void)
 #define BENCH_SIZE	4096
 #define REPS		800U
 
+static struct xor_block_template * __init
+xor_get_template_by_name(char *fastest_name)
+{
+	struct xor_block_template *f;
+
+#define xor_speed	do_xor_register
+	// build a list of templates
+	XOR_TRY_TEMPLATES;
+#undef xor_speed
+	for (f = template_list; f; f = f->next)
+		if (!strcmp(f->name, fastest_name))
+			return f;
+	return NULL;
+}
+
 static void __init
 do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)
 {
@@ -117,9 +133,18 @@ calibrate_xor_blocks(void)
 {
 	void *b1, *b2;
 	struct xor_block_template *f, *fastest;
+	char cached_name[32];
+	int ret;
 
 	fastest = XOR_SELECT_TEMPLATE(NULL);
 
+	if (!fastest) {
+		ret = bootcache_get_string("xor_blocks_fastest",
+				cached_name, sizeof(cached_name));
+		if (!ret)
+			fastest = xor_get_template_by_name(cached_name);
+	}
+
 	if (fastest) {
 		printk(KERN_INFO "xor: automatically using best "
 				 "checksumming function   %-10s\n",
@@ -149,6 +174,8 @@ calibrate_xor_blocks(void)
 		if (f->speed > fastest->speed)
 			fastest = f;
 
+	bootcache_set_string("xor_blocks_fastest", fastest->name);
+
 	pr_info("xor: using function: %s (%d MB/sec)\n",
 	       fastest->name, fastest->speed);
 

-- 
2.48.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH PREVIEW RFC 4/6] base: bootcache: Add bootcache test backend
  2025-09-23 14:23 [PATCH PREVIEW RFC 0/6] Add support for boot-time caching acampanella-thegoodpenguin
                   ` (2 preceding siblings ...)
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm acampanella-thegoodpenguin
@ 2025-09-23 14:23 ` acampanella-thegoodpenguin
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend acampanella-thegoodpenguin
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: acampanella-thegoodpenguin @ 2025-09-23 14:23 UTC (permalink / raw)
  To: linux-embedded

From: Marc Kelly <mkelly@thegoodpenguin.co.uk>

bootcache_backend_test provides a simple test backend of API
demonstration and testing purposes.

Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
---
 MAINTAINERS                           |   2 +
 drivers/base/Kconfig                  |  14 ++++
 drivers/base/Makefile                 |   1 +
 drivers/base/bootcache_backend_test.c | 119 ++++++++++++++++++++++++++++++++++
 4 files changed, 136 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 4bf3766b30bbf75214911bd4ce5256a066f05726..73679686d49ac653382ecb32b9b3b500a4509383 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4420,8 +4420,10 @@ F:	drivers/iio/imu/bmi323/
 
 BOOT CACHE
 M:	Andrea Campanella <acampanella@thegoodpenguin.co.uk>
+M:	Marc Kelly <mkelly@thegoodpenguin.co.uk>
 S:	Maintained
 F:	drivers/base/bootcache.c
+F:	drivers/base/bootcache_*
 F:	include/linux/bootcache.h
 
 BPF JIT for ARC
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index da02f95948d880da83c3025addc1e111dbce339a..1303364993ff4bf7fbbc210243dc6dc48fb1bd83 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -84,6 +84,20 @@ config BOOTCACHE
 
 	  If unsure, say N.
 
+	if BOOTCACHE
+	choice
+		prompt "Boot-time cache backend"
+		depends on BOOTCACHE
+
+	config BOOTCACHE_BACKEND_TEST
+		bool "Test backend"
+		help
+		  A simple backend for testing and development.
+		  It does not persist any data externally.
+
+	endchoice
+	endif
+
 config STANDALONE
 	bool "Select only drivers that don't need compile-time external firmware"
 	default y
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 10a16e6c2ea1ad778fb7793583b9ee54d2498b2b..dc87c21cd79468045878c4b3cef5714c12f65ec4 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -9,6 +9,7 @@ obj-y			:= component.o core.o bus.o dd.o syscore.o \
 			   swnode.o faux.o
 obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
 obj-$(CONFIG_BOOTCACHE)    += bootcache.o
+obj-$(CONFIG_BOOTCACHE_BACKEND_TEST)    += bootcache_backend_test.o
 obj-$(CONFIG_DEVTMPFS)	+= devtmpfs.o
 obj-y			+= power/
 obj-$(CONFIG_ISA_BUS_API)	+= isa.o
diff --git a/drivers/base/bootcache_backend_test.c b/drivers/base/bootcache_backend_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..b44b4802fb99aa70c731017c503c6084796e90ae
--- /dev/null
+++ b/drivers/base/bootcache_backend_test.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/hashtable.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
+#include <linux/bootcache.h>
+
+#define DRIVER_NAME "bootcache_test_backend"
+
+struct test_data {
+	const char *name;
+	u32 value;
+	size_t data_length;
+};
+
+static struct test_data test_data_array[] = {
+	{
+		.name = "Bootcache Test One",
+		.value = 1234,
+		.data_length = sizeof(u32),
+	},
+	{
+		.name = "Bootcache Test Two",
+		.value = 5678,
+		.data_length = sizeof(u32),
+	},
+	{
+		.name = "Bootcache Test Three",
+		.value = 9012,
+		.data_length = sizeof(u32),
+	},
+	{
+		.name = "Bootcache Test Four",
+		.value = 0xDEADBEEF,
+		.data_length = sizeof(u32),
+	},
+	{
+		.name = "Bootcache Test Five",
+		.value = 0xC0DEBAD0,
+		.data_length = sizeof(u32),
+	}
+};
+
+static int test_backend_load_cache(void)
+{
+
+	struct bootcache_entry *new_entry = NULL;
+	int i;
+	int ret;
+
+	pr_info("%s: Backend local_cache callback\n", DRIVER_NAME);
+
+	/*
+	 * We want to load a bunch of fake data into the cache here
+	 * so that it can be used for testing purposes
+	 */
+	for (i = 0; i < ARRAY_SIZE(test_data_array); i++) {
+		/*
+		 * Print the name and value of the current element.
+		 * Use pr_info for a standard kernel log message.
+		 */
+		new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
+		if (!new_entry)
+			return -ENOMEM;
+
+		new_entry->key = kstrdup(test_data_array[i].name, GFP_KERNEL);
+		if (!new_entry->key) {
+			kfree(new_entry);
+			return -ENOMEM;
+		}
+		new_entry->len = test_data_array[i].data_length;
+		new_entry->data = kmemdup(&test_data_array[i].value,
+			test_data_array[i].data_length, GFP_KERNEL);
+		if (!new_entry->data) {
+			kfree(new_entry->key);
+			kfree(new_entry);
+			return -ENOMEM;
+		}
+
+		/* call the framework provided function */
+		ret = bootcache_add_entry(new_entry);
+		if (ret) {
+			kfree(new_entry->key);
+			kfree(new_entry->data);
+			kfree(new_entry);
+			ret = 0;
+		}
+	}
+	return 0;
+}
+
+static struct bootcache_info cache_info = {
+	.name = "test",
+	.load_cache = test_backend_load_cache,
+};
+
+static int __init bootcache_backend_init(void)
+{
+	int ret;
+
+	ret = bootcache_register_backend(&cache_info);
+
+	if (ret < 0) {
+		pr_err("%s: bootcache_register_backend() failed with error %d\n",
+			DRIVER_NAME, ret);
+		return ret;
+	}
+	pr_info("%s: Backend loaded\n", DRIVER_NAME);
+
+	return 0;
+}
+
+core_initcall(bootcache_backend_init);

-- 
2.48.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend
  2025-09-23 14:23 [PATCH PREVIEW RFC 0/6] Add support for boot-time caching acampanella-thegoodpenguin
                   ` (3 preceding siblings ...)
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 4/6] base: bootcache: Add bootcache test backend acampanella-thegoodpenguin
@ 2025-09-23 14:23 ` acampanella-thegoodpenguin
  2025-09-24 14:42   ` Dan Scally
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 6/6] dt-bindings: bootcache: Add bindings for bootcache backend acampanella-thegoodpenguin
  2025-09-30 12:49 ` [PATCH PREVIEW RFC 0/6] Add support for boot-time caching Rob Landley
  6 siblings, 1 reply; 16+ messages in thread
From: acampanella-thegoodpenguin @ 2025-09-23 14:23 UTC (permalink / raw)
  To: linux-embedded

From: Marc Kelly <mkelly@thegoodpenguin.co.uk>

bootcache_backend_memory provides a simple memory based backend that can
inject data found stored in a reserved-memory block into the bootcache
framework.

Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
---
 drivers/base/Kconfig                    |   7 +
 drivers/base/Makefile                   |   1 +
 drivers/base/bootcache_backend_memory.c | 220 ++++++++++++++++++++++++++++++++
 3 files changed, 228 insertions(+)

diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 1303364993ff4bf7fbbc210243dc6dc48fb1bd83..00c0ea6fa31f2d9a8863c93218a4db7ff87f9c0a 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -95,6 +95,13 @@ config BOOTCACHE
 		  A simple backend for testing and development.
 		  It does not persist any data externally.
 
+	config BOOTCACHE_BACKEND_MEMORY
+		bool "Memory backend"
+		help
+		  A backend that reads the cache data from reserved system memory.
+		  The reserved memory block is defined in the device tree and is
+		  assumed to be populated by the bootloader.
+
 	endchoice
 	endif
 
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index dc87c21cd79468045878c4b3cef5714c12f65ec4..d818e72df290e6772297345efc71082adc04e1f2 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -10,6 +10,7 @@ obj-y			:= component.o core.o bus.o dd.o syscore.o \
 obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
 obj-$(CONFIG_BOOTCACHE)    += bootcache.o
 obj-$(CONFIG_BOOTCACHE_BACKEND_TEST)    += bootcache_backend_test.o
+obj-$(CONFIG_BOOTCACHE_BACKEND_MEMORY)    += bootcache_backend_memory.o
 obj-$(CONFIG_DEVTMPFS)	+= devtmpfs.o
 obj-y			+= power/
 obj-$(CONFIG_ISA_BUS_API)	+= isa.o
diff --git a/drivers/base/bootcache_backend_memory.c b/drivers/base/bootcache_backend_memory.c
new file mode 100644
index 0000000000000000000000000000000000000000..d7a83ce2725bc7aa5f37d5fc3dcd7bea753e4d68
--- /dev/null
+++ b/drivers/base/bootcache_backend_memory.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+#define DEBUG 1
+#include <linux/unaligned.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/hashtable.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
+#include <linux/bootcache.h>
+
+#define DRIVER_NAME "bootcache_memory_backend"
+#define BOOTCACHE_MAGIC ('B' << 24 | 'C' << 16 | 'H' << 8 | 'E')
+#define BOOTCACHE_MINSIZE 4096
+
+/*
+ * This defines a cache entry as stored.
+ */
+struct cache_memory_store_entry {
+	u32 key_length;
+	u32 data_length;
+	u8 data_type;
+	u8 data[];
+} __packed;
+
+/*
+ * The in memory store of multiple cache entries.
+ */
+struct cache_memory_store {
+	u32 magic;
+	u32 entry_count;
+	struct cache_memory_store_entry entries[];
+} __packed;
+
+struct reserved_mem *rmem;
+
+/*
+ * This function processes the loaded data and adds each entry to the
+ * system cache via the callbck.
+ */
+static int memory_backend_load_cache(void)
+{
+	const struct cache_memory_store *store;
+	const u8 *current_ptr;
+	const void *max_address;
+	u32 entry_count;
+	int i;
+	int ret;
+
+	if (!rmem) {
+		pr_warn("%s: No bootcache was found\n", DRIVER_NAME);
+		return 0;
+	}
+
+	store = ioremap(rmem->base, rmem->size);
+	if (!store) {
+		pr_warn("%s: Unable to map reserved memory 0x%llx\n", DRIVER_NAME, rmem->base);
+		return -ENOMEM;
+	}
+	max_address = store + rmem->size;
+	current_ptr = (const unsigned char *)store->entries;
+	entry_count = get_unaligned(&store->entry_count);
+
+	for (i = 0; i < entry_count; i++) {
+		struct cache_memory_store_entry *entry;
+		struct bootcache_entry *new_entry = NULL;
+		size_t data_length, key_length;
+		u8 *src, *dest;
+		int j;
+
+		entry = (struct cache_memory_store_entry *)current_ptr;
+		data_length = get_unaligned(&entry->data_length);
+		key_length = get_unaligned(&entry->key_length);
+
+		/* Check if will go outside the bounds */
+		if ((current_ptr + sizeof(struct cache_memory_store_entry) +
+				data_length + key_length + 1) > max_address) {
+			ret = -ENOMEM;
+			goto error;
+		}
+
+		new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
+		if (!new_entry) {
+			ret = -ENOMEM;
+			goto error;
+		}
+
+		new_entry->len = data_length;
+		new_entry->key = kzalloc(key_length+1, GFP_KERNEL);
+		new_entry->data = kzalloc(data_length, GFP_KERNEL);
+
+		if (!new_entry->key || !new_entry->data) {
+			pr_err("%s: Memory Allocation error creating new_entry data\n",
+				DRIVER_NAME);
+			kfree(new_entry->key);
+			kfree(new_entry->data);
+			kfree(new_entry);
+			ret = -ENOMEM;
+			goto error;
+		}
+		/*
+		 * Source data is potentially unaligned, so we copy it with the correct
+		 * access functions
+		 */
+		src = &entry->data[0];
+		dest = new_entry->key;
+		for (j = 0; j < key_length; j++)
+			*dest++ = get_unaligned(src++);
+
+		src = &entry->data[key_length+1];
+		dest = new_entry->data;
+		for (j = 0; j < data_length; j++)
+			*dest++ = get_unaligned(src++);
+
+		pr_debug("%s: Setting up Entry (%d) with key: %s, data length is %zu\n",
+			DRIVER_NAME, i, new_entry->key, new_entry->len);
+
+		/* call the framework provided function */
+		ret = bootcache_add_entry(new_entry);
+		if (ret) {
+			kfree(new_entry->key);
+			kfree(new_entry->data);
+			kfree(new_entry);
+			ret = 0;
+		}
+
+		/* Sanity check we've got space for the next extry */
+
+		current_ptr += sizeof(struct cache_memory_store_entry) +
+			data_length + key_length + 1;
+		if (current_ptr + sizeof(struct cache_memory_store_entry)
+				> max_address) {
+			ret = ret = -ENOMEM;
+			goto error;
+		}
+	}
+
+error:
+	if (store)
+		iounmap((void *)store);
+
+	return ret;
+}
+
+static struct bootcache_info cache_info = {
+	.name = "memory",
+	.load_cache = memory_backend_load_cache,
+};
+
+static int bootcache_backend_probe(struct platform_device *pdev)
+{
+	int ret;
+	size_t table_size;
+	struct cache_memory_store *temp_store;
+	struct device_node *reserved_mem_node;
+
+	/* Check for the front end being ready */
+
+	pr_debug("%s: %s\n", DRIVER_NAME, __func__);
+
+	reserved_mem_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
+	if (reserved_mem_node) {
+		rmem = of_reserved_mem_lookup(reserved_mem_node);
+		of_node_put(reserved_mem_node);
+	}
+
+	if (!rmem) {
+		pr_err("%s: Failed to find reserved memory region.\n", DRIVER_NAME);
+		return -ENOMEM;
+	}
+	pr_debug("%s: Found reserved cache memory block (%s):\n", DRIVER_NAME, rmem->name);
+	pr_debug("%s:  Physical Address: 0x%llx\n", DRIVER_NAME, rmem->base);
+	pr_debug("%s:  Size: 0x%llx (%llu bytes)\n", DRIVER_NAME, rmem->size,
+		rmem->size);
+
+	if (rmem->size < BOOTCACHE_MINSIZE) {
+		pr_err("%s: reserved memory too small (%llu bytes)\n", DRIVER_NAME, rmem->size);
+		return -ENOMEM;
+	}
+
+	ret = bootcache_register_backend(&cache_info);
+
+	if (ret < 0) {
+		pr_err("%s: bootcache_register_backend() failed with error %d\n",
+			DRIVER_NAME, ret);
+		return ret;
+	}
+	pr_info("%s: Backend loaded\n", DRIVER_NAME);
+
+	return 0;
+}
+
+static const struct of_device_id bootcache_backend_driver_dt_ids[] = {
+	{ .compatible = "linux,backend-backend-memory", },
+	{ }
+};
+
+static struct platform_driver bootcache_memory_platform_driver = {
+	.probe		= bootcache_backend_probe,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.of_match_table = of_match_ptr(bootcache_backend_driver_dt_ids),
+	},
+};
+
+static int __init bootcache_backend_init(void)
+{
+	return platform_driver_register(&bootcache_memory_platform_driver);
+}
+
+core_initcall(bootcache_backend_init);

-- 
2.48.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH PREVIEW RFC 6/6] dt-bindings: bootcache: Add bindings for bootcache backend
  2025-09-23 14:23 [PATCH PREVIEW RFC 0/6] Add support for boot-time caching acampanella-thegoodpenguin
                   ` (4 preceding siblings ...)
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend acampanella-thegoodpenguin
@ 2025-09-23 14:23 ` acampanella-thegoodpenguin
  2025-09-30 12:49 ` [PATCH PREVIEW RFC 0/6] Add support for boot-time caching Rob Landley
  6 siblings, 0 replies; 16+ messages in thread
From: acampanella-thegoodpenguin @ 2025-09-23 14:23 UTC (permalink / raw)
  To: linux-embedded

From: Marc Kelly <mkelly@thegoodpenguin.co.uk>

Add device tree bindings for the bootcache_backend_memory.

Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
---
 .../bootcache/linux,bootcache-backend-memory.yaml  | 67 ++++++++++++++++++++++
 MAINTAINERS                                        |  1 +
 2 files changed, 68 insertions(+)

diff --git a/Documentation/devicetree/bindings/bootcache/linux,bootcache-backend-memory.yaml b/Documentation/devicetree/bindings/bootcache/linux,bootcache-backend-memory.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..cf46bf4c513e263f67cdf2323349fa16837c8d22
--- /dev/null
+++ b/Documentation/devicetree/bindings/bootcache/linux,bootcache-backend-memory.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/bootcache/linux,bootcache-backend-memory.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Bootcache Framework reserved memory backend
+
+maintainers:
+  - Marc Kelly <mkelly@thegoodpenguin.co.uk>
+  - Andrea Campenella <acampenella@thegoodpenguin.co.uk>
+
+description:
+  This bootcache backend adds support for using reserved memory
+  for the storage location for bootcache data. On initialization this
+  memory is read and any bootcache entries found are added to the
+  bootcache framework.
+  Data is inserted into these memory locations via the bootloader
+  or other means before the kernel boots.
+
+properties:
+  compatible:
+    enum:
+      - linux,backend-backend-memory
+
+  reg:
+    maxItems: 2
+
+  memory-region:
+    maxItems: 1
+    description:
+      Contains the bootcache reserved memory.
+      Data format is a binary block of packed cache data consisting
+      of a selection of key and value pairs, along with size indicators.
+      The format should be considered private to the driver and should
+      not be interpreted outside of the drivers context.
+
+required:
+  - compatible
+  - reg
+  - memory-region
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    /*
+     * The bootcache-backend is placed in the in the main domain
+     * of the SOC, with the reserved memory location defined as
+     * follows.
+     * This defines 64kBytes in the upper 2GBytes.
+     *
+     * The size
+     * reserved-memory {
+     *     bootcache_reserved: bootcache-reserved@BFFF0000 {
+     *         no-map;
+     *         reg = <0x00 0xBFFF0000 0x00 0x10000>;
+     *         label = "bootcache_backend_memory";
+     *     };
+     * }
+     */
+
+    bootcache-backend@BFFF0000 {
+        compatible = "linux,backend-backend-memory";
+        reg = <0x00 0xBFFF0000 0x00 0x10000>;
+        memory-region = <&bootcache_reserved>;
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 73679686d49ac653382ecb32b9b3b500a4509383..d547fe2fa77f6fabdd19be3c0a68526d2f6d2d4d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4422,6 +4422,7 @@ BOOT CACHE
 M:	Andrea Campanella <acampanella@thegoodpenguin.co.uk>
 M:	Marc Kelly <mkelly@thegoodpenguin.co.uk>
 S:	Maintained
+F:	Documentation/devicetree/bindings/bootcache/linux,bootcache-backend-memory.yaml
 F:	drivers/base/bootcache.c
 F:	drivers/base/bootcache_*
 F:	include/linux/bootcache.h

-- 
2.48.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend acampanella-thegoodpenguin
@ 2025-09-24 14:42   ` Dan Scally
  2025-09-24 15:13     ` Andrea Campanella
  2025-09-26 17:34     ` Marc Kelly
  0 siblings, 2 replies; 16+ messages in thread
From: Dan Scally @ 2025-09-24 14:42 UTC (permalink / raw)
  To: acampanella-thegoodpenguin, linux-embedded

Hi Andrea

On 23/09/2025 15:23, acampanella-thegoodpenguin wrote:
> From: Marc Kelly <mkelly@thegoodpenguin.co.uk>
> 
> bootcache_backend_memory provides a simple memory based backend that can
> inject data found stored in a reserved-memory block into the bootcache
> framework.

This looks really cool and I'd like to give it a go; how are you testing it currently? Do you have a 
tool to create the blob that you're putting into the reserved memory that I could take a look at?

Thanks
Dan

> 
> Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
> ---
>   drivers/base/Kconfig                    |   7 +
>   drivers/base/Makefile                   |   1 +
>   drivers/base/bootcache_backend_memory.c | 220 ++++++++++++++++++++++++++++++++
>   3 files changed, 228 insertions(+)
> 
> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> index 1303364993ff4bf7fbbc210243dc6dc48fb1bd83..00c0ea6fa31f2d9a8863c93218a4db7ff87f9c0a 100644
> --- a/drivers/base/Kconfig
> +++ b/drivers/base/Kconfig
> @@ -95,6 +95,13 @@ config BOOTCACHE
>   		  A simple backend for testing and development.
>   		  It does not persist any data externally.
>   
> +	config BOOTCACHE_BACKEND_MEMORY
> +		bool "Memory backend"
> +		help
> +		  A backend that reads the cache data from reserved system memory.
> +		  The reserved memory block is defined in the device tree and is
> +		  assumed to be populated by the bootloader.
> +
>   	endchoice
>   	endif
>   
> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> index dc87c21cd79468045878c4b3cef5714c12f65ec4..d818e72df290e6772297345efc71082adc04e1f2 100644
> --- a/drivers/base/Makefile
> +++ b/drivers/base/Makefile
> @@ -10,6 +10,7 @@ obj-y			:= component.o core.o bus.o dd.o syscore.o \
>   obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
>   obj-$(CONFIG_BOOTCACHE)    += bootcache.o
>   obj-$(CONFIG_BOOTCACHE_BACKEND_TEST)    += bootcache_backend_test.o
> +obj-$(CONFIG_BOOTCACHE_BACKEND_MEMORY)    += bootcache_backend_memory.o
>   obj-$(CONFIG_DEVTMPFS)	+= devtmpfs.o
>   obj-y			+= power/
>   obj-$(CONFIG_ISA_BUS_API)	+= isa.o
> diff --git a/drivers/base/bootcache_backend_memory.c b/drivers/base/bootcache_backend_memory.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d7a83ce2725bc7aa5f37d5fc3dcd7bea753e4d68
> --- /dev/null
> +++ b/drivers/base/bootcache_backend_memory.c
> @@ -0,0 +1,220 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#define DEBUG 1
> +#include <linux/unaligned.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/hashtable.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/kobject.h>
> +#include <linux/sysfs.h>
> +
> +#include <linux/bootcache.h>
> +
> +#define DRIVER_NAME "bootcache_memory_backend"
> +#define BOOTCACHE_MAGIC ('B' << 24 | 'C' << 16 | 'H' << 8 | 'E')
> +#define BOOTCACHE_MINSIZE 4096
> +
> +/*
> + * This defines a cache entry as stored.
> + */
> +struct cache_memory_store_entry {
> +	u32 key_length;
> +	u32 data_length;
> +	u8 data_type;
> +	u8 data[];
> +} __packed;
> +
> +/*
> + * The in memory store of multiple cache entries.
> + */
> +struct cache_memory_store {
> +	u32 magic;
> +	u32 entry_count;
> +	struct cache_memory_store_entry entries[];
> +} __packed;
> +
> +struct reserved_mem *rmem;
> +
> +/*
> + * This function processes the loaded data and adds each entry to the
> + * system cache via the callbck.
> + */
> +static int memory_backend_load_cache(void)
> +{
> +	const struct cache_memory_store *store;
> +	const u8 *current_ptr;
> +	const void *max_address;
> +	u32 entry_count;
> +	int i;
> +	int ret;
> +
> +	if (!rmem) {
> +		pr_warn("%s: No bootcache was found\n", DRIVER_NAME);
> +		return 0;
> +	}
> +
> +	store = ioremap(rmem->base, rmem->size);
> +	if (!store) {
> +		pr_warn("%s: Unable to map reserved memory 0x%llx\n", DRIVER_NAME, rmem->base);
> +		return -ENOMEM;
> +	}
> +	max_address = store + rmem->size;
> +	current_ptr = (const unsigned char *)store->entries;
> +	entry_count = get_unaligned(&store->entry_count);
> +
> +	for (i = 0; i < entry_count; i++) {
> +		struct cache_memory_store_entry *entry;
> +		struct bootcache_entry *new_entry = NULL;
> +		size_t data_length, key_length;
> +		u8 *src, *dest;
> +		int j;
> +
> +		entry = (struct cache_memory_store_entry *)current_ptr;
> +		data_length = get_unaligned(&entry->data_length);
> +		key_length = get_unaligned(&entry->key_length);
> +
> +		/* Check if will go outside the bounds */
> +		if ((current_ptr + sizeof(struct cache_memory_store_entry) +
> +				data_length + key_length + 1) > max_address) {
> +			ret = -ENOMEM;
> +			goto error;
> +		}
> +
> +		new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
> +		if (!new_entry) {
> +			ret = -ENOMEM;
> +			goto error;
> +		}
> +
> +		new_entry->len = data_length;
> +		new_entry->key = kzalloc(key_length+1, GFP_KERNEL);
> +		new_entry->data = kzalloc(data_length, GFP_KERNEL);
> +
> +		if (!new_entry->key || !new_entry->data) {
> +			pr_err("%s: Memory Allocation error creating new_entry data\n",
> +				DRIVER_NAME);
> +			kfree(new_entry->key);
> +			kfree(new_entry->data);
> +			kfree(new_entry);
> +			ret = -ENOMEM;
> +			goto error;
> +		}
> +		/*
> +		 * Source data is potentially unaligned, so we copy it with the correct
> +		 * access functions
> +		 */
> +		src = &entry->data[0];
> +		dest = new_entry->key;
> +		for (j = 0; j < key_length; j++)
> +			*dest++ = get_unaligned(src++);
> +
> +		src = &entry->data[key_length+1];
> +		dest = new_entry->data;
> +		for (j = 0; j < data_length; j++)
> +			*dest++ = get_unaligned(src++);
> +
> +		pr_debug("%s: Setting up Entry (%d) with key: %s, data length is %zu\n",
> +			DRIVER_NAME, i, new_entry->key, new_entry->len);
> +
> +		/* call the framework provided function */
> +		ret = bootcache_add_entry(new_entry);
> +		if (ret) {
> +			kfree(new_entry->key);
> +			kfree(new_entry->data);
> +			kfree(new_entry);
> +			ret = 0;
> +		}
> +
> +		/* Sanity check we've got space for the next extry */
> +
> +		current_ptr += sizeof(struct cache_memory_store_entry) +
> +			data_length + key_length + 1;
> +		if (current_ptr + sizeof(struct cache_memory_store_entry)
> +				> max_address) {
> +			ret = ret = -ENOMEM;
> +			goto error;
> +		}
> +	}
> +
> +error:
> +	if (store)
> +		iounmap((void *)store);
> +
> +	return ret;
> +}
> +
> +static struct bootcache_info cache_info = {
> +	.name = "memory",
> +	.load_cache = memory_backend_load_cache,
> +};
> +
> +static int bootcache_backend_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	size_t table_size;
> +	struct cache_memory_store *temp_store;
> +	struct device_node *reserved_mem_node;
> +
> +	/* Check for the front end being ready */
> +
> +	pr_debug("%s: %s\n", DRIVER_NAME, __func__);
> +
> +	reserved_mem_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
> +	if (reserved_mem_node) {
> +		rmem = of_reserved_mem_lookup(reserved_mem_node);
> +		of_node_put(reserved_mem_node);
> +	}
> +
> +	if (!rmem) {
> +		pr_err("%s: Failed to find reserved memory region.\n", DRIVER_NAME);
> +		return -ENOMEM;
> +	}
> +	pr_debug("%s: Found reserved cache memory block (%s):\n", DRIVER_NAME, rmem->name);
> +	pr_debug("%s:  Physical Address: 0x%llx\n", DRIVER_NAME, rmem->base);
> +	pr_debug("%s:  Size: 0x%llx (%llu bytes)\n", DRIVER_NAME, rmem->size,
> +		rmem->size);
> +
> +	if (rmem->size < BOOTCACHE_MINSIZE) {
> +		pr_err("%s: reserved memory too small (%llu bytes)\n", DRIVER_NAME, rmem->size);
> +		return -ENOMEM;
> +	}
> +
> +	ret = bootcache_register_backend(&cache_info);
> +
> +	if (ret < 0) {
> +		pr_err("%s: bootcache_register_backend() failed with error %d\n",
> +			DRIVER_NAME, ret);
> +		return ret;
> +	}
> +	pr_info("%s: Backend loaded\n", DRIVER_NAME);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id bootcache_backend_driver_dt_ids[] = {
> +	{ .compatible = "linux,backend-backend-memory", },
> +	{ }
> +};
> +
> +static struct platform_driver bootcache_memory_platform_driver = {
> +	.probe		= bootcache_backend_probe,
> +	.driver		= {
> +		.name	= DRIVER_NAME,
> +		.of_match_table = of_match_ptr(bootcache_backend_driver_dt_ids),
> +	},
> +};
> +
> +static int __init bootcache_backend_init(void)
> +{
> +	return platform_driver_register(&bootcache_memory_platform_driver);
> +}
> +
> +core_initcall(bootcache_backend_init);
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend
  2025-09-24 14:42   ` Dan Scally
@ 2025-09-24 15:13     ` Andrea Campanella
  2025-09-26 17:34     ` Marc Kelly
  1 sibling, 0 replies; 16+ messages in thread
From: Andrea Campanella @ 2025-09-24 15:13 UTC (permalink / raw)
  To: Dan Scally, linux-embedded


On 9/24/25 15:42, Dan Scally wrote:
> Hi Andrea
>
> On 23/09/2025 15:23, acampanella-thegoodpenguin wrote:
>> From: Marc Kelly <mkelly@thegoodpenguin.co.uk>
>>
>> bootcache_backend_memory provides a simple memory based backend that can
>> inject data found stored in a reserved-memory block into the bootcache
>> framework.
>
> This looks really cool and I'd like to give it a go; how are you 
> testing it currently? Do you have a tool to create the blob that 
> you're putting into the reserved memory that I could take a look at?
>
> Thanks
> Dan
>
>

Hello Dan,


I'm glad you are finding it interesting!

We are in the process of cleaning up the demo for public use, we are 
using qemuarm64 to test it, we should be releasing the test setup soon!



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend
  2025-09-24 14:42   ` Dan Scally
  2025-09-24 15:13     ` Andrea Campanella
@ 2025-09-26 17:34     ` Marc Kelly
  2025-09-26 20:09       ` Dan Scally
  1 sibling, 1 reply; 16+ messages in thread
From: Marc Kelly @ 2025-09-26 17:34 UTC (permalink / raw)
  To: linux-embedded; +Cc: Dan Scally

Hi Dan,

There is a yocto build that targets qemuarm64 available at
https://github.com/The-Good-Penguin/TGP-kernel-cache-test which has
the latest patches and a script for generating the test cache data as
a binary for the bootloader to insert into reserved memory.

There are build instructions and it should be able to run with qemu
via the commands shown in the readme in the repo.

If you have any questions don't hesitate to get in touch. Our plan is
to keep the test distro up to date as we add more features etc as it's
a covenant way to test and demo things.

Best regards
Marc.

On Wed, 24 Sept 2025 at 15:42, Dan Scally <dan.scally@ideasonboard.com> wrote:
>
> Hi Andrea
>
> On 23/09/2025 15:23, acampanella-thegoodpenguin wrote:
> > From: Marc Kelly <mkelly@thegoodpenguin.co.uk>
> >
> > bootcache_backend_memory provides a simple memory based backend that can
> > inject data found stored in a reserved-memory block into the bootcache
> > framework.
>
> This looks really cool and I'd like to give it a go; how are you testing it currently? Do you have a
> tool to create the blob that you're putting into the reserved memory that I could take a look at?
>
> Thanks
> Dan
>
> >
> > Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
> > ---
> >   drivers/base/Kconfig                    |   7 +
> >   drivers/base/Makefile                   |   1 +
> >   drivers/base/bootcache_backend_memory.c | 220 ++++++++++++++++++++++++++++++++
> >   3 files changed, 228 insertions(+)
> >
> > diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> > index 1303364993ff4bf7fbbc210243dc6dc48fb1bd83..00c0ea6fa31f2d9a8863c93218a4db7ff87f9c0a 100644
> > --- a/drivers/base/Kconfig
> > +++ b/drivers/base/Kconfig
> > @@ -95,6 +95,13 @@ config BOOTCACHE
> >                 A simple backend for testing and development.
> >                 It does not persist any data externally.
> >
> > +     config BOOTCACHE_BACKEND_MEMORY
> > +             bool "Memory backend"
> > +             help
> > +               A backend that reads the cache data from reserved system memory.
> > +               The reserved memory block is defined in the device tree and is
> > +               assumed to be populated by the bootloader.
> > +
> >       endchoice
> >       endif
> >
> > diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> > index dc87c21cd79468045878c4b3cef5714c12f65ec4..d818e72df290e6772297345efc71082adc04e1f2 100644
> > --- a/drivers/base/Makefile
> > +++ b/drivers/base/Makefile
> > @@ -10,6 +10,7 @@ obj-y                       := component.o core.o bus.o dd.o syscore.o \
> >   obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
> >   obj-$(CONFIG_BOOTCACHE)    += bootcache.o
> >   obj-$(CONFIG_BOOTCACHE_BACKEND_TEST)    += bootcache_backend_test.o
> > +obj-$(CONFIG_BOOTCACHE_BACKEND_MEMORY)    += bootcache_backend_memory.o
> >   obj-$(CONFIG_DEVTMPFS)      += devtmpfs.o
> >   obj-y                       += power/
> >   obj-$(CONFIG_ISA_BUS_API)   += isa.o
> > diff --git a/drivers/base/bootcache_backend_memory.c b/drivers/base/bootcache_backend_memory.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..d7a83ce2725bc7aa5f37d5fc3dcd7bea753e4d68
> > --- /dev/null
> > +++ b/drivers/base/bootcache_backend_memory.c
> > @@ -0,0 +1,220 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +#define DEBUG 1
> > +#include <linux/unaligned.h>
> > +#include <linux/init.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/slab.h>
> > +#include <linux/hashtable.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_reserved_mem.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/string.h>
> > +#include <linux/types.h>
> > +#include <linux/errno.h>
> > +#include <linux/kobject.h>
> > +#include <linux/sysfs.h>
> > +
> > +#include <linux/bootcache.h>
> > +
> > +#define DRIVER_NAME "bootcache_memory_backend"
> > +#define BOOTCACHE_MAGIC ('B' << 24 | 'C' << 16 | 'H' << 8 | 'E')
> > +#define BOOTCACHE_MINSIZE 4096
> > +
> > +/*
> > + * This defines a cache entry as stored.
> > + */
> > +struct cache_memory_store_entry {
> > +     u32 key_length;
> > +     u32 data_length;
> > +     u8 data_type;
> > +     u8 data[];
> > +} __packed;
> > +
> > +/*
> > + * The in memory store of multiple cache entries.
> > + */
> > +struct cache_memory_store {
> > +     u32 magic;
> > +     u32 entry_count;
> > +     struct cache_memory_store_entry entries[];
> > +} __packed;
> > +
> > +struct reserved_mem *rmem;
> > +
> > +/*
> > + * This function processes the loaded data and adds each entry to the
> > + * system cache via the callbck.
> > + */
> > +static int memory_backend_load_cache(void)
> > +{
> > +     const struct cache_memory_store *store;
> > +     const u8 *current_ptr;
> > +     const void *max_address;
> > +     u32 entry_count;
> > +     int i;
> > +     int ret;
> > +
> > +     if (!rmem) {
> > +             pr_warn("%s: No bootcache was found\n", DRIVER_NAME);
> > +             return 0;
> > +     }
> > +
> > +     store = ioremap(rmem->base, rmem->size);
> > +     if (!store) {
> > +             pr_warn("%s: Unable to map reserved memory 0x%llx\n", DRIVER_NAME, rmem->base);
> > +             return -ENOMEM;
> > +     }
> > +     max_address = store + rmem->size;
> > +     current_ptr = (const unsigned char *)store->entries;
> > +     entry_count = get_unaligned(&store->entry_count);
> > +
> > +     for (i = 0; i < entry_count; i++) {
> > +             struct cache_memory_store_entry *entry;
> > +             struct bootcache_entry *new_entry = NULL;
> > +             size_t data_length, key_length;
> > +             u8 *src, *dest;
> > +             int j;
> > +
> > +             entry = (struct cache_memory_store_entry *)current_ptr;
> > +             data_length = get_unaligned(&entry->data_length);
> > +             key_length = get_unaligned(&entry->key_length);
> > +
> > +             /* Check if will go outside the bounds */
> > +             if ((current_ptr + sizeof(struct cache_memory_store_entry) +
> > +                             data_length + key_length + 1) > max_address) {
> > +                     ret = -ENOMEM;
> > +                     goto error;
> > +             }
> > +
> > +             new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
> > +             if (!new_entry) {
> > +                     ret = -ENOMEM;
> > +                     goto error;
> > +             }
> > +
> > +             new_entry->len = data_length;
> > +             new_entry->key = kzalloc(key_length+1, GFP_KERNEL);
> > +             new_entry->data = kzalloc(data_length, GFP_KERNEL);
> > +
> > +             if (!new_entry->key || !new_entry->data) {
> > +                     pr_err("%s: Memory Allocation error creating new_entry data\n",
> > +                             DRIVER_NAME);
> > +                     kfree(new_entry->key);
> > +                     kfree(new_entry->data);
> > +                     kfree(new_entry);
> > +                     ret = -ENOMEM;
> > +                     goto error;
> > +             }
> > +             /*
> > +              * Source data is potentially unaligned, so we copy it with the correct
> > +              * access functions
> > +              */
> > +             src = &entry->data[0];
> > +             dest = new_entry->key;
> > +             for (j = 0; j < key_length; j++)
> > +                     *dest++ = get_unaligned(src++);
> > +
> > +             src = &entry->data[key_length+1];
> > +             dest = new_entry->data;
> > +             for (j = 0; j < data_length; j++)
> > +                     *dest++ = get_unaligned(src++);
> > +
> > +             pr_debug("%s: Setting up Entry (%d) with key: %s, data length is %zu\n",
> > +                     DRIVER_NAME, i, new_entry->key, new_entry->len);
> > +
> > +             /* call the framework provided function */
> > +             ret = bootcache_add_entry(new_entry);
> > +             if (ret) {
> > +                     kfree(new_entry->key);
> > +                     kfree(new_entry->data);
> > +                     kfree(new_entry);
> > +                     ret = 0;
> > +             }
> > +
> > +             /* Sanity check we've got space for the next extry */
> > +
> > +             current_ptr += sizeof(struct cache_memory_store_entry) +
> > +                     data_length + key_length + 1;
> > +             if (current_ptr + sizeof(struct cache_memory_store_entry)
> > +                             > max_address) {
> > +                     ret = ret = -ENOMEM;
> > +                     goto error;
> > +             }
> > +     }
> > +
> > +error:
> > +     if (store)
> > +             iounmap((void *)store);
> > +
> > +     return ret;
> > +}
> > +
> > +static struct bootcache_info cache_info = {
> > +     .name = "memory",
> > +     .load_cache = memory_backend_load_cache,
> > +};
> > +
> > +static int bootcache_backend_probe(struct platform_device *pdev)
> > +{
> > +     int ret;
> > +     size_t table_size;
> > +     struct cache_memory_store *temp_store;
> > +     struct device_node *reserved_mem_node;
> > +
> > +     /* Check for the front end being ready */
> > +
> > +     pr_debug("%s: %s\n", DRIVER_NAME, __func__);
> > +
> > +     reserved_mem_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
> > +     if (reserved_mem_node) {
> > +             rmem = of_reserved_mem_lookup(reserved_mem_node);
> > +             of_node_put(reserved_mem_node);
> > +     }
> > +
> > +     if (!rmem) {
> > +             pr_err("%s: Failed to find reserved memory region.\n", DRIVER_NAME);
> > +             return -ENOMEM;
> > +     }
> > +     pr_debug("%s: Found reserved cache memory block (%s):\n", DRIVER_NAME, rmem->name);
> > +     pr_debug("%s:  Physical Address: 0x%llx\n", DRIVER_NAME, rmem->base);
> > +     pr_debug("%s:  Size: 0x%llx (%llu bytes)\n", DRIVER_NAME, rmem->size,
> > +             rmem->size);
> > +
> > +     if (rmem->size < BOOTCACHE_MINSIZE) {
> > +             pr_err("%s: reserved memory too small (%llu bytes)\n", DRIVER_NAME, rmem->size);
> > +             return -ENOMEM;
> > +     }
> > +
> > +     ret = bootcache_register_backend(&cache_info);
> > +
> > +     if (ret < 0) {
> > +             pr_err("%s: bootcache_register_backend() failed with error %d\n",
> > +                     DRIVER_NAME, ret);
> > +             return ret;
> > +     }
> > +     pr_info("%s: Backend loaded\n", DRIVER_NAME);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct of_device_id bootcache_backend_driver_dt_ids[] = {
> > +     { .compatible = "linux,backend-backend-memory", },
> > +     { }
> > +};
> > +
> > +static struct platform_driver bootcache_memory_platform_driver = {
> > +     .probe          = bootcache_backend_probe,
> > +     .driver         = {
> > +             .name   = DRIVER_NAME,
> > +             .of_match_table = of_match_ptr(bootcache_backend_driver_dt_ids),
> > +     },
> > +};
> > +
> > +static int __init bootcache_backend_init(void)
> > +{
> > +     return platform_driver_register(&bootcache_memory_platform_driver);
> > +}
> > +
> > +core_initcall(bootcache_backend_init);
> >
>
>


-- 
Marc Kelly, Engineering Manager
https://www.thegoodpenguin.co.uk

The Good Penguin Ltd is a company registered in England and Wales with
company number 12374667 and VAT number 341687879. Registered office:
The Good Penguin Ltd, Merlin House, Priory Drive, Langstone, Newport,
Wales, NP18 2HJ, UK.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend
  2025-09-26 17:34     ` Marc Kelly
@ 2025-09-26 20:09       ` Dan Scally
  0 siblings, 0 replies; 16+ messages in thread
From: Dan Scally @ 2025-09-26 20:09 UTC (permalink / raw)
  To: Marc Kelly, linux-embedded

Hi Marc

On 26/09/2025 18:34, Marc Kelly wrote:
> Hi Dan,
> 
> There is a yocto build that targets qemuarm64 available at
> https://github.com/The-Good-Penguin/TGP-kernel-cache-test which has
> the latest patches and a script for generating the test cache data as
> a binary for the bootloader to insert into reserved memory.
> 
> There are build instructions and it should be able to run with qemu
> via the commands shown in the readme in the repo.
> 
> If you have any questions don't hesitate to get in touch. Our plan is
> to keep the test distro up to date as we add more features etc as it's
> a covenant way to test and demo things.

Thanks! That's great. I did actually give it a try on a board already and it seems to work well. I 
knocked up a script that's pretty similar to your generate_test_cache.py but parses a yaml file to 
create the .bin and load it and yeah - looks good to me.

I don't know if you're wanting reviews on the patches already yet, but if you let me know when you 
do and CC me on the sets I'll make sure I review them

Thanks
Dan

> 
> Best regards
> Marc.
> 
> On Wed, 24 Sept 2025 at 15:42, Dan Scally <dan.scally@ideasonboard.com> wrote:
>>
>> Hi Andrea
>>
>> On 23/09/2025 15:23, acampanella-thegoodpenguin wrote:
>>> From: Marc Kelly <mkelly@thegoodpenguin.co.uk>
>>>
>>> bootcache_backend_memory provides a simple memory based backend that can
>>> inject data found stored in a reserved-memory block into the bootcache
>>> framework.
>>
>> This looks really cool and I'd like to give it a go; how are you testing it currently? Do you have a
>> tool to create the blob that you're putting into the reserved memory that I could take a look at?
>>
>> Thanks
>> Dan
>>
>>>
>>> Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
>>> ---
>>>    drivers/base/Kconfig                    |   7 +
>>>    drivers/base/Makefile                   |   1 +
>>>    drivers/base/bootcache_backend_memory.c | 220 ++++++++++++++++++++++++++++++++
>>>    3 files changed, 228 insertions(+)
>>>
>>> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
>>> index 1303364993ff4bf7fbbc210243dc6dc48fb1bd83..00c0ea6fa31f2d9a8863c93218a4db7ff87f9c0a 100644
>>> --- a/drivers/base/Kconfig
>>> +++ b/drivers/base/Kconfig
>>> @@ -95,6 +95,13 @@ config BOOTCACHE
>>>                  A simple backend for testing and development.
>>>                  It does not persist any data externally.
>>>
>>> +     config BOOTCACHE_BACKEND_MEMORY
>>> +             bool "Memory backend"
>>> +             help
>>> +               A backend that reads the cache data from reserved system memory.
>>> +               The reserved memory block is defined in the device tree and is
>>> +               assumed to be populated by the bootloader.
>>> +
>>>        endchoice
>>>        endif
>>>
>>> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
>>> index dc87c21cd79468045878c4b3cef5714c12f65ec4..d818e72df290e6772297345efc71082adc04e1f2 100644
>>> --- a/drivers/base/Makefile
>>> +++ b/drivers/base/Makefile
>>> @@ -10,6 +10,7 @@ obj-y                       := component.o core.o bus.o dd.o syscore.o \
>>>    obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
>>>    obj-$(CONFIG_BOOTCACHE)    += bootcache.o
>>>    obj-$(CONFIG_BOOTCACHE_BACKEND_TEST)    += bootcache_backend_test.o
>>> +obj-$(CONFIG_BOOTCACHE_BACKEND_MEMORY)    += bootcache_backend_memory.o
>>>    obj-$(CONFIG_DEVTMPFS)      += devtmpfs.o
>>>    obj-y                       += power/
>>>    obj-$(CONFIG_ISA_BUS_API)   += isa.o
>>> diff --git a/drivers/base/bootcache_backend_memory.c b/drivers/base/bootcache_backend_memory.c
>>> new file mode 100644
>>> index 0000000000000000000000000000000000000000..d7a83ce2725bc7aa5f37d5fc3dcd7bea753e4d68
>>> --- /dev/null
>>> +++ b/drivers/base/bootcache_backend_memory.c
>>> @@ -0,0 +1,220 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +#define DEBUG 1
>>> +#include <linux/unaligned.h>
>>> +#include <linux/init.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/hashtable.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/of_reserved_mem.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/string.h>
>>> +#include <linux/types.h>
>>> +#include <linux/errno.h>
>>> +#include <linux/kobject.h>
>>> +#include <linux/sysfs.h>
>>> +
>>> +#include <linux/bootcache.h>
>>> +
>>> +#define DRIVER_NAME "bootcache_memory_backend"
>>> +#define BOOTCACHE_MAGIC ('B' << 24 | 'C' << 16 | 'H' << 8 | 'E')
>>> +#define BOOTCACHE_MINSIZE 4096
>>> +
>>> +/*
>>> + * This defines a cache entry as stored.
>>> + */
>>> +struct cache_memory_store_entry {
>>> +     u32 key_length;
>>> +     u32 data_length;
>>> +     u8 data_type;
>>> +     u8 data[];
>>> +} __packed;
>>> +
>>> +/*
>>> + * The in memory store of multiple cache entries.
>>> + */
>>> +struct cache_memory_store {
>>> +     u32 magic;
>>> +     u32 entry_count;
>>> +     struct cache_memory_store_entry entries[];
>>> +} __packed;
>>> +
>>> +struct reserved_mem *rmem;
>>> +
>>> +/*
>>> + * This function processes the loaded data and adds each entry to the
>>> + * system cache via the callbck.
>>> + */
>>> +static int memory_backend_load_cache(void)
>>> +{
>>> +     const struct cache_memory_store *store;
>>> +     const u8 *current_ptr;
>>> +     const void *max_address;
>>> +     u32 entry_count;
>>> +     int i;
>>> +     int ret;
>>> +
>>> +     if (!rmem) {
>>> +             pr_warn("%s: No bootcache was found\n", DRIVER_NAME);
>>> +             return 0;
>>> +     }
>>> +
>>> +     store = ioremap(rmem->base, rmem->size);
>>> +     if (!store) {
>>> +             pr_warn("%s: Unable to map reserved memory 0x%llx\n", DRIVER_NAME, rmem->base);
>>> +             return -ENOMEM;
>>> +     }
>>> +     max_address = store + rmem->size;
>>> +     current_ptr = (const unsigned char *)store->entries;
>>> +     entry_count = get_unaligned(&store->entry_count);
>>> +
>>> +     for (i = 0; i < entry_count; i++) {
>>> +             struct cache_memory_store_entry *entry;
>>> +             struct bootcache_entry *new_entry = NULL;
>>> +             size_t data_length, key_length;
>>> +             u8 *src, *dest;
>>> +             int j;
>>> +
>>> +             entry = (struct cache_memory_store_entry *)current_ptr;
>>> +             data_length = get_unaligned(&entry->data_length);
>>> +             key_length = get_unaligned(&entry->key_length);
>>> +
>>> +             /* Check if will go outside the bounds */
>>> +             if ((current_ptr + sizeof(struct cache_memory_store_entry) +
>>> +                             data_length + key_length + 1) > max_address) {
>>> +                     ret = -ENOMEM;
>>> +                     goto error;
>>> +             }
>>> +
>>> +             new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
>>> +             if (!new_entry) {
>>> +                     ret = -ENOMEM;
>>> +                     goto error;
>>> +             }
>>> +
>>> +             new_entry->len = data_length;
>>> +             new_entry->key = kzalloc(key_length+1, GFP_KERNEL);
>>> +             new_entry->data = kzalloc(data_length, GFP_KERNEL);
>>> +
>>> +             if (!new_entry->key || !new_entry->data) {
>>> +                     pr_err("%s: Memory Allocation error creating new_entry data\n",
>>> +                             DRIVER_NAME);
>>> +                     kfree(new_entry->key);
>>> +                     kfree(new_entry->data);
>>> +                     kfree(new_entry);
>>> +                     ret = -ENOMEM;
>>> +                     goto error;
>>> +             }
>>> +             /*
>>> +              * Source data is potentially unaligned, so we copy it with the correct
>>> +              * access functions
>>> +              */
>>> +             src = &entry->data[0];
>>> +             dest = new_entry->key;
>>> +             for (j = 0; j < key_length; j++)
>>> +                     *dest++ = get_unaligned(src++);
>>> +
>>> +             src = &entry->data[key_length+1];
>>> +             dest = new_entry->data;
>>> +             for (j = 0; j < data_length; j++)
>>> +                     *dest++ = get_unaligned(src++);
>>> +
>>> +             pr_debug("%s: Setting up Entry (%d) with key: %s, data length is %zu\n",
>>> +                     DRIVER_NAME, i, new_entry->key, new_entry->len);
>>> +
>>> +             /* call the framework provided function */
>>> +             ret = bootcache_add_entry(new_entry);
>>> +             if (ret) {
>>> +                     kfree(new_entry->key);
>>> +                     kfree(new_entry->data);
>>> +                     kfree(new_entry);
>>> +                     ret = 0;
>>> +             }
>>> +
>>> +             /* Sanity check we've got space for the next extry */
>>> +
>>> +             current_ptr += sizeof(struct cache_memory_store_entry) +
>>> +                     data_length + key_length + 1;
>>> +             if (current_ptr + sizeof(struct cache_memory_store_entry)
>>> +                             > max_address) {
>>> +                     ret = ret = -ENOMEM;
>>> +                     goto error;
>>> +             }
>>> +     }
>>> +
>>> +error:
>>> +     if (store)
>>> +             iounmap((void *)store);
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static struct bootcache_info cache_info = {
>>> +     .name = "memory",
>>> +     .load_cache = memory_backend_load_cache,
>>> +};
>>> +
>>> +static int bootcache_backend_probe(struct platform_device *pdev)
>>> +{
>>> +     int ret;
>>> +     size_t table_size;
>>> +     struct cache_memory_store *temp_store;
>>> +     struct device_node *reserved_mem_node;
>>> +
>>> +     /* Check for the front end being ready */
>>> +
>>> +     pr_debug("%s: %s\n", DRIVER_NAME, __func__);
>>> +
>>> +     reserved_mem_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
>>> +     if (reserved_mem_node) {
>>> +             rmem = of_reserved_mem_lookup(reserved_mem_node);
>>> +             of_node_put(reserved_mem_node);
>>> +     }
>>> +
>>> +     if (!rmem) {
>>> +             pr_err("%s: Failed to find reserved memory region.\n", DRIVER_NAME);
>>> +             return -ENOMEM;
>>> +     }
>>> +     pr_debug("%s: Found reserved cache memory block (%s):\n", DRIVER_NAME, rmem->name);
>>> +     pr_debug("%s:  Physical Address: 0x%llx\n", DRIVER_NAME, rmem->base);
>>> +     pr_debug("%s:  Size: 0x%llx (%llu bytes)\n", DRIVER_NAME, rmem->size,
>>> +             rmem->size);
>>> +
>>> +     if (rmem->size < BOOTCACHE_MINSIZE) {
>>> +             pr_err("%s: reserved memory too small (%llu bytes)\n", DRIVER_NAME, rmem->size);
>>> +             return -ENOMEM;
>>> +     }
>>> +
>>> +     ret = bootcache_register_backend(&cache_info);
>>> +
>>> +     if (ret < 0) {
>>> +             pr_err("%s: bootcache_register_backend() failed with error %d\n",
>>> +                     DRIVER_NAME, ret);
>>> +             return ret;
>>> +     }
>>> +     pr_info("%s: Backend loaded\n", DRIVER_NAME);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static const struct of_device_id bootcache_backend_driver_dt_ids[] = {
>>> +     { .compatible = "linux,backend-backend-memory", },
>>> +     { }
>>> +};
>>> +
>>> +static struct platform_driver bootcache_memory_platform_driver = {
>>> +     .probe          = bootcache_backend_probe,
>>> +     .driver         = {
>>> +             .name   = DRIVER_NAME,
>>> +             .of_match_table = of_match_ptr(bootcache_backend_driver_dt_ids),
>>> +     },
>>> +};
>>> +
>>> +static int __init bootcache_backend_init(void)
>>> +{
>>> +     return platform_driver_register(&bootcache_memory_platform_driver);
>>> +}
>>> +
>>> +core_initcall(bootcache_backend_init);
>>>
>>
>>
> 
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit acampanella-thegoodpenguin
@ 2025-09-29 23:38   ` Bird, Tim
  2025-09-30  8:24     ` Andrea Campanella
  0 siblings, 1 reply; 16+ messages in thread
From: Bird, Tim @ 2025-09-29 23:38 UTC (permalink / raw)
  To: acampanella-thegoodpenguin, linux-embedded@vger.kernel.org



> -----Original Message-----
> From: acampanella-thegoodpenguin <acampanella@thegoodpenguin.co.uk>
> 
> bootcache provides boot-time key-value cache to help improve
> boot performance by allowing drivers to cache expensive computations.
> 
> Simple API are provided:
> - bootcache_get_u16()/bootcache_set_u16() - retrieve/store u16 values
> - bootcache_get_u32()/bootcache_set_u32() - retrieve/store u32 values
> - bootcache_get_u64()/bootcache_set_u64() - retrieve/store u64 values
> - bootcache_get_string()/bootcache_set_string() - retrieve/store strings
> - bootcache_register_backend() - Backend registration
> - bootcache_add_entry() - Add cache entry into framework
> 
> Signed-off-by: Marc Kelly <mkelly@thegoodpenguin.co.uk>
> Signed-off-by: Andrea Campanella <acampanella@thegoodpenguin.co.uk>
> ---
>  MAINTAINERS               |   6 ++
>  drivers/base/Kconfig      |  11 +++
>  drivers/base/Makefile     |   1 +
>  drivers/base/bootcache.c  | 179 +++++++++++++++++++++++++++++++++++++
>  include/linux/bootcache.h | 219 ++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 416 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index daf520a13bdf6a991c0160a96620f40308c29ee0..4bf3766b30bbf75214911bd4ce5256a066f05726 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4418,6 +4418,12 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/iio/imu/bosch,bmi323.yaml
>  F:	drivers/iio/imu/bmi323/
> 
> +BOOT CACHE
> +M:	Andrea Campanella <acampanella@thegoodpenguin.co.uk>
> +S:	Maintained
> +F:	drivers/base/bootcache.c
> +F:	include/linux/bootcache.h
> +
>  BPF JIT for ARC
>  M:	Shahab Vahedi <list+bpf@vahedi.org>
>  L:	bpf@vger.kernel.org
> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> index 064eb52ff7e2d4d8745e9c39882b41dc4cf02a89..da02f95948d880da83c3025addc1e111dbce339a 100644
> --- a/drivers/base/Kconfig
> +++ b/drivers/base/Kconfig
> @@ -73,6 +73,17 @@ config DEVTMPFS_SAFE
>  	  with the PROT_EXEC flag. This can break, for example, non-KMS
>  	  video drivers.
> 
> +config BOOTCACHE
> +	bool "Boot-time cache for the kernel"
> +	help
> +	  Enable a simple key-value cache subsystem for storing boot-time
> +	  configuration data. This allows drivers and kernel subsystems to
> +	  cache expensive computations during boot, potentially improving
> +	  boot performance on subsequent reboots by avoiding redundant
> +	  hardware detection and initialization work
> +
> +	  If unsure, say N.
> +
>  config STANDALONE
>  	bool "Select only drivers that don't need compile-time external firmware"
>  	default y
> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> index 8074a10183dcb720a6b820b8476b230716b37f01..10a16e6c2ea1ad778fb7793583b9ee54d2498b2b 100644
> --- a/drivers/base/Makefile
> +++ b/drivers/base/Makefile
> @@ -8,6 +8,7 @@ obj-y			:= component.o core.o bus.o dd.o syscore.o \
>  			   topology.o container.o property.o cacheinfo.o \
>  			   swnode.o faux.o
>  obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
> +obj-$(CONFIG_BOOTCACHE)    += bootcache.o
>  obj-$(CONFIG_DEVTMPFS)	+= devtmpfs.o
>  obj-y			+= power/
>  obj-$(CONFIG_ISA_BUS_API)	+= isa.o
> diff --git a/drivers/base/bootcache.c b/drivers/base/bootcache.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d74ead796b0f50ca9a90e84e7230b9ad6ca896d8
> --- /dev/null
> +++ b/drivers/base/bootcache.c
> @@ -0,0 +1,179 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/hashtable.h>
> +#include <linux/string.h>
> +#include <linux/stringhash.h>
> +#include <linux/types.h>
> +#include <linux/spinlock.h>
> +#include <linux/errno.h>
> +#include <linux/kobject.h>
> +#include <linux/sysfs.h>
> +#include <linux/bootcache.h>
> +
> +static DEFINE_HASHTABLE(bootcache_table, BOOTCACHE_HASH_BITS);
> +static DEFINE_SPINLOCK(bootcache_lock);
> +static struct kobject *bootcache_kobj;
> +static struct bootcache_info *bootcache_backend;
> +static bool bootcache_initilized;
> +
> +int bootcache_register_backend(struct bootcache_info *bci)
> +{
> +	int ret;
> +
> +	if (!bci)
> +		return -EINVAL;
> +	if (!bci->name)
> +		return -EINVAL;
> +
> +	/* If we're not ready, tell backend to try again later */
> +	if (!bootcache_initilized)
> +		return -EPROBE_DEFER;
> +
> +	if (bootcache_backend) {
> +		pr_warn("bootcache: Backend '%s' is already registered, cannot register '%s'\n",
> +		bootcache_backend->name, bci->name);
> +		return -EBUSY;
> +	}
> +	pr_info("bootcache: Registering backend '%s'\n",
> +		bci->name);
> +
> +	/* Have the backend load and populate the cache store */
> +	ret = bci->load_cache();
> +
> +	if (ret)
> +		goto failed_initilize;
> +
> +	bootcache_backend = bci;
> +	return 0;
> +
> +failed_initilize:
> +	return ret;
> +}
> +EXPORT_SYMBOL(bootcache_register_backend);
> +
> +int bootcache_get(const char *name, void *buf, size_t *len)
> +{
> +	struct bootcache_entry *entry;
> +	u32 hash;
> +	int ret = -ENOENT;
> +
> +	if (!name || !buf || !len)
> +		return -EINVAL;
> +
> +	hash = full_name_hash(NULL, name, strlen(name));
> +
> +	spin_lock(&bootcache_lock);
> +	hash_for_each_possible(bootcache_table, entry, node, hash) {
> +		if (strcmp(entry->key, name) == 0) {
> +			if (*len < entry->len) {
> +				*len = entry->len;
> +				ret = -ENOSPC;
> +				goto unlock;
> +			}
> +			memcpy(buf, entry->data, entry->len);
> +			*len = entry->len;
> +			ret = 0;
> +			goto unlock;
> +		}
> +	}
> +
> +unlock:
> +	spin_unlock(&bootcache_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL(bootcache_get);
> +
> +int bootcache_add_entry(struct bootcache_entry *entry)
> +{
> +	u32 hash;
> +	struct bootcache_entry *existing_entry;
> +	int ret = 0;
> +
> +	hash = full_name_hash(NULL, entry->key, strlen(entry->key));
> +
> +	spin_lock(&bootcache_lock);
> +
> +	hash_for_each_possible(bootcache_table, existing_entry, node, hash) {
> +		if (strcmp(existing_entry->key, entry->key) == 0) {
> +			ret = -EEXIST;  // Key already exists
> +			goto unlock;
> +		}
> +	}
> +
> +	hash_add(bootcache_table, &entry->node, hash);
> +
> +unlock:
> +	spin_unlock(&bootcache_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL(bootcache_add_entry);
> +
> +int bootcache_set(const char *name, const void *data, size_t len)
> +{
> +	struct bootcache_entry *new_entry;
> +	u32 hash;
> +	int ret = 0;
> +
> +	if (!name || !data || !len)
> +		return -EINVAL;
> +
> +	new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
> +	if (!new_entry)
> +		return -ENOMEM;
> +
> +	new_entry->key = kstrdup(name, GFP_KERNEL);
> +	if (!new_entry->key) {
> +		ret = -ENOMEM;
> +		goto free;
> +	}
> +
> +	new_entry->data = kmemdup(data, len, GFP_KERNEL);
> +	if (!new_entry->data) {
> +		ret = -ENOMEM;
> +		goto free;
> +	}
> +
> +	new_entry->len = len;
> +	ret = bootcache_add_entry(new_entry);
> +	if (!ret)
> +		return 0;
> +
> +free:
> +	kfree(new_entry->data);
> +	kfree(new_entry->key);
> +	kfree(new_entry);
> +	return ret;
> +}
> +EXPORT_SYMBOL(bootcache_set);
> +
> +static ssize_t writeout_store(struct kobject *kobj, struct kobj_attribute *attr,
> +			    const char *buf, size_t count)
> +{
> +	/*Implement persistent storage backend */
> +	return count;
> +}
> +
> +static struct kobj_attribute writeout_attr = __ATTR_WO(writeout);
> +
> +static int __init bootcache_init(void)
> +{
> +	int ret;
> +
> +	pr_info("bootcache: backend loaded\n");
> +
> +	/* Create /sys/kernel/bootcache/writeout */
> +	bootcache_kobj = kobject_create_and_add("bootcache", kernel_kobj);
> +	if (!bootcache_kobj)
> +		return -ENOMEM;
> +
> +	ret = sysfs_create_file(bootcache_kobj, &writeout_attr.attr);
> +	if (ret) {
> +		kobject_put(bootcache_kobj);
> +		return ret;
> +	}
> +	bootcache_initilized = true;
> +	return 0;
> +}
> +core_initcall(bootcache_init);
> diff --git a/include/linux/bootcache.h b/include/linux/bootcache.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..52c07cf09bb87fc6c305485e5409bd235ede4e6e
> --- /dev/null
> +++ b/include/linux/bootcache.h
> @@ -0,0 +1,219 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _LINUX_BOOTCACHE_H
> +#define _LINUX_BOOTCACHE_H
> +
> +#include <linux/types.h>
> +
> +#ifdef CONFIG_BOOTCACHE
> +
> +#define BOOTCACHE_HASH_BITS 6  /* 64 buckets */
> +
> +struct bootcache_entry {
> +	struct hlist_node node;
> +	char *key;
> +	void *data;
> +	size_t len;
> +};
> +
> +/**
> + * struct bootcache_info - Structure for registering a boot cache backend.
> + *
> + * @name:   The name of the backend.
> + *
> + * Callbacks:
> + * @load_cache: Callback function to read and populate the framework from the cache.
> + */
> +
> +struct bootcache_info {
> +	const char *name;
> +	/* Callback Function Pointers */
> +	int (*load_cache)(void);
> +};
> +
> +/**
> + * bootcache_add_entry - Add an entry directly into the hash table
> + * @entry: bootcache_entry structure
> + *
> + * Returns: 0 on success, entry was added, do not free it.
> + *          1 on success but an existing entry was updated,
> + *            free to deallocate entry.
> + */
> +int bootcache_add_entry(struct bootcache_entry *entry);
> +
> +/**
> + * bootcache_register_backend - Register a backend provider with the framework
> + * @bci: bootcache_info structure
> + *
> + * Returns: 0 on success, -EPROBE_DEFER if the frontend is not ready,
> + *          -EBUSY if another backend is already registered,
> + *          -EINVAL on invalid registration information.
> + */
> +int bootcache_register_backend(struct bootcache_info *bci);
> +
> +/**
> + * bootcache_get - Retrieve arbitrary data from the cache
> + * @name: Key to look up
> + * @buf: Buffer to store retrieved data
> + * @len: On input, size of buffer; on output, actual data size
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters,
> + *          -ENOENT if not found, -ENOSPC if buffer too small
> + */
> +int bootcache_get(const char *name, void *buf, size_t *len);
> +
> +/**
> + * bootcache_set - Store arbitrary data in the cache
> + * @name: Key to store under
> + * @data: Data to store
> + * @len: Length of data
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +int bootcache_set(const char *name, const void *data, size_t len);
> +
> +#else /* !CONFIG_BOOTCACHE */
> +
> +static inline int bootcache_get(const char *name, void *buf, size_t *len)
> +{
> +	return -ENOENT;
> +}
> +
> +static inline int bootcache_set(const char *name, const void *data, size_t len)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +/**
> + * bootcache_get_u16 - Retrieve a u16 value from the cache
> + * @name: Key to look up
> + * @out_val: Pointer to store the retrieved value
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOENT if not found
> + */
> +static inline int bootcache_get_u16(const char *name, u16 *out_val)
> +{
> +	size_t len = sizeof(u16);
> +
> +	if (IS_ENABLED(CONFIG_BOOTCACHE))
This seems redundant given we're in a #else /* !CONFIG_BOOTCACHE */ conditional section
How could IS_ENABLED() return true?  (did I miss an #endif somewhere?)

> +		return bootcache_get(name, out_val, &len);
> +	else
> +		return -ENOENT;

Come to think of it, why are these specialized calls in a !CONFIG_BOOTCACHE conditional section?
If the base call 'bootcache_get' is already conditional, and turns into a 'return -ENOENT', then
this call (bootcache_get_u16()), will turn into 'return -ENOENT' also, which will cause the whole
call chain to evaporate at the callsite (due to the conditional there on '==0')

I'm not sure any of these specialized calls need to be in conditional sections - just the base
ones.

> +}
> +
> +/**
> + * bootcache_set_u16 - Store a u16 value in the cache
> + * @name: Key to store under
> + * @val: Value to store
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +static inline int bootcache_set_u16(const char *name, u16 val)
> +{
> +	if (IS_ENABLED(CONFIG_BOOTCACHE))
> +		return bootcache_set(name, &val, sizeof(u16));
> +	else
> +		return -EOPNOTSUPP;
> +}
> +
> +/**
> + * bootcache_get_u32 - Retrieve a u32 value from the cache
> + * @name: Key to look up
> + * @out_val: Pointer to store the retrieved value
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOENT if not found
> + */
> +static inline int bootcache_get_u32(const char *name, u32 *out_val)
> +{
> +	size_t len = sizeof(u32);
> +
> +	if (IS_ENABLED(CONFIG_BOOTCACHE))
> +		return bootcache_get(name, out_val, &len);
> +	else
> +		return -ENOENT;
> +}
> +
> +/**
> + * bootcache_set_u32 - Store a u32 value in the cache
> + * @name: Key to store under
> + * @val: Value to store
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +static inline int bootcache_set_u32(const char *name, u32 val)
> +{
> +	if (IS_ENABLED(CONFIG_BOOTCACHE))
> +		return bootcache_set(name, &val, sizeof(u32));
> +	else
> +		return -EOPNOTSUPP;
> +}
> +
> +/**
> + * bootcache_get_u64 - Retrieve a u64 value from the cache
> + * @name: Key to look up
> + * @out_val: Pointer to store the retrieved value
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOENT if not found
> + */
> +static inline int bootcache_get_u64(const char *name, u64 *out_val)
> +{
> +	size_t len = sizeof(u64);
> +
> +	if (IS_ENABLED(CONFIG_BOOTCACHE))
> +		return bootcache_get(name, out_val, &len);
> +	else
> +		return -ENOENT;
> +}
> +
> +/**
> + * bootcache_set_u64 - Store a u64 value in the cache
> + * @name: Key to store under
> + * @val: Value to store
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +static inline int bootcache_set_u64(const char *name, u64 val)
> +{
> +	if (IS_ENABLED(CONFIG_BOOTCACHE))
> +		return bootcache_set(name, &val, sizeof(u64));
> +	else
> +		return -EOPNOTSUPP;
> +}
> +
> +/**
> + * bootcache_get_string - Retrieve a string from the cache
> + * @name: Key to look up
> + * @buf: Buffer to store retrieved string
> + * @buflen: Size of buffer
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters,
> + *          -ENOENT if not found, -ENOSPC if buffer too small
> + */
> +static inline int bootcache_get_string(const char *name, char *buf, size_t buflen)
> +{
> +	size_t len = buflen;
> +
> +	if (IS_ENABLED(CONFIG_BOOTCACHE))
> +		return bootcache_get(name, buf, &len);
> +	else
> +		return -ENOENT;
> +}
> +
> +/**
> + * bootcache_set_string - Store a string in the cache
> + * @name: Key to store under
> + * @str: Null-terminated string to store
> + *
> + * Returns: 0 on success, -EINVAL for invalid parameters, -ENOMEM on allocation failure
> + */
> +static inline int bootcache_set_string(const char *name, const char *str)
> +{
> +	if (IS_ENABLED(CONFIG_BOOTCACHE)) {
> +		if (!str)
> +			return -EINVAL;
> +		return bootcache_set(name, str, strlen(str) + 1);
> +	} else {
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +#endif /* _LINUX_BOOTCACHE_H */
> 
> --
> 2.48.1
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm acampanella-thegoodpenguin
@ 2025-09-29 23:48   ` Bird, Tim
  2025-09-30 11:37     ` Andrew Murray
  0 siblings, 1 reply; 16+ messages in thread
From: Bird, Tim @ 2025-09-29 23:48 UTC (permalink / raw)
  To: acampanella-thegoodpenguin, linux-embedded@vger.kernel.org

> -----Original Message-----
> From: acampanella-thegoodpenguin <acampanella@thegoodpenguin.co.uk>
> Sent: Tuesday, September 23, 2025 8:24 AM
> To: linux-embedded@vger.kernel.org
> Subject: [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm
> 
> From: Andrew Murray <amurray@ thegoodpenguin. co. uk> During boot xor_blocks may determine the fastest xor algorithm by using
> do_xor_speed to perform a speed test on available algorithms. This process can increase the overall boot time. Let's
> From: Andrew Murray <amurray@thegoodpenguin.co.uk>
> 
> During boot xor_blocks may determine the fastest xor algorithm
> by using do_xor_speed to perform a speed test on available
> algorithms. This process can increase the overall boot time.

It would be good to mention the amount of time we're talking about here.
It won't be the same value for all platforms, but you could mention the
amount of time this takes on the platform you're working on (or on a particularly
slow machine, if you want, just to highlight where it might be a problem
for some situations but not all.)
> 
> Let's make use of bootcache to cache the result of the speed
> test for subsequent boots.
> 
> Signed-off-by: Andrew Murray <amurray@thegoodpenguin.co.uk>
> ---
>  crypto/xor.c | 29 ++++++++++++++++++++++++++++-
>  1 file changed, 28 insertions(+), 1 deletion(-)
> 
> diff --git a/crypto/xor.c b/crypto/xor.c
> index f39621a57bb33c4015c06dff00e03a07716618f6..3457df0414064758a1923752e91642d2237af7b3 100644
> --- a/crypto/xor.c
> +++ b/crypto/xor.c
> @@ -14,6 +14,7 @@
>  #include <linux/raid/xor.h>
>  #include <linux/jiffies.h>
>  #include <linux/preempt.h>
> +#include <linux/bootcache.h>
>  #include <asm/xor.h>
> 
>  #ifndef XOR_SELECT_TEMPLATE
> @@ -54,13 +55,13 @@ EXPORT_SYMBOL(xor_blocks);
>  /* Set of all registered templates.  */
>  static struct xor_block_template *__initdata template_list;
> 
> -#ifndef MODULE
>  static void __init do_xor_register(struct xor_block_template *tmpl)
>  {
>  	tmpl->next = template_list;
>  	template_list = tmpl;
>  }
> 
> +#ifndef MODULE
>  static int __init register_xor_blocks(void)
>  {
>  	active_template = XOR_SELECT_TEMPLATE(NULL);
> @@ -79,6 +80,21 @@ static int __init register_xor_blocks(void)
>  #define BENCH_SIZE	4096
>  #define REPS		800U
> 
> +static struct xor_block_template * __init
> +xor_get_template_by_name(char *fastest_name)
> +{
> +	struct xor_block_template *f;
> +
> +#define xor_speed	do_xor_register
> +	// build a list of templates
> +	XOR_TRY_TEMPLATES;
> +#undef xor_speed
> +	for (f = template_list; f; f = f->next)
> +		if (!strcmp(f->name, fastest_name))
> +			return f;
> +	return NULL;
> +}
> +
>  static void __init
>  do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)
>  {
> @@ -117,9 +133,18 @@ calibrate_xor_blocks(void)
>  {
>  	void *b1, *b2;
>  	struct xor_block_template *f, *fastest;
> +	char cached_name[32];
> +	int ret;
> 
>  	fastest = XOR_SELECT_TEMPLATE(NULL);
> 
> +	if (!fastest) {
> +		ret = bootcache_get_string("xor_blocks_fastest",
> +				cached_name, sizeof(cached_name));
> +		if (!ret)
> +			fastest = xor_get_template_by_name(cached_name);

I presume that if CONFIG_BOOTCACHE is not defined, then ret ends up being -ENOENT
always, which makes this whole block completely evaporate. Is that right?
In that case, do you end up with a warning about unused variable cached_name?
Or does the compiler know not to complain about that?  Do you still end up with
space reserved on the stack?

> +	}
> +
>  	if (fastest) {
>  		printk(KERN_INFO "xor: automatically using best "
>  				 "checksumming function   %-10s\n",
> @@ -149,6 +174,8 @@ calibrate_xor_blocks(void)
>  		if (f->speed > fastest->speed)
>  			fastest = f;
> 
> +	bootcache_set_string("xor_blocks_fastest", fastest->name);
> +
>  	pr_info("xor: using function: %s (%d MB/sec)\n",
>  	       fastest->name, fastest->speed);
> 
> 
> --
> 2.48.1
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit
  2025-09-29 23:38   ` Bird, Tim
@ 2025-09-30  8:24     ` Andrea Campanella
  0 siblings, 0 replies; 16+ messages in thread
From: Andrea Campanella @ 2025-09-30  8:24 UTC (permalink / raw)
  To: Bird, Tim, linux-embedded@vger.kernel.org


On 9/30/25 00:38, Bird, Tim wrote:
>
>
> This seems redundant given we're in a #else /* !CONFIG_BOOTCACHE */ conditional section
> How could IS_ENABLED() return true?  (did I miss an #endif somewhere?)
>
>> +		return bootcache_get(name, out_val, &len);
>> +	else
>> +		return -ENOENT;
> Come to think of it, why are these specialized calls in a !CONFIG_BOOTCACHE conditional section?
> If the base call 'bootcache_get' is already conditional, and turns into a 'return -ENOENT', then
> this call (bootcache_get_u16()), will turn into 'return -ENOENT' also, which will cause the whole
> call chain to evaporate at the callsite (due to the conditional there on '==0')
>
> I'm not sure any of these specialized calls need to be in conditional sections - just the base
> ones.
>
Hi Tim,

Thanks for your feedback, good spot on that conditional, I will get it 
fixed in the last patch!



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm
  2025-09-29 23:48   ` Bird, Tim
@ 2025-09-30 11:37     ` Andrew Murray
  0 siblings, 0 replies; 16+ messages in thread
From: Andrew Murray @ 2025-09-30 11:37 UTC (permalink / raw)
  To: Bird, Tim; +Cc: acampanella-thegoodpenguin, linux-embedded@vger.kernel.org

On Tue, 30 Sept 2025 at 00:48, Bird, Tim <Tim.Bird@sony.com> wrote:
>
> > -----Original Message-----
> > From: acampanella-thegoodpenguin <acampanella@thegoodpenguin.co.uk>
> > Sent: Tuesday, September 23, 2025 8:24 AM
> > To: linux-embedded@vger.kernel.org
> > Subject: [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm
> >
> > From: Andrew Murray <amurray@ thegoodpenguin. co. uk> During boot xor_blocks may determine the fastest xor algorithm by using
> > do_xor_speed to perform a speed test on available algorithms. This process can increase the overall boot time. Let's
> > From: Andrew Murray <amurray@thegoodpenguin.co.uk>
> >
> > During boot xor_blocks may determine the fastest xor algorithm
> > by using do_xor_speed to perform a speed test on available
> > algorithms. This process can increase the overall boot time.
>
> It would be good to mention the amount of time we're talking about here.
> It won't be the same value for all platforms, but you could mention the
> amount of time this takes on the platform you're working on (or on a particularly
> slow machine, if you want, just to highlight where it might be a problem
> for some situations but not all.)

Good idea, thanks!

Though, it may be that a whole bunch of patches like this are
accumulated, you boot the kernel with them enabled, blindly cache all
the results (for reuse in subsequent boots), and gain a quicker boot.
I.e. it may not be necessary for users to pick and choose.


> >
> > Let's make use of bootcache to cache the result of the speed
> > test for subsequent boots.
> >
> > Signed-off-by: Andrew Murray <amurray@thegoodpenguin.co.uk>
> > ---
> >  crypto/xor.c | 29 ++++++++++++++++++++++++++++-
> >  1 file changed, 28 insertions(+), 1 deletion(-)
> >
> > diff --git a/crypto/xor.c b/crypto/xor.c
> > index f39621a57bb33c4015c06dff00e03a07716618f6..3457df0414064758a1923752e91642d2237af7b3 100644
> > --- a/crypto/xor.c
> > +++ b/crypto/xor.c
> > @@ -14,6 +14,7 @@
> >  #include <linux/raid/xor.h>
> >  #include <linux/jiffies.h>
> >  #include <linux/preempt.h>
> > +#include <linux/bootcache.h>
> >  #include <asm/xor.h>
> >
> >  #ifndef XOR_SELECT_TEMPLATE
> > @@ -54,13 +55,13 @@ EXPORT_SYMBOL(xor_blocks);
> >  /* Set of all registered templates.  */
> >  static struct xor_block_template *__initdata template_list;
> >
> > -#ifndef MODULE
> >  static void __init do_xor_register(struct xor_block_template *tmpl)
> >  {
> >       tmpl->next = template_list;
> >       template_list = tmpl;
> >  }
> >
> > +#ifndef MODULE
> >  static int __init register_xor_blocks(void)
> >  {
> >       active_template = XOR_SELECT_TEMPLATE(NULL);
> > @@ -79,6 +80,21 @@ static int __init register_xor_blocks(void)
> >  #define BENCH_SIZE   4096
> >  #define REPS         800U
> >
> > +static struct xor_block_template * __init
> > +xor_get_template_by_name(char *fastest_name)
> > +{
> > +     struct xor_block_template *f;
> > +
> > +#define xor_speed    do_xor_register
> > +     // build a list of templates
> > +     XOR_TRY_TEMPLATES;
> > +#undef xor_speed
> > +     for (f = template_list; f; f = f->next)
> > +             if (!strcmp(f->name, fastest_name))
> > +                     return f;
> > +     return NULL;
> > +}
> > +
> >  static void __init
> >  do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)
> >  {
> > @@ -117,9 +133,18 @@ calibrate_xor_blocks(void)
> >  {
> >       void *b1, *b2;
> >       struct xor_block_template *f, *fastest;
> > +     char cached_name[32];
> > +     int ret;
> >
> >       fastest = XOR_SELECT_TEMPLATE(NULL);
> >
> > +     if (!fastest) {
> > +             ret = bootcache_get_string("xor_blocks_fastest",
> > +                             cached_name, sizeof(cached_name));
> > +             if (!ret)
> > +                     fastest = xor_get_template_by_name(cached_name);
>
> I presume that if CONFIG_BOOTCACHE is not defined, then ret ends up being -ENOENT
> always, which makes this whole block completely evaporate. Is that right?
> In that case, do you end up with a warning about unused variable cached_name?
> Or does the compiler know not to complain about that?  Do you still end up with
> space reserved on the stack?

Good spot, I'm not sure the answer but i'll find out.

Thanks,

Andrew Murray


>
> > +     }
> > +
> >       if (fastest) {
> >               printk(KERN_INFO "xor: automatically using best "
> >                                "checksumming function   %-10s\n",
> > @@ -149,6 +174,8 @@ calibrate_xor_blocks(void)
> >               if (f->speed > fastest->speed)
> >                       fastest = f;
> >
> > +     bootcache_set_string("xor_blocks_fastest", fastest->name);
> > +
> >       pr_info("xor: using function: %s (%d MB/sec)\n",
> >              fastest->name, fastest->speed);
> >
> >
> > --
> > 2.48.1
> >
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH PREVIEW RFC 0/6] Add support for boot-time caching
  2025-09-23 14:23 [PATCH PREVIEW RFC 0/6] Add support for boot-time caching acampanella-thegoodpenguin
                   ` (5 preceding siblings ...)
  2025-09-23 14:23 ` [PATCH PREVIEW RFC 6/6] dt-bindings: bootcache: Add bindings for bootcache backend acampanella-thegoodpenguin
@ 2025-09-30 12:49 ` Rob Landley
  6 siblings, 0 replies; 16+ messages in thread
From: Rob Landley @ 2025-09-30 12:49 UTC (permalink / raw)
  To: acampanella-thegoodpenguin, linux-embedded

On 9/23/25 09:23, acampanella-thegoodpenguin wrote:
> This patch series provides a simple key-value cache for storing boot-time
> configuration data. The goal is to allow kernel components and drivers to
> cache time-consuming-to-compute values during boot, enabling faster
> subsequent boots by avoiding redundant initialization work.

Does the vmlinux build use --gc-sections yet so this sort of thing can 
drop out when it's not needed? (Marking it _init still eats early boot 
and flash space.)

The kernel build is a giant complicated thing using linker scripts so I 
don't want to blindly throw -ffunction-sections -fdata-sections in there 
but this plumbing strikes me as something that will penalize systems 
that _don't_ calculate expensive things and refer to them multiple times 
(or would rather eat the CPU than eat the RAM to do it). And adding more 
kconfig nonsense to have the build manually pull it in seems silly when 
the tools have been able to do this automatically for over 20 years...

Rob

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2025-09-30 12:49 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-23 14:23 [PATCH PREVIEW RFC 0/6] Add support for boot-time caching acampanella-thegoodpenguin
2025-09-23 14:23 ` [PATCH PREVIEW RFC 1/6] base: bootcache: initial commit acampanella-thegoodpenguin
2025-09-29 23:38   ` Bird, Tim
2025-09-30  8:24     ` Andrea Campanella
2025-09-23 14:23 ` [PATCH PREVIEW RFC 2/6] raid6: Add bootcache acampanella-thegoodpenguin
2025-09-23 14:23 ` [PATCH PREVIEW RFC 3/6] crypto: use bootcache to cache fastest algorithm acampanella-thegoodpenguin
2025-09-29 23:48   ` Bird, Tim
2025-09-30 11:37     ` Andrew Murray
2025-09-23 14:23 ` [PATCH PREVIEW RFC 4/6] base: bootcache: Add bootcache test backend acampanella-thegoodpenguin
2025-09-23 14:23 ` [PATCH PREVIEW RFC 5/6] base: bootcache: Add bootcache memory backend acampanella-thegoodpenguin
2025-09-24 14:42   ` Dan Scally
2025-09-24 15:13     ` Andrea Campanella
2025-09-26 17:34     ` Marc Kelly
2025-09-26 20:09       ` Dan Scally
2025-09-23 14:23 ` [PATCH PREVIEW RFC 6/6] dt-bindings: bootcache: Add bindings for bootcache backend acampanella-thegoodpenguin
2025-09-30 12:49 ` [PATCH PREVIEW RFC 0/6] Add support for boot-time caching Rob Landley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).