Skip to content

Commit f268eed

Browse files
plougherakpm00
authored andcommitted
squashfs: extend "page actor" to handle missing pages
Patch series "Squashfs: handle missing pages decompressing into page cache". This patchset enables Squashfs to handle missing pages when directly decompressing datablocks into the page cache. Previously if the full set of pages needed was not available, Squashfs would have to fall back to using an intermediate buffer (the older method), which is slower, involving a memcopy, and it introduces contention on a shared buffer. The first patch extends the "page actor" code to handle missing pages. The second patch updates Squashfs_readpage_block() to use the new functionality, and removes the code that falls back to using an intermediate buffer. This patchset is independent of the readahead work, and it is standalone. It can be merged on its own. But the readahead patch for efficiency also needs this patch-set. This patch (of 2): This patch extends the "page actor" code to handle missing pages. Previously if the full set of pages needed to decompress a Squashfs datablock was unavailable, this would cause decompression to fail on the missing pages. In this case direct decompression into the page cache could not be achieved and the code would fall back to using the older intermediate buffer method. With this patch, direct decompression into the page cache can be achieved with missing pages. For "multi-shot" decompressors (zlib, xz, zstd), the page actor will allocate a temporary buffer which is passed to the decompressor, and then freed by the page actor. For "single shot" decompressors (lz4, lzo) which decompress into a contiguous "bounce buffer", and which is then copied into the page cache, it would be pointless to allocate a temporary buffer, memcpy into it, and then free it. For these decompressors -ENOMEM is returned, which signifies that the memcpy for that page should be skipped. This also happens if the data block is uncompressed. Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Phillip Lougher <[email protected]> Cc: Matthew Wilcox (Oracle) <[email protected]> Cc: Hsin-Yi Wang <[email protected]> Cc: Xiongwei Song <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 0aed472 commit f268eed

File tree

10 files changed

+126
-31
lines changed

10 files changed

+126
-31
lines changed

fs/squashfs/block.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@ static int copy_bio_to_actor(struct bio *bio,
3434
struct squashfs_page_actor *actor,
3535
int offset, int req_length)
3636
{
37-
void *actor_addr = squashfs_first_page(actor);
37+
void *actor_addr;
3838
struct bvec_iter_all iter_all = {};
3939
struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
4040
int copied_bytes = 0;
4141
int actor_offset = 0;
4242

43+
squashfs_actor_nobuff(actor);
44+
actor_addr = squashfs_first_page(actor);
45+
4346
if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all)))
4447
return 0;
4548

@@ -49,8 +52,9 @@ static int copy_bio_to_actor(struct bio *bio,
4952

5053
bytes_to_copy = min_t(int, bytes_to_copy,
5154
req_length - copied_bytes);
52-
memcpy(actor_addr + actor_offset, bvec_virt(bvec) + offset,
53-
bytes_to_copy);
55+
if (!IS_ERR(actor_addr))
56+
memcpy(actor_addr + actor_offset, bvec_virt(bvec) +
57+
offset, bytes_to_copy);
5458

5559
actor_offset += bytes_to_copy;
5660
copied_bytes += bytes_to_copy;

fs/squashfs/decompressor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct squashfs_decompressor {
2020
struct bio *, int, int, struct squashfs_page_actor *);
2121
int id;
2222
char *name;
23+
int alloc_buffer;
2324
int supported;
2425
};
2526

fs/squashfs/file_direct.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,6 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
4747
if (page == NULL)
4848
return res;
4949

50-
/*
51-
* Create a "page actor" which will kmap and kunmap the
52-
* page cache pages appropriately within the decompressor
53-
*/
54-
actor = squashfs_page_actor_init_special(page, pages, 0);
55-
if (actor == NULL)
56-
goto out;
57-
5850
/* Try to grab all the pages covered by the Squashfs block */
5951
for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
6052
page[i] = (n == target_page->index) ? target_page :
@@ -89,8 +81,19 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
8981
goto out;
9082
}
9183

