Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Squashfs: Generalise paging handling in the decompressors

Further generalise the decompressors by adding a page handler
abstraction. This adds helpers to allow the decompressors
to access and process the output buffers in an implementation
independant manner.

This allows different types of output buffer to be passed
to the decompressors, with the implementation specific
aspects handled at decompression time, but without the
knowledge being held in the decompressor wrapper code.

This will allow the decompressors to handle Squashfs
cache buffers, and page cache pages.

This patch adds the abstraction and an implementation for
the caches.

Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
Reviewed-by: Minchan Kim <minchan@kernel.org>

+163 -67
+15 -12
fs/squashfs/block.c
··· 36 36 #include "squashfs_fs_sb.h" 37 37 #include "squashfs.h" 38 38 #include "decompressor.h" 39 + #include "page_actor.h" 39 40 40 41 /* 41 42 * Read the metadata block length, this is stored in the first two ··· 87 86 * generated a larger block - this does occasionally happen with compression 88 87 * algorithms). 89 88 */ 90 - int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, 91 - int length, u64 *next_index, int srclength, int pages) 89 + int squashfs_read_data(struct super_block *sb, u64 index, int length, 90 + u64 *next_index, struct squashfs_page_actor *output) 92 91 { 93 92 struct squashfs_sb_info *msblk = sb->s_fs_info; 94 93 struct buffer_head **bh; 95 94 int offset = index & ((1 << msblk->devblksize_log2) - 1); 96 95 u64 cur_index = index >> msblk->devblksize_log2; 97 - int bytes, compressed, b = 0, k = 0, page = 0, avail, i; 96 + int bytes, compressed, b = 0, k = 0, avail, i; 98 97 99 - bh = kcalloc(((srclength + msblk->devblksize - 1) 98 + bh = kcalloc(((output->length + msblk->devblksize - 1) 100 99 >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); 101 100 if (bh == NULL) 102 101 return -ENOMEM; ··· 112 111 *next_index = index + length; 113 112 114 113 TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", 115 - index, compressed ? "" : "un", length, srclength); 114 + index, compressed ? "" : "un", length, output->length); 116 115 117 - if (length < 0 || length > srclength || 116 + if (length < 0 || length > output->length || 118 117 (index + length) > msblk->bytes_used) 119 118 goto read_failure; 120 119 ··· 146 145 TRACE("Block @ 0x%llx, %scompressed size %d\n", index, 147 146 compressed ? "" : "un", length); 148 147 149 - if (length < 0 || length > srclength || 148 + if (length < 0 || length > output->length || 150 149 (index + length) > msblk->bytes_used) 151 150 goto block_release; 152 151 ··· 166 165 } 167 166 168 167 if (compressed) { 169 - length = squashfs_decompress(msblk, buffer, bh, b, offset, 170 - length, srclength, pages); 168 + length = squashfs_decompress(msblk, bh, b, offset, length, 169 + output); 171 170 if (length < 0) 172 171 goto read_failure; 173 172 } else { ··· 175 174 * Block is uncompressed. 176 175 */ 177 176 int in, pg_offset = 0; 177 + void *data = squashfs_first_page(output); 178 178 179 179 for (bytes = length; k < b; k++) { 180 180 in = min(bytes, msblk->devblksize - offset); 181 181 bytes -= in; 182 182 while (in) { 183 183 if (pg_offset == PAGE_CACHE_SIZE) { 184 - page++; 184 + data = squashfs_next_page(output); 185 185 pg_offset = 0; 186 186 } 187 187 avail = min_t(int, in, PAGE_CACHE_SIZE - 188 188 pg_offset); 189 - memcpy(buffer[page] + pg_offset, 190 - bh[k]->b_data + offset, avail); 189 + memcpy(data + pg_offset, bh[k]->b_data + offset, 190 + avail); 191 191 in -= avail; 192 192 pg_offset += avail; 193 193 offset += avail; ··· 196 194 offset = 0; 197 195 put_bh(bh[k]); 198 196 } 197 + squashfs_finish_page(output); 199 198 } 200 199 201 200 kfree(bh);
+23 -5
fs/squashfs/cache.c
··· 56 56 #include "squashfs_fs.h" 57 57 #include "squashfs_fs_sb.h" 58 58 #include "squashfs.h" 59 + #include "page_actor.h" 59 60 60 61 /* 61 62 * Look-up block in cache, and increment usage count. If not in cache, read ··· 120 119 entry->error = 0; 121 120 spin_unlock(&cache->lock); 122 121 123 - entry->length = squashfs_read_data(sb, entry->data, 124 - block, length, &entry->next_index, 125 - cache->block_size, cache->pages); 122 + entry->length = squashfs_read_data(sb, block, length, 123 + &entry->next_index, entry->actor); 126 124 127 125 spin_lock(&cache->lock); 128 126 ··· 220 220 kfree(cache->entry[i].data[j]); 221 221 kfree(cache->entry[i].data); 222 222 } 223 + kfree(cache->entry[i].actor); 223 224 } 224 225 225 226 kfree(cache->entry); ··· 280 279 ERROR("Failed to allocate %s buffer\n", name); 281 280 goto cleanup; 282 281 } 282 + } 283 + 284 + entry->actor = squashfs_page_actor_init(entry->data, 285 + cache->pages, 0); 286 + if (entry->actor == NULL) { 287 + ERROR("Failed to allocate %s cache entry\n", name); 288 + goto cleanup; 283 289 } 284 290 } 285 291 ··· 418 410 int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 419 411 int i, res; 420 412 void *table, *buffer, **data; 413 + struct squashfs_page_actor *actor; 421 414 422 415 table = buffer = kmalloc(length, GFP_KERNEL); 423 416 if (table == NULL) ··· 430 421 goto failed; 431 422 } 432 423 424 + actor = squashfs_page_actor_init(data, pages, length); 425 + if (actor == NULL) { 426 + res = -ENOMEM; 427 + goto failed2; 428 + } 429 + 433 430 for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) 434 431 data[i] = buffer; 435 432 436 - res = squashfs_read_data(sb, data, block, length | 437 - SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages); 433 + res = squashfs_read_data(sb, block, length | 434 + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor); 438 435 439 436 kfree(data); 437 + kfree(actor); 440 438 441 439 if (res < 0) 442 440 goto failed; 443 441 444 442 return table; 445 443 444 + failed2: 445 + kfree(data); 446 446 failed: 447 447 kfree(table); 448 448 return ERR_PTR(res);
+11 -3
fs/squashfs/decompressor.c
··· 30 30 #include "squashfs_fs_sb.h" 31 31 #include "decompressor.h" 32 32 #include "squashfs.h" 33 + #include "page_actor.h" 33 34 34 35 /* 35 36 * This file (and decompressor.h) implements a decompressor framework for ··· 88 87 { 89 88 struct squashfs_sb_info *msblk = sb->s_fs_info; 90 89 void *buffer = NULL, *comp_opts; 90 + struct squashfs_page_actor *actor = NULL; 91 91 int length = 0; 92 92 93 93 /* ··· 101 99 goto out; 102 100 } 103 101 104 - length = squashfs_read_data(sb, &buffer, 105 - sizeof(struct squashfs_super_block), 0, NULL, 106 - PAGE_CACHE_SIZE, 1); 102 + actor = squashfs_page_actor_init(&buffer, 1, 0); 103 + if (actor == NULL) { 104 + comp_opts = ERR_PTR(-ENOMEM); 105 + goto out; 106 + } 107 + 108 + length = squashfs_read_data(sb, 109 + sizeof(struct squashfs_super_block), 0, NULL, actor); 107 110 108 111 if (length < 0) { 109 112 comp_opts = ERR_PTR(length); ··· 119 112 comp_opts = squashfs_comp_opts(msblk, buffer, length); 120 113 121 114 out: 115 + kfree(actor); 122 116 kfree(buffer); 123 117 return comp_opts; 124 118 }
+3 -2
fs/squashfs/decompressor.h
··· 27 27 void *(*init)(struct squashfs_sb_info *, void *); 28 28 void *(*comp_opts)(struct squashfs_sb_info *, void *, int); 29 29 void (*free)(void *); 30 - int (*decompress)(struct squashfs_sb_info *, void *, void **, 31 - struct buffer_head **, int, int, int, int, int); 30 + int (*decompress)(struct squashfs_sb_info *, void *, 31 + struct buffer_head **, int, int, int, 32 + struct squashfs_page_actor *); 32 33 int id; 33 34 char *name; 34 35 int supported;
+3 -4
fs/squashfs/decompressor_multi.c
··· 183 183 } 184 184 185 185 186 - int squashfs_decompress(struct squashfs_sb_info *msblk, 187 - void **buffer, struct buffer_head **bh, int b, int offset, int length, 188 - int srclength, int pages) 186 + int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, 187 + int b, int offset, int length, struct squashfs_page_actor *output) 189 188 { 190 189 int res; 191 190 struct squashfs_stream *stream = msblk->stream; 192 191 struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream); 193 192 res = msblk->decompressor->decompress(msblk, decomp_stream->stream, 194 - buffer, bh, b, offset, length, srclength, pages); 193 + bh, b, offset, length, output); 195 194 put_decomp_stream(decomp_stream, stream); 196 195 if (res < 0) 197 196 ERROR("%s decompression failed, data probably corrupt\n",
+4 -5
fs/squashfs/decompressor_multi_percpu.c
··· 74 74 } 75 75 } 76 76 77 - int squashfs_decompress(struct squashfs_sb_info *msblk, 78 - void **buffer, struct buffer_head **bh, int b, int offset, int length, 79 - int srclength, int pages) 77 + int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, 78 + int b, int offset, int length, struct squashfs_page_actor *output) 80 79 { 81 80 struct squashfs_stream __percpu *percpu = 82 81 (struct squashfs_stream __percpu *) msblk->stream; 83 82 struct squashfs_stream *stream = get_cpu_ptr(percpu); 84 - int res = msblk->decompressor->decompress(msblk, stream->stream, buffer, 85 - bh, b, offset, length, srclength, pages); 83 + int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b, 84 + offset, length, output); 86 85 put_cpu_ptr(stream); 87 86 88 87 if (res < 0)
+4 -5
fs/squashfs/decompressor_single.c
··· 61 61 } 62 62 } 63 63 64 - int squashfs_decompress(struct squashfs_sb_info *msblk, 65 - void **buffer, struct buffer_head **bh, int b, int offset, int length, 66 - int srclength, int pages) 64 + int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, 65 + int b, int offset, int length, struct squashfs_page_actor *output) 67 66 { 68 67 int res; 69 68 struct squashfs_stream *stream = msblk->stream; 70 69 71 70 mutex_lock(&stream->mutex); 72 - res = msblk->decompressor->decompress(msblk, stream->stream, buffer, 73 - bh, b, offset, length, srclength, pages); 71 + res = msblk->decompressor->decompress(msblk, stream->stream, bh, b, 72 + offset, length, output); 74 73 mutex_unlock(&stream->mutex); 75 74 76 75 if (res < 0)
+18 -9
fs/squashfs/lzo_wrapper.c
··· 31 31 #include "squashfs_fs_sb.h" 32 32 #include "squashfs.h" 33 33 #include "decompressor.h" 34 + #include "page_actor.h" 34 35 35 36 struct squashfs_lzo { 36 37 void *input; ··· 76 75 77 76 78 77 static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, 79 - void **buffer, struct buffer_head **bh, int b, int offset, int length, 80 - int srclength, int pages) 78 + struct buffer_head **bh, int b, int offset, int length, 79 + struct squashfs_page_actor *output) 81 80 { 82 81 struct squashfs_lzo *stream = strm; 83 - void *buff = stream->input; 82 + void *buff = stream->input, *data; 84 83 int avail, i, bytes = length, res; 85 - size_t out_len = srclength; 84 + size_t out_len = output->length; 86 85 87 86 for (i = 0; i < b; i++) { 88 87 avail = min(bytes, msblk->devblksize - offset); ··· 99 98 goto failed; 100 99 101 100 res = bytes = (int)out_len; 102 - for (i = 0, buff = stream->output; bytes && i < pages; i++) { 103 - avail = min_t(int, bytes, PAGE_CACHE_SIZE); 104 - memcpy(buffer[i], buff, avail); 105 - buff += avail; 106 - bytes -= avail; 101 + data = squashfs_first_page(output); 102 + buff = stream->output; 103 + while (data) { 104 + if (bytes <= PAGE_CACHE_SIZE) { 105 + memcpy(data, buff, bytes); 106 + break; 107 + } else { 108 + memcpy(data, buff, PAGE_CACHE_SIZE); 109 + buff += PAGE_CACHE_SIZE; 110 + bytes -= PAGE_CACHE_SIZE; 111 + data = squashfs_next_page(output); 112 + } 107 113 } 114 + squashfs_finish_page(output); 108 115 109 116 return res; 110 117
+49
fs/squashfs/page_actor.h
··· 1 + #ifndef PAGE_ACTOR_H 2 + #define PAGE_ACTOR_H 3 + /* 4 + * Copyright (c) 2013 5 + * Phillip Lougher <phillip@squashfs.org.uk> 6 + * 7 + * This work is licensed under the terms of the GNU GPL, version 2. See 8 + * the COPYING file in the top-level directory. 9 + */ 10 + 11 + struct squashfs_page_actor { 12 + void **page; 13 + int pages; 14 + int length; 15 + int next_page; 16 + }; 17 + 18 + static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page, 19 + int pages, int length) 20 + { 21 + struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); 22 + 23 + if (actor == NULL) 24 + return NULL; 25 + 26 + actor->length = length ? : pages * PAGE_CACHE_SIZE; 27 + actor->page = page; 28 + actor->pages = pages; 29 + actor->next_page = 0; 30 + return actor; 31 + } 32 + 33 + static inline void *squashfs_first_page(struct squashfs_page_actor *actor) 34 + { 35 + actor->next_page = 1; 36 + return actor->page[0]; 37 + } 38 + 39 + static inline void *squashfs_next_page(struct squashfs_page_actor *actor) 40 + { 41 + return actor->next_page == actor->pages ? NULL : 42 + actor->page[actor->next_page++]; 43 + } 44 + 45 + static inline void squashfs_finish_page(struct squashfs_page_actor *actor) 46 + { 47 + /* empty */ 48 + } 49 + #endif
+4 -4
fs/squashfs/squashfs.h
··· 28 28 #define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args) 29 29 30 30 /* block.c */ 31 - extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *, 32 - int, int); 31 + extern int squashfs_read_data(struct super_block *, u64, int, u64 *, 32 + struct squashfs_page_actor *); 33 33 34 34 /* cache.c */ 35 35 extern struct squashfs_cache *squashfs_cache_init(char *, int, int); ··· 53 53 /* decompressor_xxx.c */ 54 54 extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *); 55 55 extern void squashfs_decompressor_destroy(struct squashfs_sb_info *); 56 - extern int squashfs_decompress(struct squashfs_sb_info *, void **, 57 - struct buffer_head **, int, int, int, int, int); 56 + extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **, 57 + int, int, int, struct squashfs_page_actor *); 58 58 extern int squashfs_max_decompressors(void); 59 59 60 60 /* export.c */
+1
fs/squashfs/squashfs_fs_sb.h
··· 50 50 wait_queue_head_t wait_queue; 51 51 struct squashfs_cache *cache; 52 52 void **data; 53 + struct squashfs_page_actor *actor; 53 54 }; 54 55 55 56 struct squashfs_sb_info {
+13 -9
fs/squashfs/xz_wrapper.c
··· 32 32 #include "squashfs_fs_sb.h" 33 33 #include "squashfs.h" 34 34 #include "decompressor.h" 35 + #include "page_actor.h" 35 36 36 37 struct squashfs_xz { 37 38 struct xz_dec *state; ··· 130 129 131 130 132 131 static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, 133 - void **buffer, struct buffer_head **bh, int b, int offset, int length, 134 - int srclength, int pages) 132 + struct buffer_head **bh, int b, int offset, int length, 133 + struct squashfs_page_actor *output) 135 134 { 136 135 enum xz_ret xz_err; 137 - int avail, total = 0, k = 0, page = 0; 136 + int avail, total = 0, k = 0; 138 137 struct squashfs_xz *stream = strm; 139 138 140 139 xz_dec_reset(stream->state); ··· 142 141 stream->buf.in_size = 0; 143 142 stream->buf.out_pos = 0; 144 143 stream->buf.out_size = PAGE_CACHE_SIZE; 145 - stream->buf.out = buffer[page++]; 144 + stream->buf.out = squashfs_first_page(output); 146 145 147 146 do { 148 147 if (stream->buf.in_pos == stream->buf.in_size && k < b) { ··· 154 153 offset = 0; 155 154 } 156 155 157 - if (stream->buf.out_pos == stream->buf.out_size 158 - && page < pages) { 159 - stream->buf.out = buffer[page++]; 160 - stream->buf.out_pos = 0; 161 - total += PAGE_CACHE_SIZE; 156 + if (stream->buf.out_pos == stream->buf.out_size) { 157 + stream->buf.out = squashfs_next_page(output); 158 + if (stream->buf.out != NULL) { 159 + stream->buf.out_pos = 0; 160 + total += PAGE_CACHE_SIZE; 161 + } 162 162 } 163 163 164 164 xz_err = xz_dec_run(stream->state, &stream->buf); ··· 167 165 if (stream->buf.in_pos == stream->buf.in_size && k < b) 168 166 put_bh(bh[k++]); 169 167 } while (xz_err == XZ_OK); 168 + 169 + squashfs_finish_page(output); 170 170 171 171 if (xz_err != XZ_STREAM_END || k < b) 172 172 goto out;
+15 -9
fs/squashfs/zlib_wrapper.c
··· 32 32 #include "squashfs_fs_sb.h" 33 33 #include "squashfs.h" 34 34 #include "decompressor.h" 35 + #include "page_actor.h" 35 36 36 37 static void *zlib_init(struct squashfs_sb_info *dummy, void *buff) 37 38 { ··· 63 62 64 63 65 64 static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, 66 - void **buffer, struct buffer_head **bh, int b, int offset, int length, 67 - int srclength, int pages) 65 + struct buffer_head **bh, int b, int offset, int length, 66 + struct squashfs_page_actor *output) 68 67 { 69 - int zlib_err, zlib_init = 0; 70 - int k = 0, page = 0; 68 + int zlib_err, zlib_init = 0, k = 0; 71 69 z_stream *stream = strm; 72 70 73 - stream->avail_out = 0; 71 + stream->avail_out = PAGE_CACHE_SIZE; 72 + stream->next_out = squashfs_first_page(output); 74 73 stream->avail_in = 0; 75 74 76 75 do { ··· 82 81 offset = 0; 83 82 } 84 83 85 - if (stream->avail_out == 0 && page < pages) { 86 - stream->next_out = buffer[page++]; 87 - stream->avail_out = PAGE_CACHE_SIZE; 84 + if (stream->avail_out == 0) { 85 + stream->next_out = squashfs_next_page(output); 86 + if (stream->next_out != NULL) 87 + stream->avail_out = PAGE_CACHE_SIZE; 88 88 } 89 89 90 90 if (!zlib_init) { 91 91 zlib_err = zlib_inflateInit(stream); 92 - if (zlib_err != Z_OK) 92 + if (zlib_err != Z_OK) { 93 + squashfs_finish_page(output); 93 94 goto out; 95 + } 94 96 zlib_init = 1; 95 97 } 96 98 ··· 102 98 if (stream->avail_in == 0 && k < b) 103 99 put_bh(bh[k++]); 104 100 } while (zlib_err == Z_OK); 101 + 102 + squashfs_finish_page(output); 105 103 106 104 if (zlib_err != Z_STREAM_END) 107 105 goto out;