[PATCH RFC 04/13] mm: PRAM: implement byte stream operations

From: Vladimir Davydov
Date: Mon Jul 01 2013 - 08:14:30 EST


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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/