lkml.org 
[lkml]   [2020]   [May]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC 02/43] mm: PKRAM: implement node load and save functions
    Date
    Preserved memory is divided into nodes which can be saved and loaded
    independently of each other. PKRAM nodes are kept on a list and
    identified by unique names. Whenever a save operation is initiated by
    calling pkram_prepare_save(), a new node is created and linked to the
    list. When the save operation has been committed by calling
    pkram_finish_save(), the node becomes loadable. A load operation can be
    then initiated by calling pkram_prepare_load() which deletes the node
    from the list and prepares the corresponding stream for loading data
    from it. After the load has been finished, the pkram_finish_load()
    function must be called to free the node. Nodes are also deleted when a
    save operation is discarded, i.e. pkram_discard_save() is called instead
    of pkram_finish_save().

    Originally-by: Vladimir Davydov <vdavydov.dev@gmail.com>
    Signed-off-by: Anthony Yznaga <anthony.yznaga@oracle.com>
    ---
    include/linux/pkram.h | 7 ++-
    mm/pkram.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++--
    2 files changed, 149 insertions(+), 6 deletions(-)

    diff --git a/include/linux/pkram.h b/include/linux/pkram.h
    index 4c4e13311ec8..83a0579e4c1c 100644
    --- a/include/linux/pkram.h
    +++ b/include/linux/pkram.h
    @@ -6,7 +6,12 @@
    #include <linux/types.h>
    #include <linux/mm_types.h>

    -struct pkram_stream;
    +struct pkram_node;
    +
    +struct pkram_stream {
    + gfp_t gfp_mask;
    + struct pkram_node *node;
    +};

    #define PKRAM_NAME_MAX 256 /* including nul */

    diff --git a/mm/pkram.c b/mm/pkram.c
    index d6f2f79d4852..5c57126353ff 100644
    --- a/mm/pkram.c
    +++ b/mm/pkram.c
    @@ -2,16 +2,85 @@
    #include <linux/err.h>
    #include <linux/gfp.h>
    #include <linux/kernel.h>
    +#include <linux/list.h>
    #include <linux/mm.h>
    +#include <linux/mutex.h>
    #include <linux/pkram.h>
    +#include <linux/string.h>
    #include <linux/types.h>

    +/*
    + * Preserved memory is divided into nodes that can be saved or loaded
    + * independently of each other. The nodes are identified by unique name
    + * strings.
    + *
    + * The structure occupies a memory page.
    + */
    +struct pkram_node {
    + __u32 flags;
    +
    + __u8 name[PKRAM_NAME_MAX];
    +};
    +
    +#define PKRAM_SAVE 1
    +#define PKRAM_LOAD 2
    +#define PKRAM_ACCMODE_MASK 3
    +
    +static LIST_HEAD(pkram_nodes); /* linked through page::lru */
    +static DEFINE_MUTEX(pkram_mutex); /* serializes open/close */
    +
    +static inline struct page *pkram_alloc_page(gfp_t gfp_mask)
    +{
    + return alloc_page(gfp_mask);
    +}
    +
    +static inline void pkram_free_page(void *addr)
    +{
    + free_page((unsigned long)addr);
    +}
    +
    +static inline void pkram_insert_node(struct pkram_node *node)
    +{
    + list_add(&virt_to_page(node)->lru, &pkram_nodes);
    +}
    +
    +static inline void pkram_delete_node(struct pkram_node *node)
    +{
    + list_del(&virt_to_page(node)->lru);
    +}
    +
    +static struct pkram_node *pkram_find_node(const char *name)
    +{
    + struct page *page;
    + struct pkram_node *node;
    +
    + list_for_each_entry(page, &pkram_nodes, lru) {
    + node = page_address(page);
    + if (strcmp(node->name, name) == 0)
    + return node;
    + }
    + return NULL;
    +}
    +
    +static void pkram_stream_init(struct pkram_stream *ps,
    + struct pkram_node *node, gfp_t gfp_mask)
    +{
    + memset(ps, 0, sizeof(*ps));
    + ps->gfp_mask = gfp_mask;
    + ps->node = node;
    +}
    +
    /**
    * Create a preserved memory node with name @name and initialize stream @ps
    * for saving data to it.
    *
    * @gfp_mask specifies the memory allocation mask to be used when saving data.
    *
    + * Error values:
    + * %ENAMETOOLONG: name len >= PKRAM_NAME_MAX
    + * %ENOMEM: insufficient memory available
    + * %EEXIST: node with specified name already exists
    + *
    * Returns 0 on success, -errno on failure.
    *
    * After the save has finished, pkram_finish_save() (or pkram_discard_save() in
    @@ -19,7 +88,34 @@
    */
    int pkram_prepare_save(struct pkram_stream *ps, const char *name, gfp_t gfp_mask)
    {
    - return -ENOSYS;
    + struct page *page;
    + struct pkram_node *node;
    + int err = 0;
    +
    + if (strlen(name) >= PKRAM_NAME_MAX)
    + return -ENAMETOOLONG;
    +
    + page = pkram_alloc_page(GFP_KERNEL | __GFP_ZERO);
    + if (!page)
    + return -ENOMEM;
    + node = page_address(page);
    +
    + node->flags = PKRAM_SAVE;
    + strcpy(node->name, name);
    +
    + mutex_lock(&pkram_mutex);
    + if (!pkram_find_node(name))
    + pkram_insert_node(node);
    + else
    + err = -EEXIST;
    + mutex_unlock(&pkram_mutex);
    + if (err) {
    + __free_page(page);
    + return err;
    + }
    +
    + pkram_stream_init(ps, node, gfp_mask);
    + return 0;
    }

    /**
    @@ -50,7 +146,12 @@ void pkram_finish_save_obj(struct pkram_stream *ps)
    */
    void pkram_finish_save(struct pkram_stream *ps)
    {
    - BUG();
    + struct pkram_node *node = ps->node;
    +
    + BUG_ON((node->flags & PKRAM_ACCMODE_MASK) != PKRAM_SAVE);
    +
    + smp_wmb();
    + node->flags &= ~PKRAM_ACCMODE_MASK;
    }

    /**
    @@ -60,7 +161,15 @@ void pkram_finish_save(struct pkram_stream *ps)
    */
    void pkram_discard_save(struct pkram_stream *ps)
    {
    - BUG();
    + struct pkram_node *node = ps->node;
    +
    + BUG_ON((node->flags & PKRAM_ACCMODE_MASK) != PKRAM_SAVE);
    +
    + mutex_lock(&pkram_mutex);
    + pkram_delete_node(node);
    + mutex_unlock(&pkram_mutex);
    +
    + pkram_free_page(node);
    }

    /**
    @@ -69,11 +178,36 @@ void pkram_discard_save(struct pkram_stream *ps)
    *
    * Returns 0 on success, -errno on failure.
    *
    + * Error values:
    + * %ENOENT: node with specified name does not exist
    + * %EBUSY: save to required node has not finished yet
    + *
    * After the load has finished, pkram_finish_load() is to be called.
    */
    int pkram_prepare_load(struct pkram_stream *ps, const char *name)
    {
    - return -ENOSYS;
    + struct pkram_node *node;
    + int err = 0;
    +
    + mutex_lock(&pkram_mutex);
    + node = pkram_find_node(name);
    + if (!node) {
    + err = -ENOENT;
    + goto out_unlock;
    + }
    + if (node->flags & PKRAM_ACCMODE_MASK) {
    + err = -EBUSY;
    + goto out_unlock;
    + }
    + pkram_delete_node(node);
    +out_unlock:
    + mutex_unlock(&pkram_mutex);
    + if (err)
    + return err;
    +
    + node->flags |= PKRAM_LOAD;
    + pkram_stream_init(ps, node, 0);
    + return 0;
    }

    /**
    @@ -106,7 +240,11 @@ void pkram_finish_load_obj(struct pkram_stream *ps)
    */
    void pkram_finish_load(struct pkram_stream *ps)
    {
    - BUG();
    + struct pkram_node *node = ps->node;
    +
    + BUG_ON((node->flags & PKRAM_ACCMODE_MASK) != PKRAM_LOAD);
    +
    + pkram_free_page(node);
    }

    /**
    --
    2.13.3
    \
     
     \ /
      Last update: 2020-05-07 02:45    [W:4.383 / U:0.592 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site