All of lore.kernel.org
 help / color / mirror / Atom feed
From: madvenka@linux.microsoft.com
To: gregkh@linuxfoundation.org, pbonzini@redhat.com, rppt@kernel.org,
	jgowans@amazon.com, graf@amazon.de, arnd@arndb.de,
	keescook@chromium.org, stanislav.kinsburskii@gmail.com,
	anthony.yznaga@oracle.com, linux-mm@kvack.org,
	linux-kernel@vger.kernel.org, madvenka@linux.microsoft.com,
	jamorris@linux.microsoft.com
Subject: [RFC PATCH v1 07/10] mm/prmem: Implement named Persistent Instances.
Date: Mon, 16 Oct 2023 18:32:12 -0500	[thread overview]
Message-ID: <20231016233215.13090-8-madvenka@linux.microsoft.com> (raw)
In-Reply-To: <20231016233215.13090-1-madvenka@linux.microsoft.com>

From: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>

To persist any data, a consumer needs to do the following:

	- create a persistent instance for it. The instance gets recorded
	  in the metadata.

	- Name the instance.

	- Record the instance data in the instance.

	- Retrieve the instance by name after kexec.

	- Retrieve instance data.

Implement the following API for consumers:

prmem_get(subsystem, name, create)

	Get/Create a persistent instance. The consumer provides the name
	of the subsystem and the name of the instance within the subsystem.
	E.g., for a persistent ramdisk block device:
		subsystem = "ramdisk"
		instance  = "pram0"

prmem_set_data()

	Record a data pointer and a size for the instance. An instance may
	contain many data structures connected to each other using pointers,
	etc. A consumer is expected to record the top level data structure
	in the instance. All other data structures must be reachable from
	the top level data structure.

prmem_get_data()

	Retrieve the data pointer and the size for the instance.

prmem_put()

	Destroy a persistent instance. The instance data must be NULL at
	this point. So, the consumer is responsible for freeing the
	instance data and setting it to NULL in the instance prior to
	destroying.

prmem_list()

	Walk the instances of a subsystem and call a callback for each.
	This allows a consumer to enumerate all of the instances associated
	with a subsystem.

Signed-off-by: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
---
 include/linux/prmem.h         |  36 +++++++++
 kernel/prmem/Makefile         |   2 +-
 kernel/prmem/prmem_init.c     |   1 +
 kernel/prmem/prmem_instance.c | 139 ++++++++++++++++++++++++++++++++++
 4 files changed, 177 insertions(+), 1 deletion(-)
 create mode 100644 kernel/prmem/prmem_instance.c

diff --git a/include/linux/prmem.h b/include/linux/prmem.h
index 1cb4660cf35e..c7034690f7cb 100644
--- a/include/linux/prmem.h
+++ b/include/linux/prmem.h
@@ -50,6 +50,28 @@ struct prmem_region {
 	struct gen_pool_chunk	*chunk;
 };
 
+#define PRMEM_MAX_NAME		32
+
+/*
+ * To persist any data, a persistent instance is created for it and the data is
+ * "remembered" in the instance.
+ *
+ * node		List node
+ * subsystem	Subsystem/driver/module that created the instance. E.g.,
+ *		"ramdisk" for the ramdisk driver.
+ * name		Instance name within the subsystem/driver/module. E.g., "pram0"
+ *		for a persistent ramdisk instance.
+ * data		Pointer to data. E.g., the radix tree of pages in a ram disk.
+ * size		Size of data.
+ */
+struct prmem_instance {
+	struct list_head	node;
+	char			subsystem[PRMEM_MAX_NAME];
+	char			name[PRMEM_MAX_NAME];
+	void			*data;
+	size_t			size;
+};
+
 #define PRMEM_MAX_CACHES	14
 
 /*
@@ -63,6 +85,8 @@ struct prmem_region {
  *
  * regions	List of memory regions.
  *
+ * instances	Persistent instances.
+ *
  * caches	Caches for different object sizes. For allocations smaller than
  *		PAGE_SIZE, these caches are used.
  */
@@ -74,6 +98,9 @@ struct prmem {
 	/* Persistent Regions. */
 	struct list_head	regions;
 
+	/* Persistent Instances. */
+	struct list_head	instances;
+
 	/* Allocation caches. */
 	void			*caches[PRMEM_MAX_CACHES];
 };
@@ -85,6 +112,8 @@ extern size_t			prmem_size;
 extern bool			prmem_inited;
 extern spinlock_t		prmem_lock;
 