84+
/*
85+
* Create a "page actor" which will kmap and kunmap the
86+
* page cache pages appropriately within the decompressor
87+
*/
88+
actor = squashfs_page_actor_init_special(msblk, page, pages, 0);
89+
if (actor == NULL)
90+
goto out;
91+
9292
/* Decompress directly into the page cache buffers */
9393
res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
94+
95+
kfree(actor);
96+
9497
if (res < 0)
9598
goto mark_errored;
9699

@@ -116,7 +119,6 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
116119
put_page(page[i]);
117120
}
118121

119-
kfree(actor);
120122
kfree(page);
121123

122124
return 0;
@@ -135,7 +137,6 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
135137
}
136138

137139
out:
138-
kfree(actor);
139140
kfree(page);
140141
return res;
141142
}

fs/squashfs/lz4_wrapper.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,12 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
119119
buff = stream->output;
120120
while (data) {
121121
if (bytes <= PAGE_SIZE) {
122-
memcpy(data, buff, bytes);
122+
if (!IS_ERR(data))
123+
memcpy(data, buff, bytes);
123124
break;
124125
}
125-
memcpy(data, buff, PAGE_SIZE);
126+
if (!IS_ERR(data))
127+
memcpy(data, buff, PAGE_SIZE);
126128
buff += PAGE_SIZE;
127129
bytes -= PAGE_SIZE;
128130
data = squashfs_next_page(output);
@@ -139,5 +141,6 @@ const struct squashfs_decompressor squashfs_lz4_comp_ops = {
139141
.decompress = lz4_uncompress,
140142
.id = LZ4_COMPRESSION,
141143
.name = "lz4",
144+
.alloc_buffer = 0,
142145
.supported = 1
143146
};

fs/squashfs/lzo_wrapper.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,12 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
9393
buff = stream->output;
9494
while (data) {
9595
if (bytes <= PAGE_SIZE) {
96-
memcpy(data, buff, bytes);
96+
if (!IS_ERR(data))
97+
memcpy(data, buff, bytes);
9798
break;
9899
} else {
99-
memcpy(data, buff, PAGE_SIZE);
100+
if (!IS_ERR(data))
101+
memcpy(data, buff, PAGE_SIZE);
100102
buff += PAGE_SIZE;
101103
bytes -= PAGE_SIZE;
102104
data = squashfs_next_page(output);
@@ -116,5 +118,6 @@ const struct squashfs_decompressor squashfs_lzo_comp_ops = {
116118
.decompress = lzo_uncompress,
117119
.id = LZO_COMPRESSION,
118120
.name = "lzo",
121+
.alloc_buffer = 0,
119122
.supported = 1
120123
};

fs/squashfs/page_actor.c

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <linux/kernel.h>
88
#include <linux/slab.h>
99
#include <linux/pagemap.h>
10+
#include "squashfs_fs_sb.h"
11+
#include "decompressor.h"
1012
#include "page_actor.h"
1113

1214
/*
@@ -57,29 +59,62 @@ struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
5759
}
5860

5961
/* Implementation of page_actor for decompressing directly into page cache. */
62+
static void *handle_next_page(struct squashfs_page_actor *actor)
63+
{
64+
int max_pages = (actor->length + PAGE_SIZE - 1) >> PAGE_SHIFT;
65+
66+
if (actor->returned_pages == max_pages)
67+
return NULL;
68+
69+
if ((actor->next_page == actor->pages) ||
70+
(actor->next_index != actor->page[actor->next_page]->index)) {
71+
if (actor->alloc_buffer) {
72+
void *tmp_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
73+
74+
if (tmp_buffer) {
75+
actor->tmp_buffer = tmp_buffer;
76+
actor->next_index++;
77+
actor->returned_pages++;
78+
return tmp_buffer;
79+
}
80+
}
81+
82+
actor->next_index++;
83+
actor->returned_pages++;
84+
return ERR_PTR(-ENOMEM);
85+
}
86+
87+
actor->next_index++;
88+
actor->returned_pages++;
89+
return actor->pageaddr = kmap_local_page(actor->page[actor->next_page++]);
90+
}
91+
6092
static void *direct_first_page(struct squashfs_page_actor *actor)
6193
{
62-
actor->next_page = 1;
63-
return actor->pageaddr = kmap_atomic(actor->page[0]);
94+
return handle_next_page(actor);
6495
}
6596

6697
static void *direct_next_page(struct squashfs_page_actor *actor)
6798
{
6899
if (actor->pageaddr)
69-
kunmap_atomic(actor->pageaddr);
100+
kunmap_local(actor->pageaddr);
101+
102+
kfree(actor->tmp_buffer);
103+
actor->pageaddr = actor->tmp_buffer = NULL;
70104

71-
return actor->pageaddr = actor->next_page == actor->pages ? NULL :
72-
kmap_atomic(actor->page[actor->next_page++]);
105+
return handle_next_page(actor);
73106
}
74107

75108
static void direct_finish_page(struct squashfs_page_actor *actor)
76109
{
77110
if (actor->pageaddr)
78-
kunmap_atomic(actor->pageaddr);
111+
kunmap_local(actor->pageaddr);
112+
113+
kfree(actor->tmp_buffer);
79114
}
80115

81-
struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
82-
int pages, int length)
116+
struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_info *msblk,
117+
struct page **page, int pages, int length)
83118
{
84119
struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
85120

@@ -90,7 +125,11 @@ struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
90125
actor->page = page;
91126
actor->pages = pages;
92127
actor->next_page = 0;
128+
actor->returned_pages = 0;
129+
actor->next_index = page[0]->index & ~((1 << (msblk->block_log - PAGE_SHIFT)) - 1);
93130
actor->pageaddr = NULL;
131+
actor->tmp_buffer = NULL;
132+
actor->alloc_buffer = msblk->decompressor->alloc_buffer;
94133
actor->squashfs_first_page = direct_first_page;
95134
actor->squashfs_next_page = direct_next_page;
96135
actor->squashfs_finish_page = direct_finish_page;

fs/squashfs/page_actor.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,35 @@ static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
4545
{
4646
/* empty */
4747
}
48+
49+
static inline void squashfs_actor_nobuff(struct squashfs_page_actor *actor)
50+
{
51+
/* empty */
52+
}
4853
#else
4954
struct squashfs_page_actor {
5055
union {
5156
void **buffer;
5257
struct page **page;
5358
};
5459
void *pageaddr;
60+
void *tmp_buffer;
5561
void *(*squashfs_first_page)(struct squashfs_page_actor *);
5662
void *(*squashfs_next_page)(struct squashfs_page_actor *);
5763
void (*squashfs_finish_page)(struct squashfs_page_actor *);
5864
int pages;
5965
int length;
6066
int next_page;
67+
int alloc_buffer;
68+
int returned_pages;
69+
pgoff_t next_index;
6170
};
6271

63-
extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
64-
extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
65-
**, int, int);
72+
extern struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
73+
int pages, int length);
74+
extern struct squashfs_page_actor *squashfs_page_actor_init_special(
75+
struct squashfs_sb_info *msblk,
76+
struct page **page, int pages, int length);
6677
static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
6778
{
6879
return actor->squashfs_first_page(actor);
@@ -75,5 +86,9 @@ static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
7586
{
7687
actor->squashfs_finish_page(actor);
7788
}
89+
static inline void squashfs_actor_nobuff(struct squashfs_page_actor *actor)
90+
{
91+
actor->alloc_buffer = 0;
92+
}
7893
#endif
7994
#endif

fs/squashfs/xz_wrapper.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
131131
stream->buf.out_pos = 0;
132132
stream->buf.out_size = PAGE_SIZE;
133133
stream->buf.out = squashfs_first_page(output);
134+
if (IS_ERR(stream->buf.out)) {
135+
error = PTR_ERR(stream->buf.out);
136+
goto finish;
137+
}
134138

135139
for (;;) {
136140
enum xz_ret xz_err;
@@ -156,7 +160,10 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
156160

157161
if (stream->buf.out_pos == stream->buf.out_size) {
158162
stream->buf.out = squashfs_next_page(output);
159-
if (stream->buf.out != NULL) {
163+
if (IS_ERR(stream->buf.out)) {
164+
error = PTR_ERR(stream->buf.out);
165+
break;
166+
} else if (stream->buf.out != NULL) {
160167
stream->buf.out_pos = 0;
161168
total += PAGE_SIZE;
162169
}
@@ -171,6 +178,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
171178
}
172179
}
173180

181+
finish:
174182
squashfs_finish_page(output);
175183

176184
return error ? error : total + stream->buf.out_pos;
@@ -183,5 +191,6 @@ const struct squashfs_decompressor squashfs_xz_comp_ops = {
183191
.decompress = squashfs_xz_uncompress,
184192
.id = XZ_COMPRESSION,
185193
.name = "xz",
194+
.alloc_buffer = 1,
186195
.supported = 1
187196
};

fs/squashfs/zlib_wrapper.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
6262
stream->next_out = squashfs_first_page(output);
6363
stream->avail_in = 0;
6464

65+
if (IS_ERR(stream->next_out)) {
66+
error = PTR_ERR(stream->next_out);
67+
goto finish;
68+
}
69+
6570
for (;;) {
6671
int zlib_err;
6772

@@ -85,7 +90,10 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
8590

8691
if (stream->avail_out == 0) {
8792
stream->next_out = squashfs_next_page(output);
88-
if (stream->next_out != NULL)
93+
if (IS_ERR(stream->next_out)) {
94+
error = PTR_ERR(stream->next_out);
95+
break;
96+
} else if (stream->next_out != NULL)
8997
stream->avail_out = PAGE_SIZE;
9098
}
9199

@@ -107,6 +115,7 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
107115
}
108116
}
109117

118+
finish:
110119
squashfs_finish_page(output);
111120

112121
if (!error)
@@ -122,6 +131,7 @@ const struct squashfs_decompressor squashfs_zlib_comp_ops = {
122131
.decompress = zlib_uncompress,
123132
.id = ZLIB_COMPRESSION,
124133
.name = "zlib",
134+
.alloc_buffer = 1,
125135
.supported = 1
126136
};
127137

fs/squashfs/zstd_wrapper.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
8080

8181
out_buf.size = PAGE_SIZE;
8282
out_buf.dst = squashfs_first_page(output);
83+
if (IS_ERR(out_buf.dst)) {
84+
error = PTR_ERR(out_buf.dst);
85+
goto finish;
86+
}
8387

8488
for (;;) {
8589
size_t zstd_err;
@@ -104,7 +108,10 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
104108

105109
if (out_buf.pos == out_buf.size) {
106110
out_buf.dst = squashfs_next_page(output);
107-
if (out_buf.dst == NULL) {
111+
if (IS_ERR(out_buf.dst)) {
112+
error = PTR_ERR(out_buf.dst);
113+
break;
114+
} else if (out_buf.dst == NULL) {
108115
/* Shouldn't run out of pages
109116
* before stream is done.
110117
*/
@@ -129,6 +136,8 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
129136
}
130137
}
131138

139+
finish:
140+
132141
squashfs_finish_page(output);
133142

134143
return error ? error : total_out;
@@ -140,5 +149,6 @@ const struct squashfs_decompressor squashfs_zstd_comp_ops = {
140149
.decompress = zstd_uncompress,
141150
.id = ZSTD_COMPRESSION,
142151
.name = "zstd",
152+
.alloc_buffer = 1,
143153
.supported = 1
144154
};

0 commit comments

Comments
 (0)