This patch adds ability to save arbitrary byte strings to PRAM using
pram_write() to be restored later using pram_read(). These two
operations are implemented on top of pram_save_page() and
pram_load_page() respectively.
---
 include/linux/pram.h |    4 +++
 mm/pram.c            |   86 ++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 88 insertions(+), 2 deletions(-)

diff --git a/include/linux/pram.h b/include/linux/pram.h
index dd17316..61c536c 100644
--- a/include/linux/pram.h
+++ b/include/linux/pram.h
@@ -13,6 +13,10 @@ struct pram_stream {
        struct pram_node *node;
        struct pram_link *link;         /* current link */
        unsigned int page_index;        /* next page index in link */
+
+       /* byte-stream specific */
+       struct page *data_page;
+       unsigned int data_offset;
 };
 
 #define PRAM_NAME_MAX          256     /* including nul */
diff --git a/mm/pram.c b/mm/pram.c
index a443eb0..f7eebe1 100644
--- a/mm/pram.c
+++ b/mm/pram.c
@@ -1,5 +1,6 @@
 #include <linux/err.h>
 #include <linux/gfp.h>
+#include <linux/highmem.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/mm.h>
@@ -46,6 +47,7 @@ struct pram_link {
 struct pram_node {
        __u32   flags;          /* see PRAM_* flags below */
        __u32   type;           /* data type, see enum pram_stream_type */
+       __u64   data_len;       /* data size, only for byte streams */
        __u64   link_pfn;       /* points to the first link of the node */
 
        __u8    name[PRAM_NAME_MAX];
@@ -284,6 +286,9 @@ void pram_finish_load(struct pram_stream *ps)
 
        BUG_ON((node->flags & PRAM_ACCMODE_MASK) != PRAM_LOAD);
 
+       if (ps->data_page)
+               put_page(ps->data_page);
+
        pram_truncate_node(node);
        pram_free_page(node);
 }
@@ -422,10 +427,51 @@ struct page *pram_load_page(struct pram_stream *ps, int 
*flags)
  *
  * On success, returns the number of bytes written, which is always equal to
  * @count. On failure, -errno is returned.
+ *
+ * Error values:
+ *    %ENOMEM: insufficient amount of memory available
  */
 ssize_t pram_write(struct pram_stream *ps, const void *buf, size_t count)
 {
-       return -ENOSYS;
+       void *addr;
+       size_t copy_count, write_count = 0;
+       struct pram_node *node = ps->node;
+
+       BUG_ON(node->type != PRAM_BYTE_STREAM);
+       BUG_ON((node->flags & PRAM_ACCMODE_MASK) != PRAM_SAVE);
+
+       while (count > 0) {
+               if (!ps->data_page) {
+                       struct page *page;
+                       int err;
+
+                       page = pram_alloc_page((ps->gfp_mask & 
GFP_RECLAIM_MASK) |
+                                              __GFP_HIGHMEM | __GFP_ZERO);
+                       if (!page)
+                               return -ENOMEM;
+                       err = __pram_save_page(ps, page, 0);
+                       put_page(page);
+                       if (err)
+                               return err;
+                       ps->data_page = page;
+                       ps->data_offset = 0;
+               }
+
+               copy_count = min_t(size_t, count, PAGE_SIZE - ps->data_offset);
+               addr = kmap_atomic(ps->data_page);
+               memcpy(addr + ps->data_offset, buf, copy_count);
+               kunmap_atomic(addr);
+
+               buf += copy_count;
+               node->data_len += copy_count;
+               ps->data_offset += copy_count;
+               if (ps->data_offset >= PAGE_SIZE)
+                       ps->data_page = NULL;
+
+               write_count += copy_count;
+               count -= copy_count;
+       }
+       return write_count;
 }
 
 /**
@@ -437,5 +483,41 @@ ssize_t pram_write(struct pram_stream *ps, const void 
*buf, size_t count)
  */
 size_t pram_read(struct pram_stream *ps, void *buf, size_t count)
 {
-       return 0;
+       char *addr;
+       size_t copy_count, read_count = 0;
+       struct pram_node *node = ps->node;
+
+       BUG_ON(node->type != PRAM_BYTE_STREAM);
+       BUG_ON((node->flags & PRAM_ACCMODE_MASK) != PRAM_LOAD);
+
+       while (count > 0 && node->data_len > 0) {
+               if (!ps->data_page) {
+                       struct page *page;
+
+                       page = __pram_load_page(ps, NULL);
+                       if (!page)
+                               break;
+                       ps->data_page = page;
+                       ps->data_offset = 0;
+               }
+
+               copy_count = min_t(size_t, count, PAGE_SIZE - ps->data_offset);
+               if (copy_count > node->data_len)
+                       copy_count = node->data_len;
+               addr = kmap_atomic(ps->data_page);
+               memcpy(buf, addr + ps->data_offset, copy_count);
+               kunmap_atomic(addr);
+
+               buf += copy_count;
+               node->data_len -= copy_count;
+               ps->data_offset += copy_count;
+               if (ps->data_offset >= PAGE_SIZE || !node->data_len) {
+                       put_page(ps->data_page);
+                       ps->data_page = NULL;
+               }
+
+               read_count += copy_count;
+               count -= copy_count;
+       }
+       return read_count;
 }
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to