+typedef int (*prmem_list_func_t)(struct prmem_instance *instance, void *arg);
+
 /* Kernel API. */
 void prmem_reserve_early(void);
 void prmem_reserve(void);
@@ -98,6 +127,13 @@ void prmem_free_pages(struct page *pages, unsigned int order);
 void *prmem_alloc(size_t size, gfp_t gfp);
 void prmem_free(void *va, size_t size);
 
+/* Persistent Instance API. */
+void *prmem_get(char *subsystem, char *name, bool create);
+void prmem_set_data(struct prmem_instance *instance, void *data, size_t size);
+void prmem_get_data(struct prmem_instance *instance, void **data, size_t *size);
+bool prmem_put(struct prmem_instance *instance);
+int prmem_list(char *subsystem, prmem_list_func_t func, void *arg);
+
 /* Internal functions. */
 struct prmem_region *prmem_add_region(unsigned long pa, size_t size);
 bool prmem_create_pool(struct prmem_region *region, bool new_region);
diff --git a/kernel/prmem/Makefile b/kernel/prmem/Makefile
index 99bb19f0afd3..0ed7976580d6 100644
--- a/kernel/prmem/Makefile
+++ b/kernel/prmem/Makefile
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-y += prmem_parse.o prmem_reserve.o prmem_init.o prmem_region.o prmem_misc.o
-obj-y += prmem_allocator.o
+obj-y += prmem_allocator.o prmem_instance.o
diff --git a/kernel/prmem/prmem_init.c b/kernel/prmem/prmem_init.c
index d23833d296fe..166fca688ab3 100644
--- a/kernel/prmem/prmem_init.c
+++ b/kernel/prmem/prmem_init.c
@@ -21,6 +21,7 @@ void __init prmem_init(void)
 		prmem->metadata = prmem_metadata;
 		prmem->size = prmem_size;
 		INIT_LIST_HEAD(&prmem->regions);
+		INIT_LIST_HEAD(&prmem->instances);
 
 		if (!prmem_add_region(prmem_pa, prmem_size))
 			return;
