blob: e348f5647fbdcc028305039b3fef86d961260555 [file] [log] [blame]
Phillip Lougher0d455c12013-11-13 02:04:19 +00001/*
2 * Copyright (c) 2013
3 * Phillip Lougher <phillip@squashfs.org.uk>
4 *
5 * This work is licensed under the terms of the GNU GPL, version 2. See
6 * the COPYING file in the top-level directory.
7 */
8
9#include <linux/kernel.h>
10#include <linux/slab.h>
11#include <linux/pagemap.h>
Adrien Schildknecht38840af2016-09-28 13:59:18 -070012#include <linux/buffer_head.h>
Phillip Lougher0d455c12013-11-13 02:04:19 +000013#include "page_actor.h"
14
Adrien Schildknecht38840af2016-09-28 13:59:18 -070015struct squashfs_page_actor *squashfs_page_actor_init(struct page **page,
16 int pages, int length, void (*release_pages)(struct page **, int, int))
Phillip Lougher0d455c12013-11-13 02:04:19 +000017{
18 struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
19
20 if (actor == NULL)
21 return NULL;
22
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +030023 actor->length = length ? : pages * PAGE_SIZE;
Phillip Lougher0d455c12013-11-13 02:04:19 +000024 actor->page = page;
25 actor->pages = pages;
26 actor->next_page = 0;
27 actor->pageaddr = NULL;
Adrien Schildknecht38840af2016-09-28 13:59:18 -070028 actor->release_pages = release_pages;
Phillip Lougher0d455c12013-11-13 02:04:19 +000029 return actor;
30}
Adrien Schildknecht38840af2016-09-28 13:59:18 -070031
32void squashfs_page_actor_free(struct squashfs_page_actor *actor, int error)
33{
34 if (!actor)
35 return;
36
37 if (actor->release_pages)
38 actor->release_pages(actor->page, actor->pages, error);
39 kfree(actor);
40}
41
42void squashfs_actor_to_buf(struct squashfs_page_actor *actor, void *buf,
43 int length)
44{
45 void *pageaddr;
46 int pos = 0, avail, i;
47
48 for (i = 0; i < actor->pages && pos < length; ++i) {
49 avail = min_t(int, length - pos, PAGE_SIZE);
50 if (actor->page[i]) {
51 pageaddr = kmap_atomic(actor->page[i]);
52 memcpy(buf + pos, pageaddr, avail);
53 kunmap_atomic(pageaddr);
54 }
55 pos += avail;
56 }
57}
58
59void squashfs_buf_to_actor(void *buf, struct squashfs_page_actor *actor,
60 int length)
61{
62 void *pageaddr;
63 int pos = 0, avail, i;
64
65 for (i = 0; i < actor->pages && pos < length; ++i) {
66 avail = min_t(int, length - pos, PAGE_SIZE);
67 if (actor->page[i]) {
68 pageaddr = kmap_atomic(actor->page[i]);
69 memcpy(pageaddr, buf + pos, avail);
70 kunmap_atomic(pageaddr);
71 }
72 pos += avail;
73 }
74}
75
76void squashfs_bh_to_actor(struct buffer_head **bh, int nr_buffers,
77 struct squashfs_page_actor *actor, int offset, int length, int blksz)
78{
79 void *kaddr = NULL;
80 int bytes = 0, pgoff = 0, b = 0, p = 0, avail, i;
81
82 while (bytes < length) {
83 if (actor->page[p]) {
84 kaddr = kmap_atomic(actor->page[p]);
85 while (pgoff < PAGE_SIZE && bytes < length) {
86 avail = min_t(int, blksz - offset,
87 PAGE_SIZE - pgoff);
88 memcpy(kaddr + pgoff, bh[b]->b_data + offset,
89 avail);
90 pgoff += avail;
91 bytes += avail;
92 offset = (offset + avail) % blksz;
93 if (!offset) {
94 put_bh(bh[b]);
95 ++b;
96 }
97 }
98 kunmap_atomic(kaddr);
99 pgoff = 0;
100 } else {
101 for (i = 0; i < PAGE_SIZE / blksz; ++i) {
102 if (bh[b])
103 put_bh(bh[b]);
104 ++b;
105 }
106 bytes += PAGE_SIZE;
107 }
108 ++p;
109 }
110}
111
112void squashfs_bh_to_buf(struct buffer_head **bh, int nr_buffers, void *buf,
113 int offset, int length, int blksz)
114{
115 int i, avail, bytes = 0;
116
117 for (i = 0; i < nr_buffers && bytes < length; ++i) {
118 avail = min_t(int, length - bytes, blksz - offset);
119 if (bh[i]) {
120 memcpy(buf + bytes, bh[i]->b_data + offset, avail);
121 put_bh(bh[i]);
122 }
123 bytes += avail;
124 offset = 0;
125 }
126}
127
128void free_page_array(struct page **page, int nr_pages)
129{
130 int i;
131
132 for (i = 0; i < nr_pages; ++i)
133 __free_page(page[i]);
134 kfree(page);
135}
136
137struct page **alloc_page_array(int nr_pages, int gfp_mask)
138{
139 int i;
140 struct page **page;
141
142 page = kcalloc(nr_pages, sizeof(struct page *), gfp_mask);
143 if (!page)
144 return NULL;
145 for (i = 0; i < nr_pages; ++i) {
146 page[i] = alloc_page(gfp_mask);
147 if (!page[i]) {
148 free_page_array(page, i);
149 return NULL;
150 }
151 }
152 return page;
153}