For that purpose, the pkram super
block pfn is exported via /sys/kernel/pkram. If none is passed, any
preserved memory will not be kept, and a new super block will be
allocated.
Originally-by: Vladimir Davydov <vdavydov.dev@xxxxxxxxx>
Signed-off-by: Anthony Yznaga <anthony.yznaga@xxxxxxxxxx>
---
mm/pkram.c | 102
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 100 insertions(+), 2 deletions(-)
diff --git a/mm/pkram.c b/mm/pkram.c
index da166cb6afb7..c66b2ae4d520 100644
--- a/mm/pkram.c
+++ b/mm/pkram.c
@@ -5,15 +5,18 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
+#include <linux/pfn.h>
#include <linux/pkram.h>
#include <linux/reboot.h>
#include <linux/sched.h>
#include <linux/string.h>
+#include <linux/sysfs.h>
#include <linux/types.h>
#include "internal.h"
@@ -82,12 +85,38 @@ struct pkram_node {
#define PKRAM_ACCMODE_MASK 3
/*
+ * The PKRAM super block contains data needed to restore the
preserved memory
+ * structure on boot. The pointer to it (pfn) should be passed
via the 'pkram'
+ * boot param if one wants to restore preserved data saved by the
previously
+ * executing kernel. For that purpose the kernel exports the pfn via
+ * /sys/kernel/pkram. If none is passed, preserved memory if any
will not be
+ * preserved and a new clean page will be allocated for the super
block.
+ *
+ * The structure occupies a memory page.
+ */
+struct pkram_super_block {
+ __u64 node_pfn; /* first element of the node list */
+};
+
+static unsigned long pkram_sb_pfn __initdata;
+static struct pkram_super_block *pkram_sb;
+
+/*
* For convenience sake PKRAM nodes are kept in an auxiliary
doubly-linked list
* connected through the lru field of the page struct.
*/
static LIST_HEAD(pkram_nodes); /* linked through page::lru */
static DEFINE_MUTEX(pkram_mutex); /* serializes open/close */
+/*
+ * The PKRAM super block pfn, see above.
+ */
+static int __init parse_pkram_sb_pfn(char *arg)
+{
+ return kstrtoul(arg, 16, &pkram_sb_pfn);
+}
+early_param("pkram", parse_pkram_sb_pfn);
+
static inline struct page *pkram_alloc_page(gfp_t gfp_mask)
{
return alloc_page(gfp_mask);
@@ -270,6 +299,7 @@ static void pkram_stream_init(struct
pkram_stream *ps,
* @gfp_mask specifies the memory allocation mask to be used when
saving data.
*
* Error values:
+ * %ENODEV: PKRAM not available
* %ENAMETOOLONG: name len >= PKRAM_NAME_MAX
* %ENOMEM: insufficient memory available
* %EEXIST: node with specified name already exists
@@ -285,6 +315,9 @@ int pkram_prepare_save(struct pkram_stream
*ps, const char *name, gfp_t gfp_mask
struct pkram_node *node;
int err = 0;
+ if (!pkram_sb)
+ return -ENODEV;
+
if (strlen(name) >= PKRAM_NAME_MAX)
return -ENAMETOOLONG;
@@ -404,6 +437,7 @@ void pkram_discard_save(struct pkram_stream *ps)
* Returns 0 on success, -errno on failure.
*
* Error values:
+ * %ENODEV: PKRAM not available
* %ENOENT: node with specified name does not exist
* %EBUSY: save to required node has not finished yet
*
@@ -414,6 +448,9 @@ int pkram_prepare_load(struct pkram_stream
*ps, const char *name)
struct pkram_node *node;
int err = 0;
+ if (!pkram_sb)
+ return -ENODEV;
+
mutex_lock(&pkram_mutex);
node = pkram_find_node(name);
if (!node) {
@@ -825,6 +862,13 @@ static void __pkram_reboot(void)
node->node_pfn = node_pfn;
node_pfn = page_to_pfn(page);
}
+
+ /*
+ * Zero out pkram_sb completely since it may have been passed from
+ * the previous boot.
+ */
+ memset(pkram_sb, 0, PAGE_SIZE);
+ pkram_sb->node_pfn = node_pfn;
}
static int pkram_reboot(struct notifier_block *notifier,
@@ -832,7 +876,8 @@ static int pkram_reboot(struct notifier_block
*notifier,
{
if (val != SYS_RESTART)
return NOTIFY_DONE;
- __pkram_reboot();
+ if (pkram_sb)
+ __pkram_reboot();
return NOTIFY_OK;
}
@@ -840,9 +885,62 @@ static int pkram_reboot(struct notifier_block
*notifier,
.notifier_call = pkram_reboot,
};
+static ssize_t show_pkram_sb_pfn(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ unsigned long pfn = pkram_sb ? PFN_DOWN(__pa(pkram_sb)) : 0;
+
+ return sprintf(buf, "%lx\n", pfn);
+}
+
+static struct kobj_attribute pkram_sb_pfn_attr =
+ __ATTR(pkram, 0444, show_pkram_sb_pfn, NULL);
+
+static struct attribute *pkram_attrs[] = {
+ &pkram_sb_pfn_attr.attr,
+ NULL,
+};
+
+static struct attribute_group pkram_attr_group = {
+ .attrs = pkram_attrs,
+};
+
+/* returns non-zero on success */
+static int __init pkram_init_sb(void)
+{
+ unsigned long pfn;
+ struct pkram_node *node;
+
+ if (!pkram_sb) {
+ struct page *page;
+
+ page = pkram_alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!page) {
+ pr_err("PKRAM: Failed to allocate super block\n");
+ return 0;
+ }
+ pkram_sb = page_address(page);
+ }
+
+ /*
+ * Build auxiliary doubly-linked list of nodes connected through
+ * page::lru for convenience sake.
+ */
+ pfn = pkram_sb->node_pfn;
+ while (pfn) {
+ node = pfn_to_kaddr(pfn);
+ pkram_insert_node(node);
+ pfn = node->node_pfn;
+ }
+ return 1;
+}
+
static int __init pkram_init(void)
{
- register_reboot_notifier(&pkram_reboot_notifier);
+ if (pkram_init_sb()) {
+ register_reboot_notifier(&pkram_reboot_notifier);
+ sysfs_update_group(kernel_kobj, &pkram_attr_group);
+ }
return 0;
}
module_init(pkram_init);
--
1.9.4
_______________________________________________
kexec mailing list
kexec@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/kexec