[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[MINI-OS PATCH 13/19] kexec: add support for allocating pages from kexec module memory



Add the needed functions for allocating and freeing memory pages of
the kexec module.

As the pages are always related to a kexec module record, add the
related utility functions, too. For now only support adding records
and retrieving them.

Signed-off-by: Juergen Gross <jgross@xxxxxxxx>
---
 arch/x86/kexec.c |  16 +++++++
 include/kexec.h  |  52 +++++++++++++++++++++
 kexec.c          | 115 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 183 insertions(+)

diff --git a/arch/x86/kexec.c b/arch/x86/kexec.c
index 7fb98473..8b3e841a 100644
--- a/arch/x86/kexec.c
+++ b/arch/x86/kexec.c
@@ -238,6 +238,9 @@ static void get_mod_addr(unsigned long from, unsigned long 
to)
 #define min(a, b) ((a) < (b) ? (a) : (b))
 void kexec_module(unsigned long start_pfn, unsigned long max_pfn)
 {
+    unsigned int i;
+    char *rec_end;
+
     /* Reuse already existing kexec module. */
     mod_ptr = kexec_check_module();
     if ( !mod_ptr && CONFIG_KEXEC_MODULE_PAGES )
@@ -255,11 +258,24 @@ void kexec_module(unsigned long start_pfn, unsigned long 
max_pfn)
                sizeof(mod_ptr->eye_catcher));
         mod_ptr->n_pages = CONFIG_KEXEC_MODULE_PAGES - 1;
         memset(mod_ptr->pg2rec, KEXECMOD_PG_FREE, mod_ptr->n_pages);
+        mod_ptr->n_records = 16;
         mod_ptr->recs_off = sizeof(struct kexec_module) +
                             CONFIG_KEXEC_MODULE_PAGES + (mod_ptr->n_pages & 1);
 
         set_reserved_range(kexec_mod_start, (unsigned long)mod_ptr + 
PAGE_SIZE);
     }
+
+    mod_recs = (void *)((unsigned long)mod_ptr + mod_ptr->recs_off);
+    mod_rec_start = (char *)(mod_recs + mod_ptr->n_records);
+    mod_rec_end = mod_rec_start;
+    for ( i = 0; i < mod_ptr->n_records; i++ )
+    {
+        if ( mod_recs[i].type == KEXECMOD_REC_NONE )
+            continue;
+        rec_end = (char *)mod_ptr + mod_recs[i].offset + mod_recs[i].size;
+        if ( mod_rec_end < rec_end )
+            mod_rec_end = rec_end;
+    }
 }
 
 void kexec_set_param_loc(const char *cmdline)