diff --git a/kernel/prmem/prmem_instance.c b/kernel/prmem/prmem_instance.c
new file mode 100644
index 000000000000..ee3554d0ab8b
--- /dev/null
+++ b/kernel/prmem/prmem_instance.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Persistent-Across-Kexec memory (prmem) - Persistent instances.
+ *
+ * Copyright (C) 2023 Microsoft Corporation
+ * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com)
+ */
+#include <linux/prmem.h>
+
+static struct prmem_instance *prmem_find(char *subsystem, char *name)
+{
+	struct prmem_instance	*instance;
+
+	list_for_each_entry(instance, &prmem->instances, node) {
+		if (!strcmp(instance->subsystem, subsystem) &&
+		    !strcmp(instance->name, name)) {
+			return instance;
+		}
+	}
+	return NULL;
+}
+
+void *prmem_get(char *subsystem, char *name, bool create)
+{
+	int			subsystem_len = strlen(subsystem);
+	int			name_len = strlen(name);
+	struct prmem_instance	*instance;
+
+	/*
+	 * In early boot, you are allowed to get an existing instance. But
+	 * you are not allowed to create one until prmem is fully initialized.
+	 */
+	if (!prmem || (!prmem_inited && create))
+		return NULL;
+
+	if (!subsystem_len || subsystem_len >= PRMEM_MAX_NAME ||
+	    !name_len || name_len >= PRMEM_MAX_NAME) {
+		return NULL;
+	}
+
+	spin_lock(&prmem_lock);
+
+	/* Check if it already exists. */
+	instance = prmem_find(subsystem, name);
+	if (instance || !create)
+		goto unlock;
+
+	instance = prmem_alloc_locked(sizeof(*instance));
+	if (!instance)
+		goto unlock;
+
+	strcpy(instance->subsystem, subsystem);
+	strcpy(instance->name, name);
+	instance->data = NULL;
+	instance->size = 0;
+
+	list_add_tail(&instance->node, &prmem->instances);
+unlock:
+	spin_unlock(&prmem_lock);
+	return instance;
+}
+EXPORT_SYMBOL_GPL(prmem_get);
+
+void prmem_set_data(struct prmem_instance *instance, void *data, size_t size)
+{
+	if (!prmem_inited)
+		return;
+
+	spin_lock(&prmem_lock);
+	instance->data = data;
+	instance->size = size;
+	spin_unlock(&prmem_lock);
+}
+EXPORT_SYMBOL_GPL(prmem_set_data);
+
+void prmem_get_data(struct prmem_instance *instance, void **data, size_t *size)
+{
+	if (!prmem)
+		return;
+
+	spin_lock(&prmem_lock);
+	*data = instance->data;
+	*size = instance->size;
+	spin_unlock(&prmem_lock);
+}
+EXPORT_SYMBOL_GPL(prmem_get_data);
+
+bool prmem_put(struct prmem_instance *instance)
+{
+	if (!prmem_inited)
+		return true;
+
+	spin_lock(&prmem_lock);
+
+	if (instance->data) {
+		/*
+		 * Caller is responsible for freeing instance data and setting
+		 * it to NULL.
+		 */
+		spin_unlock(&prmem_lock);
+		return false;
+	}
+
+	/* Free instance. */
+	list_del(&instance->node);
+	prmem_free_locked(instance, sizeof(*instance));
+
+	spin_unlock(&prmem_lock);
+	return true;
+}
+EXPORT_SYMBOL_GPL(prmem_put);
+
+int prmem_list(char *subsystem, prmem_list_func_t func, void *arg)
+{
+	int			subsystem_len = strlen(subsystem);
+	struct prmem_instance	*instance;
+	int			ret;
+
+	if (!prmem)
+		return 0;
+
+	if (!subsystem_len || subsystem_len >= PRMEM_MAX_NAME)
+		return -EINVAL;
+
+	spin_lock(&prmem_lock);
+
+	list_for_each_entry(instance, &prmem->instances, node) {
+		if (strcmp(instance->subsystem, subsystem))
+			continue;
+
+		ret = func(instance, arg);
+		if (ret)
+			break;
+	}
+
+	spin_unlock(&prmem_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(prmem_list);
-- 
2.25.1


  parent reply	other threads:[~2023-10-16 23:32 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <1b1bc25eb87355b91fcde1de7c2f93f38abb2bf9>
2023-10-16 23:32 ` [RFC PATCH v1 00/10] mm/prmem: Implement the Persistent-Across-Kexec memory feature (prmem) madvenka
2023-10-16 23:32   ` [RFC PATCH v1 01/10] mm/prmem: Allocate memory during boot for storing persistent data madvenka
2023-10-17 18:36     ` kernel test robot
2023-10-16 23:32   ` [RFC PATCH v1 02/10] mm/prmem: Reserve metadata and persistent regions in early boot after kexec madvenka
2023-10-17 19:29     ` kernel test robot
2023-10-16 23:32   ` [RFC PATCH v1 03/10] mm/prmem: Manage persistent memory with the gen pool allocator madvenka
2023-10-16 23:32   ` [RFC PATCH v1 04/10] mm/prmem: Implement a page allocator for persistent memory madvenka
2023-10-16 23:32   ` [RFC PATCH v1 05/10] mm/prmem: Implement a buffer " madvenka
2023-10-16 23:32   ` [RFC PATCH v1 06/10] mm/prmem: Implement persistent XArray (and Radix Tree) madvenka
2023-10-16 23:32   ` madvenka [this message]
2023-10-16 23:32   ` [RFC PATCH v1 08/10] mm/prmem: Implement Persistent Ramdisk instances madvenka
2023-10-17 16:39     ` kernel test robot
2023-10-16 23:32   ` [RFC PATCH v1 09/10] mm/prmem: Implement DAX support for Persistent Ramdisks madvenka
2023-10-16 23:32   ` [RFC PATCH v1 10/10] mm/prmem: Implement dynamic expansion of prmem madvenka
2023-10-17  8:31   ` [RFC PATCH v1 00/10] mm/prmem: Implement the Persistent-Across-Kexec memory feature (prmem) Alexander Graf
2023-10-17 18:08     ` Madhavan T. Venkataraman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231016233215.13090-8-madvenka@linux.microsoft.com \
    --to=madvenka@linux.microsoft.com \
    --cc=anthony.yznaga@oracle.com \
    --cc=arnd@arndb.de \
    --cc=graf@amazon.de \
    --cc=gregkh@linuxfoundation.org \
    --cc=jamorris@linux.microsoft.com \
    --cc=jgowans@amazon.com \
    --cc=keescook@chromium.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=pbonzini@redhat.com \
    --cc=rppt@kernel.org \
    --cc=stanislav.kinsburskii@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.