diff --git a/include/kexec.h b/include/kexec.h
index 0200005f..1e753ee1 100644
--- a/include/kexec.h
+++ b/include/kexec.h
@@ -43,6 +43,9 @@ struct kexec_module_rec {
 
 extern unsigned long kexec_mod_start;
 extern struct kexec_module *mod_ptr;
+extern struct kexec_module_rec *mod_recs;
+extern char *mod_rec_start;
+extern char *mod_rec_end;
 
 /* One element of kexec actions (last element must have action KEXEC_CALL): */
 struct kexec_action {
@@ -56,6 +59,17 @@ struct kexec_action {
     void *src;
 };
 
+#ifdef CONFIG_KEXEC
+unsigned long kexec_alloc_mod_pages(unsigned int recid, unsigned int n);
+void kexec_free_mod_pages(unsigned int recid, unsigned long addr,
+                          unsigned int n);
+int kexec_find_mod_record(unsigned int start_idx, unsigned int type,
+                          unsigned int *size);
+int kexec_add_mod_record(unsigned int type, void *addr, unsigned int size);
+int kexec_upd_mod_record(unsigned int idx, unsigned int type,
+                         void *addr, unsigned int size);
+int kexec_read_mod_record(unsigned int idx, void *addr, unsigned int size);
+
 #define KEXEC_MAX_ACTIONS  16
 
 extern char _kexec_start[], _kexec_end[];
@@ -105,4 +119,42 @@ void kexec_move_used_pages_undo(void);
 /* Check for kexec module and create kexec memory if needed. */
 void kexec_module(unsigned long start_pfn, unsigned long max_pfn);
 
+#else /* CONFIG_KEXEC */
+static inline unsigned long kexec_alloc_mod_pages(unsigned int recid,
+                                                  unsigned int n)
+{
+    return 0;
+}
+
+static inline void kexec_free_mod_pages(unsigned int recid, unsigned long addr,
+                                        unsigned int n)
+{
+}
+
+static inline int kexec_find_mod_record(unsigned int start_idx,
+                                        unsigned int type, unsigned int *size)
+{
+    return 0;
+}
+
+static inline int kexec_add_mod_record(unsigned int type, void *addr,
+                                       unsigned int size)
+{
+    return 0;
+}
+
+static inline int kexec_upd_mod_record(unsigned int idx, unsigned int type,
+                                       void *addr, unsigned int size)
+{
+    return 0;
+}
+
+static inline int kexec_read_mod_record(unsigned int idx, void *addr,
+                                        unsigned int size)
+{
+    return 0;
+}
+
+#endif
+
 #endif /* _KEXEC_H */
diff --git a/kexec.c b/kexec.c
index ded29882..f17ed13d 100644
--- a/kexec.c
+++ b/kexec.c
@@ -253,3 +253,118 @@ int kexec_add_action(int action, void *dest, void *src, 
unsigned int len)
 
 unsigned long kexec_mod_start;
 struct kexec_module *mod_ptr;
+struct kexec_module_rec *mod_recs;
+char *mod_rec_start;
+char *mod_rec_end;
+
+unsigned long kexec_alloc_mod_pages(unsigned int recid, unsigned int n)
+{
+    unsigned int first = 0, i;
+
+    for ( i = 0; i < mod_ptr->n_pages; i++ )
+    {
+        if ( i - first == n - 1 )
+        {
+            for ( i = 0; i < n; i++ )
+                mod_ptr->pg2rec[first + i] = recid;
+
+            return kexec_mod_start + PFN_PHYS(first);
+        }
+
+        if ( mod_ptr->pg2rec[i] != KEXECMOD_PG_FREE )
+            first = i + 1;
+    }
+
+    printk("Kexec module out of memory\n");
+    BUG();
+}
+
+void kexec_free_mod_pages(unsigned int recid, unsigned long addr,
+                          unsigned int n)
+{
+    unsigned int s = PHYS_PFN(addr - kexec_mod_start);
+    unsigned int i;
+
+    BUG_ON(addr < kexec_mod_start ||
+           addr + PFN_PHYS(n) > (unsigned long)mod_ptr);
+
+    for ( i = 0; i < n; i++ )
+    {
+        BUG_ON(mod_ptr->pg2rec[s + i] != recid);
+        mod_ptr->pg2rec[s + i] = KEXECMOD_PG_FREE;
+    }
+}
+
+int kexec_find_mod_record(unsigned int start_idx, unsigned int type,
+                          unsigned int *size)
+{
+    unsigned int i;
+
+    for ( i = start_idx; i < mod_ptr->n_records; i++ )
+    {
+        if ( mod_recs[i].type == type )
+        {
+            *size = mod_recs[i].size;
+            return i;
+        }
+    }
+
+    return -ENOENT;
+}
+
+int kexec_add_mod_record(unsigned int type, void *addr, unsigned int size)
+{
+    unsigned int i;
+
+    if ( mod_rec_end + size > (char *)mod_ptr + PAGE_SIZE )
+    {
+        /* TODO: support compressing record space. */
+        printk("Kexec module record space exhausted\n");
+        BUG();
+    }
+
+    for ( i = 0; i < mod_ptr->n_records; i++ )
+    {
+        if ( mod_recs[i].type == KEXECMOD_REC_NONE )
+        {
+            mod_recs[i].offset = mod_rec_end - (char *)mod_ptr;
+            mod_recs[i].type = type;
+            mod_recs[i].size = size;
+            memcpy(mod_rec_end, addr, size);
+            mod_rec_end = mod_rec_end + size;
+
+            return i;
+        }
+    }
+
+    /* TODO: support extending the mod_recs[] table. */
+    printk("Kexec module record table exhausted\n");
+    BUG();
+}
+
+int kexec_upd_mod_record(unsigned int idx, unsigned int type,
+                         void *addr, unsigned int size)
+{
+    if ( idx >= mod_ptr->n_records )
+        return -ENOENT;
+
+    if ( mod_recs[idx].type != type || mod_recs[idx].size != size )
+        return -EINVAL;
+
+    memcpy((char *)mod_ptr + mod_recs[idx].offset, addr, size);
+
+    return 0;
+}
+
+int kexec_read_mod_record(unsigned int idx, void *addr, unsigned int size)
+{
+    if ( idx >= mod_ptr->n_records )
+        return -ENOENT;
+
+    if ( mod_recs[idx].size != size )
+        return -EINVAL;
+
+    memcpy(addr, (char *)mod_ptr + mod_recs[idx].offset, size);
+
+    return 0;
+}
-- 
2.43.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.