diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-20 17:51:37 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-20 17:51:37 -0500 |
commit | af2e2f328052082f58f041d574ed50c7f21c598f (patch) | |
tree | aad7fdd9fe6f3016596b1be8098eaaaa7e0a85f7 /fs | |
parent | 8b2e9b712f6139df9c754af0d67fecc4bbc88545 (diff) | |
parent | ed4f381ec15e5f11724cdbc68cffd2c22d1eaebd (diff) |
Merge tag 'squashfs-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-next
Pull squashfs updates from Phillip Lougher:
"These patches optionally improve the multi-threading peformance of
Squashfs by adding parallel decompression, and direct decompression
into the page cache, eliminating an intermediate buffer (removing
memcpy overhead and lock contention)"
* tag 'squashfs-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-next:
Squashfs: Check stream is not NULL in decompressor_multi.c
Squashfs: Directly decompress into the page cache for file data
Squashfs: Restructure squashfs_readpage()
Squashfs: Generalise paging handling in the decompressors
Squashfs: add multi-threaded decompression using percpu variable
squashfs: Enhance parallel I/O
Squashfs: Refactor decompressor interface and code
Diffstat (limited to 'fs')
-rw-r--r-- | fs/squashfs/Kconfig | 72 | ||||
-rw-r--r-- | fs/squashfs/Makefile | 5 | ||||
-rw-r--r-- | fs/squashfs/block.c | 36 | ||||
-rw-r--r-- | fs/squashfs/cache.c | 28 | ||||
-rw-r--r-- | fs/squashfs/decompressor.c | 59 | ||||
-rw-r--r-- | fs/squashfs/decompressor.h | 24 | ||||
-rw-r--r-- | fs/squashfs/decompressor_multi.c | 198 | ||||
-rw-r--r-- | fs/squashfs/decompressor_multi_percpu.c | 97 | ||||
-rw-r--r-- | fs/squashfs/decompressor_single.c | 85 | ||||
-rw-r--r-- | fs/squashfs/file.c | 142 | ||||
-rw-r--r-- | fs/squashfs/file_cache.c | 38 | ||||
-rw-r--r-- | fs/squashfs/file_direct.c | 173 | ||||
-rw-r--r-- | fs/squashfs/lzo_wrapper.c | 47 | ||||
-rw-r--r-- | fs/squashfs/page_actor.c | 100 | ||||
-rw-r--r-- | fs/squashfs/page_actor.h | 81 | ||||
-rw-r--r-- | fs/squashfs/squashfs.h | 20 | ||||
-rw-r--r-- | fs/squashfs/squashfs_fs_sb.h | 4 | ||||
-rw-r--r-- | fs/squashfs/super.c | 10 | ||||
-rw-r--r-- | fs/squashfs/xz_wrapper.c | 105 | ||||
-rw-r--r-- | fs/squashfs/zlib_wrapper.c | 64 |
20 files changed, 1145 insertions, 243 deletions
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index c70111ebefd4..b6fa8657dcbc 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig | |||
@@ -25,6 +25,78 @@ config SQUASHFS | |||
25 | 25 | ||
26 | If unsure, say N. | 26 | If unsure, say N. |
27 | 27 | ||
28 | choice | ||
29 | prompt "File decompression options" | ||
30 | depends on SQUASHFS | ||
31 | help | ||
32 | Squashfs now supports two options for decompressing file | ||
33 | data. Traditionally Squashfs has decompressed into an | ||
34 | intermediate buffer and then memcopied it into the page cache. | ||
35 | Squashfs now supports the ability to decompress directly into | ||
36 | the page cache. | ||
37 | |||
38 | If unsure, select "Decompress file data into an intermediate buffer" | ||
39 | |||
40 | config SQUASHFS_FILE_CACHE | ||
41 | bool "Decompress file data into an intermediate buffer" | ||
42 | help | ||
43 | Decompress file data into an intermediate buffer and then | ||
44 | memcopy it into the page cache. | ||
45 | |||
46 | config SQUASHFS_FILE_DIRECT | ||
47 | bool "Decompress files directly into the page cache" | ||
48 | help | ||
49 | Directly decompress file data into the page cache. | ||
50 | Doing so can significantly improve performance because | ||
51 | it eliminates a memcpy and it also removes the lock contention | ||
52 | on the single buffer. | ||
53 | |||
54 | endchoice | ||
55 | |||
56 | choice | ||
57 | prompt "Decompressor parallelisation options" | ||
58 | depends on SQUASHFS | ||
59 | help | ||
60 | Squashfs now supports three parallelisation options for | ||
61 | decompression. Each one exhibits various trade-offs between | ||
62 | decompression performance and CPU and memory usage. | ||
63 | |||
64 | If in doubt, select "Single threaded compression" | ||
65 | |||
66 | config SQUASHFS_DECOMP_SINGLE | ||
67 | bool "Single threaded compression" | ||
68 | help | ||
69 | Traditionally Squashfs has used single-threaded decompression. | ||
70 | Only one block (data or metadata) can be decompressed at any | ||
71 | one time. This limits CPU and memory usage to a minimum. | ||
72 | |||
73 | config SQUASHFS_DECOMP_MULTI | ||
74 | bool "Use multiple decompressors for parallel I/O" | ||
75 | help | ||
76 | By default Squashfs uses a single decompressor but it gives | ||
77 | poor performance on parallel I/O workloads when using multiple CPU | ||
78 | machines due to waiting on decompressor availability. | ||
79 | |||
80 | If you have a parallel I/O workload and your system has enough memory, | ||
81 | using this option may improve overall I/O performance. | ||
82 | |||
83 | This decompressor implementation uses up to two parallel | ||
84 | decompressors per core. It dynamically allocates decompressors | ||
85 | on a demand basis. | ||
86 | |||
87 | config SQUASHFS_DECOMP_MULTI_PERCPU | ||
88 | bool "Use percpu multiple decompressors for parallel I/O" | ||
89 | help | ||
90 | By default Squashfs uses a single decompressor but it gives | ||
91 | poor performance on parallel I/O workloads when using multiple CPU | ||
92 | machines due to waiting on decompressor availability. | ||
93 | |||
94 | This decompressor implementation uses a maximum of one | ||
95 | decompressor per core. It uses percpu variables to ensure | ||
96 | decompression is load-balanced across the cores. | ||
97 | |||
98 | endchoice | ||
99 | |||
28 | config SQUASHFS_XATTR | 100 | config SQUASHFS_XATTR |
29 | bool "Squashfs XATTR support" | 101 | bool "Squashfs XATTR support" |
30 | depends on SQUASHFS | 102 | depends on SQUASHFS |
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index 110b0476f3b4..4132520b4ff2 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile | |||
@@ -5,6 +5,11 @@ | |||
5 | obj-$(CONFIG_SQUASHFS) += squashfs.o | 5 | obj-$(CONFIG_SQUASHFS) += squashfs.o |
6 | squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o | 6 | squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o |
7 | squashfs-y += namei.o super.o symlink.o decompressor.o | 7 | squashfs-y += namei.o super.o symlink.o decompressor.o |
8 | squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o | ||
9 | squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o | ||
10 | squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o | ||
11 | squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o | ||
12 | squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o | ||
8 | squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o | 13 | squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o |
9 | squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o | 14 | squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o |
10 | squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o | 15 | squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o |
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c index 41d108ecc9be..0cea9b9236d0 100644 --- a/fs/squashfs/block.c +++ b/fs/squashfs/block.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include "squashfs_fs_sb.h" | 36 | #include "squashfs_fs_sb.h" |
37 | #include "squashfs.h" | 37 | #include "squashfs.h" |
38 | #include "decompressor.h" | 38 | #include "decompressor.h" |
39 | #include "page_actor.h" | ||
39 | 40 | ||
40 | /* | 41 | /* |
41 | * Read the metadata block length, this is stored in the first two | 42 | * Read the metadata block length, this is stored in the first two |
@@ -86,16 +87,16 @@ static struct buffer_head *get_block_length(struct super_block *sb, | |||
86 | * generated a larger block - this does occasionally happen with compression | 87 | * generated a larger block - this does occasionally happen with compression |
87 | * algorithms). | 88 | * algorithms). |
88 | */ | 89 | */ |
89 | int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | 90 | int squashfs_read_data(struct super_block *sb, u64 index, int length, |
90 | int length, u64 *next_index, int srclength, int pages) | 91 | u64 *next_index, struct squashfs_page_actor *output) |
91 | { | 92 | { |
92 | struct squashfs_sb_info *msblk = sb->s_fs_info; | 93 | struct squashfs_sb_info *msblk = sb->s_fs_info; |
93 | struct buffer_head **bh; | 94 | struct buffer_head **bh; |
94 | int offset = index & ((1 << msblk->devblksize_log2) - 1); | 95 | int offset = index & ((1 << msblk->devblksize_log2) - 1); |
95 | u64 cur_index = index >> msblk->devblksize_log2; | 96 | u64 cur_index = index >> msblk->devblksize_log2; |
96 | int bytes, compressed, b = 0, k = 0, page = 0, avail; | 97 | int bytes, compressed, b = 0, k = 0, avail, i; |
97 | 98 | ||
98 | bh = kcalloc(((srclength + msblk->devblksize - 1) | 99 | bh = kcalloc(((output->length + msblk->devblksize - 1) |
99 | >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); | 100 | >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); |
100 | if (bh == NULL) | 101 | if (bh == NULL) |
101 | return -ENOMEM; | 102 | return -ENOMEM; |
@@ -111,9 +112,9 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
111 | *next_index = index + length; | 112 | *next_index = index + length; |
112 | 113 | ||
113 | TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", | 114 | TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", |
114 | index, compressed ? "" : "un", length, srclength); | 115 | index, compressed ? "" : "un", length, output->length); |
115 | 116 | ||
116 | if (length < 0 || length > srclength || | 117 | if (length < 0 || length > output->length || |
117 | (index + length) > msblk->bytes_used) | 118 | (index + length) > msblk->bytes_used) |
118 | goto read_failure; | 119 | goto read_failure; |
119 | 120 | ||
@@ -145,7 +146,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
145 | TRACE("Block @ 0x%llx, %scompressed size %d\n", index, | 146 | TRACE("Block @ 0x%llx, %scompressed size %d\n", index, |
146 | compressed ? "" : "un", length); | 147 | compressed ? "" : "un", length); |
147 | 148 | ||
148 | if (length < 0 || length > srclength || | 149 | if (length < 0 || length > output->length || |
149 | (index + length) > msblk->bytes_used) | 150 | (index + length) > msblk->bytes_used) |
150 | goto block_release; | 151 | goto block_release; |
151 | 152 | ||
@@ -158,9 +159,15 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
158 | ll_rw_block(READ, b - 1, bh + 1); | 159 | ll_rw_block(READ, b - 1, bh + 1); |
159 | } | 160 | } |
160 | 161 | ||
162 | for (i = 0; i < b; i++) { | ||
163 | wait_on_buffer(bh[i]); | ||
164 | if (!buffer_uptodate(bh[i])) | ||
165 | goto block_release; | ||
166 | } | ||
167 | |||
161 | if (compressed) { | 168 | if (compressed) { |
162 | length = squashfs_decompress(msblk, buffer, bh, b, offset, | 169 | length = squashfs_decompress(msblk, bh, b, offset, length, |
163 | length, srclength, pages); | 170 | output); |
164 | if (length < 0) | 171 | if (length < 0) |
165 | goto read_failure; | 172 | goto read_failure; |
166 | } else { | 173 | } else { |
@@ -168,22 +175,20 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
168 | * Block is uncompressed. | 175 | * Block is uncompressed. |
169 | */ | 176 | */ |
170 | int in, pg_offset = 0; | 177 | int in, pg_offset = 0; |
178 | void *data = squashfs_first_page(output); | ||
171 | 179 | ||
172 | for (bytes = length; k < b; k++) { | 180 | for (bytes = length; k < b; k++) { |
173 | in = min(bytes, msblk->devblksize - offset); | 181 | in = min(bytes, msblk->devblksize - offset); |
174 | bytes -= in; | 182 | bytes -= in; |
175 | wait_on_buffer(bh[k]); | ||
176 | if (!buffer_uptodate(bh[k])) | ||
177 | goto block_release; | ||
178 | while (in) { | 183 | while (in) { |
179 | if (pg_offset == PAGE_CACHE_SIZE) { | 184 | if (pg_offset == PAGE_CACHE_SIZE) { |
180 | page++; | 185 | data = squashfs_next_page(output); |
181 | pg_offset = 0; | 186 | pg_offset = 0; |
182 | } | 187 | } |
183 | avail = min_t(int, in, PAGE_CACHE_SIZE - | 188 | avail = min_t(int, in, PAGE_CACHE_SIZE - |
184 | pg_offset); | 189 | pg_offset); |
185 | memcpy(buffer[page] + pg_offset, | 190 | memcpy(data + pg_offset, bh[k]->b_data + offset, |
186 | bh[k]->b_data + offset, avail); | 191 | avail); |
187 | in -= avail; | 192 | in -= avail; |
188 | pg_offset += avail; | 193 | pg_offset += avail; |
189 | offset += avail; | 194 | offset += avail; |
@@ -191,6 +196,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
191 | offset = 0; | 196 | offset = 0; |
192 | put_bh(bh[k]); | 197 | put_bh(bh[k]); |
193 | } | 198 | } |
199 | squashfs_finish_page(output); | ||
194 | } | 200 | } |
195 | 201 | ||
196 | kfree(bh); | 202 | kfree(bh); |
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c index af0b73802592..1cb70a0b2168 100644 --- a/fs/squashfs/cache.c +++ b/fs/squashfs/cache.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #include "squashfs_fs.h" | 56 | #include "squashfs_fs.h" |
57 | #include "squashfs_fs_sb.h" | 57 | #include "squashfs_fs_sb.h" |
58 | #include "squashfs.h" | 58 | #include "squashfs.h" |
59 | #include "page_actor.h" | ||
59 | 60 | ||
60 | /* | 61 | /* |
61 | * Look-up block in cache, and increment usage count. If not in cache, read | 62 | * Look-up block in cache, and increment usage count. If not in cache, read |
@@ -119,9 +120,8 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb, | |||
119 | entry->error = 0; | 120 | entry->error = 0; |
120 | spin_unlock(&cache->lock); | 121 | spin_unlock(&cache->lock); |
121 | 122 | ||
122 | entry->length = squashfs_read_data(sb, entry->data, | 123 | entry->length = squashfs_read_data(sb, block, length, |
123 | block, length, &entry->next_index, | 124 | &entry->next_index, entry->actor); |
124 | cache->block_size, cache->pages); | ||
125 | 125 | ||
126 | spin_lock(&cache->lock); | 126 | spin_lock(&cache->lock); |
127 | 127 | ||
@@ -220,6 +220,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache) | |||
220 | kfree(cache->entry[i].data[j]); | 220 | kfree(cache->entry[i].data[j]); |
221 | kfree(cache->entry[i].data); | 221 | kfree(cache->entry[i].data); |
222 | } | 222 | } |
223 | kfree(cache->entry[i].actor); | ||
223 | } | 224 | } |
224 | 225 | ||
225 | kfree(cache->entry); | 226 | kfree(cache->entry); |
@@ -280,6 +281,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries, | |||
280 | goto cleanup; | 281 | goto cleanup; |
281 | } | 282 | } |
282 | } | 283 | } |
284 | |||
285 | entry->actor = squashfs_page_actor_init(entry->data, | ||
286 | cache->pages, 0); | ||
287 | if (entry->actor == NULL) { | ||
288 | ERROR("Failed to allocate %s cache entry\n", name); | ||
289 | goto cleanup; | ||
290 | } | ||
283 | } | 291 | } |
284 | 292 | ||
285 | return cache; | 293 | return cache; |
@@ -410,6 +418,7 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length) | |||
410 | int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | 418 | int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; |
411 | int i, res; | 419 | int i, res; |
412 | void *table, *buffer, **data; | 420 | void *table, *buffer, **data; |
421 | struct squashfs_page_actor *actor; | ||
413 | 422 | ||
414 | table = buffer = kmalloc(length, GFP_KERNEL); | 423 | table = buffer = kmalloc(length, GFP_KERNEL); |
415 | if (table == NULL) | 424 | if (table == NULL) |
@@ -421,19 +430,28 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length) | |||
421 | goto failed; | 430 | goto failed; |
422 | } | 431 | } |
423 | 432 | ||
433 | actor = squashfs_page_actor_init(data, pages, length); | ||
434 | if (actor == NULL) { | ||
435 | res = -ENOMEM; | ||
436 | goto failed2; | ||
437 | } | ||
438 | |||
424 | for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) | 439 | for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) |
425 | data[i] = buffer; | 440 | data[i] = buffer; |
426 | 441 | ||
427 | res = squashfs_read_data(sb, data, block, length | | 442 | res = squashfs_read_data(sb, block, length | |
428 | SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages); | 443 | SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor); |
429 | 444 | ||
430 | kfree(data); | 445 | kfree(data); |
446 | kfree(actor); | ||
431 | 447 | ||
432 | if (res < 0) | 448 | if (res < 0) |
433 | goto failed; | 449 | goto failed; |
434 | 450 | ||
435 | return table; | 451 | return table; |
436 | 452 | ||
453 | failed2: | ||
454 | kfree(data); | ||
437 | failed: | 455 | failed: |
438 | kfree(table); | 456 | kfree(table); |
439 | return ERR_PTR(res); | 457 | return ERR_PTR(res); |
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c index 3f6271d86abc..ac22fe73b0ad 100644 --- a/fs/squashfs/decompressor.c +++ b/fs/squashfs/decompressor.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "squashfs_fs_sb.h" | 30 | #include "squashfs_fs_sb.h" |
31 | #include "decompressor.h" | 31 | #include "decompressor.h" |
32 | #include "squashfs.h" | 32 | #include "squashfs.h" |
33 | #include "page_actor.h" | ||
33 | 34 | ||
34 | /* | 35 | /* |
35 | * This file (and decompressor.h) implements a decompressor framework for | 36 | * This file (and decompressor.h) implements a decompressor framework for |
@@ -37,29 +38,29 @@ | |||
37 | */ | 38 | */ |
38 | 39 | ||
39 | static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = { | 40 | static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = { |
40 | NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0 | 41 | NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0 |
41 | }; | 42 | }; |
42 | 43 | ||
43 | #ifndef CONFIG_SQUASHFS_LZO | 44 | #ifndef CONFIG_SQUASHFS_LZO |
44 | static const struct squashfs_decompressor squashfs_lzo_comp_ops = { | 45 | static const struct squashfs_decompressor squashfs_lzo_comp_ops = { |
45 | NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0 | 46 | NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0 |
46 | }; | 47 | }; |
47 | #endif | 48 | #endif |
48 | 49 | ||
49 | #ifndef CONFIG_SQUASHFS_XZ | 50 | #ifndef CONFIG_SQUASHFS_XZ |
50 | static const struct squashfs_decompressor squashfs_xz_comp_ops = { | 51 | static const struct squashfs_decompressor squashfs_xz_comp_ops = { |
51 | NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0 | 52 | NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0 |
52 | }; | 53 | }; |
53 | #endif | 54 | #endif |
54 | 55 | ||
55 | #ifndef CONFIG_SQUASHFS_ZLIB | 56 | #ifndef CONFIG_SQUASHFS_ZLIB |
56 | static const struct squashfs_decompressor squashfs_zlib_comp_ops = { | 57 | static const struct squashfs_decompressor squashfs_zlib_comp_ops = { |
57 | NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0 | 58 | NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0 |
58 | }; | 59 | }; |
59 | #endif | 60 | #endif |
60 | 61 | ||
61 | static const struct squashfs_decompressor squashfs_unknown_comp_ops = { | 62 | static const struct squashfs_decompressor squashfs_unknown_comp_ops = { |
62 | NULL, NULL, NULL, 0, "unknown", 0 | 63 | NULL, NULL, NULL, NULL, 0, "unknown", 0 |
63 | }; | 64 | }; |
64 | 65 | ||
65 | static const struct squashfs_decompressor *decompressor[] = { | 66 | static const struct squashfs_decompressor *decompressor[] = { |
@@ -83,10 +84,11 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id) | |||
83 | } | 84 | } |
84 | 85 | ||
85 | 86 | ||
86 | void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags) | 87 | static void *get_comp_opts(struct super_block *sb, unsigned short flags) |
87 | { | 88 | { |
88 | struct squashfs_sb_info *msblk = sb->s_fs_info; | 89 | struct squashfs_sb_info *msblk = sb->s_fs_info; |
89 | void *strm, *buffer = NULL; | 90 | void *buffer = NULL, *comp_opts; |
91 | struct squashfs_page_actor *actor = NULL; | ||
90 | int length = 0; | 92 | int length = 0; |
91 | 93 | ||
92 | /* | 94 | /* |
@@ -94,23 +96,46 @@ void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags) | |||
94 | */ | 96 | */ |
95 | if (SQUASHFS_COMP_OPTS(flags)) { | 97 | if (SQUASHFS_COMP_OPTS(flags)) { |
96 | buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); | 98 | buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); |
97 | if (buffer == NULL) | 99 | if (buffer == NULL) { |
98 | return ERR_PTR(-ENOMEM); | 100 | comp_opts = ERR_PTR(-ENOMEM); |
101 | goto out; | ||
102 | } | ||
103 | |||
104 | actor = squashfs_page_actor_init(&buffer, 1, 0); | ||
105 | if (actor == NULL) { | ||
106 | comp_opts = ERR_PTR(-ENOMEM); | ||
107 | goto out; | ||
108 | } | ||
99 | 109 | ||
100 | length = squashfs_read_data(sb, &buffer, | 110 | length = squashfs_read_data(sb, |
101 | sizeof(struct squashfs_super_block), 0, NULL, | 111 | sizeof(struct squashfs_super_block), 0, NULL, actor); |
102 | PAGE_CACHE_SIZE, 1); | ||
103 | 112 | ||
104 | if (length < 0) { | 113 | if (length < 0) { |
105 | strm = ERR_PTR(length); | 114 | comp_opts = ERR_PTR(length); |
106 | goto finished; | 115 | goto out; |
107 | } | 116 | } |
108 | } | 117 | } |
109 | 118 | ||
110 | strm = msblk->decompressor->init(msblk, buffer, length); | 119 | comp_opts = squashfs_comp_opts(msblk, buffer, length); |
111 | 120 | ||
112 | finished: | 121 | out: |
122 | kfree(actor); | ||
113 | kfree(buffer); | 123 | kfree(buffer); |
124 | return comp_opts; | ||
125 | } | ||
126 | |||
127 | |||
128 | void *squashfs_decompressor_setup(struct super_block *sb, unsigned short flags) | ||
129 | { | ||
130 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
131 | void *stream, *comp_opts = get_comp_opts(sb, flags); | ||
132 | |||
133 | if (IS_ERR(comp_opts)) | ||
134 | return comp_opts; | ||
135 | |||
136 | stream = squashfs_decompressor_create(msblk, comp_opts); | ||
137 | if (IS_ERR(stream)) | ||
138 | kfree(comp_opts); | ||
114 | 139 | ||
115 | return strm; | 140 | return stream; |
116 | } | 141 | } |
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h index 330073e29029..af0985321808 100644 --- a/fs/squashfs/decompressor.h +++ b/fs/squashfs/decompressor.h | |||
@@ -24,28 +24,22 @@ | |||
24 | */ | 24 | */ |
25 | 25 | ||
26 | struct squashfs_decompressor { | 26 | struct squashfs_decompressor { |
27 | void *(*init)(struct squashfs_sb_info *, void *, int); | 27 | void *(*init)(struct squashfs_sb_info *, void *); |
28 | void *(*comp_opts)(struct squashfs_sb_info *, void *, int); | ||
28 | void (*free)(void *); | 29 | void (*free)(void *); |
29 | int (*decompress)(struct squashfs_sb_info *, void **, | 30 | int (*decompress)(struct squashfs_sb_info *, void *, |
30 | struct buffer_head **, int, int, int, int, int); | 31 | struct buffer_head **, int, int, int, |
32 | struct squashfs_page_actor *); | ||
31 | int id; | 33 | int id; |
32 | char *name; | 34 | char *name; |
33 | int supported; | 35 | int supported; |
34 | }; | 36 | }; |
35 | 37 | ||
36 | static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk, | 38 | static inline void *squashfs_comp_opts(struct squashfs_sb_info *msblk, |
37 | void *s) | 39 | void *buff, int length) |
38 | { | 40 | { |
39 | if (msblk->decompressor) | 41 | return msblk->decompressor->comp_opts ? |
40 | msblk->decompressor->free(s); | 42 | msblk->decompressor->comp_opts(msblk, buff, length) : NULL; |
41 | } | ||
42 | |||
43 | static inline int squashfs_decompress(struct squashfs_sb_info *msblk, | ||
44 | void **buffer, struct buffer_head **bh, int b, int offset, int length, | ||
45 | int srclength, int pages) | ||
46 | { | ||
47 | return msblk->decompressor->decompress(msblk, buffer, bh, b, offset, | ||
48 | length, srclength, pages); | ||
49 | } | 43 | } |
50 | 44 | ||
51 | #ifdef CONFIG_SQUASHFS_XZ | 45 | #ifdef CONFIG_SQUASHFS_XZ |
diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c new file mode 100644 index 000000000000..d6008a636479 --- /dev/null +++ b/fs/squashfs/decompressor_multi.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 | ||
3 | * Minchan Kim <minchan@kernel.org> | ||
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 | #include <linux/types.h> | ||
9 | #include <linux/mutex.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include <linux/buffer_head.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/wait.h> | ||
14 | #include <linux/cpumask.h> | ||
15 | |||
16 | #include "squashfs_fs.h" | ||
17 | #include "squashfs_fs_sb.h" | ||
18 | #include "decompressor.h" | ||
19 | #include "squashfs.h" | ||
20 | |||
21 | /* | ||
22 | * This file implements multi-threaded decompression in the | ||
23 | * decompressor framework | ||
24 | */ | ||
25 | |||
26 | |||
27 | /* | ||
28 | * The reason that multiply two is that a CPU can request new I/O | ||
29 | * while it is waiting previous request. | ||
30 | */ | ||
31 | #define MAX_DECOMPRESSOR (num_online_cpus() * 2) | ||
32 | |||
33 | |||
34 | int squashfs_max_decompressors(void) | ||
35 | { | ||
36 | return MAX_DECOMPRESSOR; | ||
37 | } | ||
38 | |||
39 | |||
40 | struct squashfs_stream { | ||
41 | void *comp_opts; | ||
42 | struct list_head strm_list; | ||
43 | struct mutex mutex; | ||
44 | int avail_decomp; | ||
45 | wait_queue_head_t wait; | ||
46 | }; | ||
47 | |||
48 | |||
49 | struct decomp_stream { | ||
50 | void *stream; | ||
51 | struct list_head list; | ||
52 | }; | ||
53 | |||
54 | |||
55 | static void put_decomp_stream(struct decomp_stream *decomp_strm, | ||
56 | struct squashfs_stream *stream) | ||
57 | { | ||
58 | mutex_lock(&stream->mutex); | ||
59 | list_add(&decomp_strm->list, &stream->strm_list); | ||
60 | mutex_unlock(&stream->mutex); | ||
61 | wake_up(&stream->wait); | ||
62 | } | ||
63 | |||
64 | void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, | ||
65 | void *comp_opts) | ||
66 | { | ||
67 | struct squashfs_stream *stream; | ||
68 | struct decomp_stream *decomp_strm = NULL; | ||
69 | int err = -ENOMEM; | ||
70 | |||
71 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); | ||
72 | if (!stream) | ||
73 | goto out; | ||
74 | |||
75 | stream->comp_opts = comp_opts; | ||
76 | mutex_init(&stream->mutex); | ||
77 | INIT_LIST_HEAD(&stream->strm_list); | ||
78 | init_waitqueue_head(&stream->wait); | ||
79 | |||
80 | /* | ||
81 | * We should have a decompressor at least as default | ||
82 | * so if we fail to allocate new decompressor dynamically, | ||
83 | * we could always fall back to default decompressor and | ||
84 | * file system works. | ||
85 | */ | ||
86 | decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); | ||
87 | if (!decomp_strm) | ||
88 | goto out; | ||
89 | |||
90 | decomp_strm->stream = msblk->decompressor->init(msblk, | ||
91 | stream->comp_opts); | ||
92 | if (IS_ERR(decomp_strm->stream)) { | ||
93 | err = PTR_ERR(decomp_strm->stream); | ||
94 | goto out; | ||
95 | } | ||
96 | |||
97 | list_add(&decomp_strm->list, &stream->strm_list); | ||
98 | stream->avail_decomp = 1; | ||
99 | return stream; | ||
100 | |||
101 | out: | ||
102 | kfree(decomp_strm); | ||
103 | kfree(stream); | ||
104 | return ERR_PTR(err); | ||
105 | } | ||
106 | |||
107 | |||
108 | void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) | ||
109 | { | ||
110 | struct squashfs_stream *stream = msblk->stream; | ||
111 | if (stream) { | ||
112 | struct decomp_stream *decomp_strm; | ||
113 | |||
114 | while (!list_empty(&stream->strm_list)) { | ||
115 | decomp_strm = list_entry(stream->strm_list.prev, | ||
116 | struct decomp_stream, list); | ||
117 | list_del(&decomp_strm->list); | ||
118 | msblk->decompressor->free(decomp_strm->stream); | ||
119 | kfree(decomp_strm); | ||
120 | stream->avail_decomp--; | ||
121 | } | ||
122 | WARN_ON(stream->avail_decomp); | ||
123 | kfree(stream->comp_opts); | ||
124 | kfree(stream); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | |||
129 | static struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk, | ||
130 | struct squashfs_stream *stream) | ||
131 | { | ||
132 | struct decomp_stream *decomp_strm; | ||
133 | |||
134 | while (1) { | ||
135 | mutex_lock(&stream->mutex); | ||
136 | |||
137 | /* There is available decomp_stream */ | ||
138 | if (!list_empty(&stream->strm_list)) { | ||
139 | decomp_strm = list_entry(stream->strm_list.prev, | ||
140 | struct decomp_stream, list); | ||
141 | list_del(&decomp_strm->list); | ||
142 | mutex_unlock(&stream->mutex); | ||
143 | break; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * If there is no available decomp and already full, | ||
148 | * let's wait for releasing decomp from other users. | ||
149 | */ | ||
150 | if (stream->avail_decomp >= MAX_DECOMPRESSOR) | ||
151 | goto wait; | ||
152 | |||
153 | /* Let's allocate new decomp */ | ||
154 | decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); | ||
155 | if (!decomp_strm) | ||
156 | goto wait; | ||
157 | |||
158 | decomp_strm->stream = msblk->decompressor->init(msblk, | ||
159 | stream->comp_opts); | ||
160 | if (IS_ERR(decomp_strm->stream)) { | ||
161 | kfree(decomp_strm); | ||
162 | goto wait; | ||
163 | } | ||
164 | |||
165 | stream->avail_decomp++; | ||
166 | WARN_ON(stream->avail_decomp > MAX_DECOMPRESSOR); | ||
167 | |||
168 | mutex_unlock(&stream->mutex); | ||
169 | break; | ||
170 | wait: | ||
171 | /* | ||
172 | * If system memory is tough, let's for other's | ||
173 | * releasing instead of hurting VM because it could | ||
174 | * make page cache thrashing. | ||
175 | */ | ||
176 | mutex_unlock(&stream->mutex); | ||
177 | wait_event(stream->wait, | ||
178 | !list_empty(&stream->strm_list)); | ||
179 | } | ||
180 | |||
181 | return decomp_strm; | ||
182 | } | ||
183 | |||
184 | |||
185 | int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, | ||
186 | int b, int offset, int length, struct squashfs_page_actor *output) | ||
187 | { | ||
188 | int res; | ||
189 | struct squashfs_stream *stream = msblk->stream; | ||
190 | struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream); | ||
191 | res = msblk->decompressor->decompress(msblk, decomp_stream->stream, | ||
192 | bh, b, offset, length, output); | ||
193 | put_decomp_stream(decomp_stream, stream); | ||
194 | if (res < 0) | ||
195 | ERROR("%s decompression failed, data probably corrupt\n", | ||
196 | msblk->decompressor->name); | ||
197 | return res; | ||
198 | } | ||
diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c new file mode 100644 index 000000000000..23a9c28ad8ea --- /dev/null +++ b/fs/squashfs/decompressor_multi_percpu.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
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/types.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include <linux/percpu.h> | ||
12 | #include <linux/buffer_head.h> | ||
13 | |||
14 | #include "squashfs_fs.h" | ||
15 | #include "squashfs_fs_sb.h" | ||
16 | #include "decompressor.h" | ||
17 | #include "squashfs.h" | ||
18 | |||
19 | /* | ||
20 | * This file implements multi-threaded decompression using percpu | ||
21 | * variables, one thread per cpu core. | ||
22 | */ | ||
23 | |||
24 | struct squashfs_stream { | ||
25 | void *stream; | ||
26 | }; | ||
27 | |||
28 | void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, | ||
29 | void *comp_opts) | ||
30 | { | ||
31 | struct squashfs_stream *stream; | ||
32 | struct squashfs_stream __percpu *percpu; | ||
33 | int err, cpu; | ||
34 | |||
35 | percpu = alloc_percpu(struct squashfs_stream); | ||
36 | if (percpu == NULL) | ||
37 | return ERR_PTR(-ENOMEM); | ||
38 | |||
39 | for_each_possible_cpu(cpu) { | ||
40 | stream = per_cpu_ptr(percpu, cpu); | ||
41 | stream->stream = msblk->decompressor->init(msblk, comp_opts); | ||
42 | if (IS_ERR(stream->stream)) { | ||
43 | err = PTR_ERR(stream->stream); | ||
44 | goto out; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | kfree(comp_opts); | ||
49 | return (__force void *) percpu; | ||
50 | |||
51 | out: | ||
52 | for_each_possible_cpu(cpu) { | ||
53 | stream = per_cpu_ptr(percpu, cpu); | ||
54 | if (!IS_ERR_OR_NULL(stream->stream)) | ||
55 | msblk->decompressor->free(stream->stream); | ||
56 | } | ||
57 | free_percpu(percpu); | ||
58 | return ERR_PTR(err); | ||
59 | } | ||
60 | |||
61 | void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) | ||
62 | { | ||
63 | struct squashfs_stream __percpu *percpu = | ||
64 | (struct squashfs_stream __percpu *) msblk->stream; | ||
65 | struct squashfs_stream *stream; | ||
66 | int cpu; | ||
67 | |||
68 | if (msblk->stream) { | ||
69 | for_each_possible_cpu(cpu) { | ||
70 | stream = per_cpu_ptr(percpu, cpu); | ||
71 | msblk->decompressor->free(stream->stream); | ||
72 | } | ||
73 | free_percpu(percpu); | ||
74 | } | ||
75 | } | ||
76 | |||
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) | ||
79 | { | ||
80 | struct squashfs_stream __percpu *percpu = | ||
81 | (struct squashfs_stream __percpu *) msblk->stream; | ||
82 | struct squashfs_stream *stream = get_cpu_ptr(percpu); | ||
83 | int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b, | ||
84 | offset, length, output); | ||
85 | put_cpu_ptr(stream); | ||
86 | |||
87 | if (res < 0) | ||
88 | ERROR("%s decompression failed, data probably corrupt\n", | ||
89 | msblk->decompressor->name); | ||
90 | |||
91 | return res; | ||
92 | } | ||
93 | |||
94 | int squashfs_max_decompressors(void) | ||
95 | { | ||
96 | return num_possible_cpus(); | ||
97 | } | ||
diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c new file mode 100644 index 000000000000..a6c75929a00e --- /dev/null +++ b/fs/squashfs/decompressor_single.c | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
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/types.h> | ||
10 | #include <linux/mutex.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/buffer_head.h> | ||
13 | |||
14 | #include "squashfs_fs.h" | ||
15 | #include "squashfs_fs_sb.h" | ||
16 | #include "decompressor.h" | ||
17 | #include "squashfs.h" | ||
18 | |||
19 | /* | ||
20 | * This file implements single-threaded decompression in the | ||
21 | * decompressor framework | ||
22 | */ | ||
23 | |||
24 | struct squashfs_stream { | ||
25 | void *stream; | ||
26 | struct mutex mutex; | ||
27 | }; | ||
28 | |||
29 | void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, | ||
30 | void *comp_opts) | ||
31 | { | ||
32 | struct squashfs_stream *stream; | ||
33 | int err = -ENOMEM; | ||
34 | |||
35 | stream = kmalloc(sizeof(*stream), GFP_KERNEL); | ||
36 | if (stream == NULL) | ||
37 | goto out; | ||
38 | |||
39 | stream->stream = msblk->decompressor->init(msblk, comp_opts); | ||
40 | if (IS_ERR(stream->stream)) { | ||
41 | err = PTR_ERR(stream->stream); | ||
42 | goto out; | ||
43 | } | ||
44 | |||
45 | kfree(comp_opts); | ||
46 | mutex_init(&stream->mutex); | ||
47 | return stream; | ||
48 | |||
49 | out: | ||
50 | kfree(stream); | ||
51 | return ERR_PTR(err); | ||
52 | } | ||
53 | |||
54 | void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) | ||
55 | { | ||
56 | struct squashfs_stream *stream = msblk->stream; | ||
57 | |||
58 | if (stream) { | ||
59 | msblk->decompressor->free(stream->stream); | ||
60 | kfree(stream); | ||
61 | } | ||
62 | } | ||
63 | |||
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) | ||
66 | { | ||
67 | int res; | ||
68 | struct squashfs_stream *stream = msblk->stream; | ||
69 | |||
70 | mutex_lock(&stream->mutex); | ||
71 | res = msblk->decompressor->decompress(msblk, stream->stream, bh, b, | ||
72 | offset, length, output); | ||
73 | mutex_unlock(&stream->mutex); | ||
74 | |||
75 | if (res < 0) | ||
76 | ERROR("%s decompression failed, data probably corrupt\n", | ||
77 | msblk->decompressor->name); | ||
78 | |||
79 | return res; | ||
80 | } | ||
81 | |||
82 | int squashfs_max_decompressors(void) | ||
83 | { | ||
84 | return 1; | ||
85 | } | ||
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index 8ca62c28fe12..e5c9689062ba 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c | |||
@@ -370,77 +370,15 @@ static int read_blocklist(struct inode *inode, int index, u64 *block) | |||
370 | return le32_to_cpu(size); | 370 | return le32_to_cpu(size); |
371 | } | 371 | } |
372 | 372 | ||
373 | 373 | /* Copy data into page cache */ | |
374 | static int squashfs_readpage(struct file *file, struct page *page) | 374 | void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer, |
375 | int bytes, int offset) | ||
375 | { | 376 | { |
376 | struct inode *inode = page->mapping->host; | 377 | struct inode *inode = page->mapping->host; |
377 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | 378 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; |
378 | int bytes, i, offset = 0, sparse = 0; | ||
379 | struct squashfs_cache_entry *buffer = NULL; | ||
380 | void *pageaddr; | 379 | void *pageaddr; |
381 | 380 | int i, mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; | |
382 | int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; | 381 | int start_index = page->index & ~mask, end_index = start_index | mask; |
383 | int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); | ||
384 | int start_index = page->index & ~mask; | ||
385 | int end_index = start_index | mask; | ||
386 | int file_end = i_size_read(inode) >> msblk->block_log; | ||
387 | |||
388 | TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", | ||
389 | page->index, squashfs_i(inode)->start); | ||
390 | |||
391 | if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> | ||
392 | PAGE_CACHE_SHIFT)) | ||
393 | goto out; | ||
394 | |||
395 | if (index < file_end || squashfs_i(inode)->fragment_block == | ||
396 | SQUASHFS_INVALID_BLK) { | ||
397 | /* | ||
398 | * Reading a datablock from disk. Need to read block list | ||
399 | * to get location and block size. | ||
400 | */ | ||
401 | u64 block = 0; | ||
402 | int bsize = read_blocklist(inode, index, &block); | ||
403 | if (bsize < 0) | ||
404 | goto error_out; | ||
405 | |||
406 | if (bsize == 0) { /* hole */ | ||
407 | bytes = index == file_end ? | ||
408 | (i_size_read(inode) & (msblk->block_size - 1)) : | ||
409 | msblk->block_size; | ||
410 | sparse = 1; | ||
411 | } else { | ||
412 | /* | ||
413 | * Read and decompress datablock. | ||
414 | */ | ||
415 | buffer = squashfs_get_datablock(inode->i_sb, | ||
416 | block, bsize); | ||
417 | if (buffer->error) { | ||
418 | ERROR("Unable to read page, block %llx, size %x" | ||
419 | "\n", block, bsize); | ||
420 | squashfs_cache_put(buffer); | ||
421 | goto error_out; | ||
422 | } | ||
423 | bytes = buffer->length; | ||
424 | } | ||
425 | } else { | ||
426 | /* | ||
427 | * Datablock is stored inside a fragment (tail-end packed | ||
428 | * block). | ||
429 | */ | ||
430 | buffer = squashfs_get_fragment(inode->i_sb, | ||
431 | squashfs_i(inode)->fragment_block, | ||
432 | squashfs_i(inode)->fragment_size); | ||
433 | |||
434 | if (buffer->error) { | ||
435 | ERROR("Unable to read page, block %llx, size %x\n", | ||
436 | squashfs_i(inode)->fragment_block, | ||
437 | squashfs_i(inode)->fragment_size); | ||
438 | squashfs_cache_put(buffer); | ||
439 | goto error_out; | ||
440 | } | ||
441 | bytes = i_size_read(inode) & (msblk->block_size - 1); | ||
442 | offset = squashfs_i(inode)->fragment_offset; | ||
443 | } | ||
444 | 382 | ||
445 | /* | 383 | /* |
446 | * Loop copying datablock into pages. As the datablock likely covers | 384 | * Loop copying datablock into pages. As the datablock likely covers |
@@ -451,7 +389,7 @@ static int squashfs_readpage(struct file *file, struct page *page) | |||
451 | for (i = start_index; i <= end_index && bytes > 0; i++, | 389 | for (i = start_index; i <= end_index && bytes > 0; i++, |
452 | bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { | 390 | bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { |
453 | struct page *push_page; | 391 | struct page *push_page; |
454 | int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE); | 392 | int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0; |
455 | 393 | ||
456 | TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); | 394 | TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); |
457 | 395 | ||
@@ -475,11 +413,75 @@ skip_page: | |||
475 | if (i != page->index) | 413 | if (i != page->index) |
476 | page_cache_release(push_page); | 414 | page_cache_release(push_page); |
477 | } | 415 | } |
416 | } | ||
417 | |||
418 | /* Read datablock stored packed inside a fragment (tail-end packed block) */ | ||
419 | static int squashfs_readpage_fragment(struct page *page) | ||
420 | { | ||
421 | struct inode *inode = page->mapping->host; | ||
422 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
423 | struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb, | ||
424 | squashfs_i(inode)->fragment_block, | ||
425 | squashfs_i(inode)->fragment_size); | ||
426 | int res = buffer->error; | ||
427 | |||
428 | if (res) | ||
429 | ERROR("Unable to read page, block %llx, size %x\n", | ||
430 | squashfs_i(inode)->fragment_block, | ||
431 | squashfs_i(inode)->fragment_size); | ||
432 | else | ||
433 | squashfs_copy_cache(page, buffer, i_size_read(inode) & | ||
434 | (msblk->block_size - 1), | ||
435 | squashfs_i(inode)->fragment_offset); | ||
436 | |||
437 | squashfs_cache_put(buffer); | ||
438 | return res; | ||
439 | } | ||
478 | 440 | ||
479 | if (!sparse) | 441 | static int squashfs_readpage_sparse(struct page *page, int index, int file_end) |
480 | squashfs_cache_put(buffer); | 442 | { |
443 | struct inode *inode = page->mapping->host; | ||
444 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
445 | int bytes = index == file_end ? | ||
446 | (i_size_read(inode) & (msblk->block_size - 1)) : | ||
447 | msblk->block_size; | ||
481 | 448 | ||
449 | squashfs_copy_cache(page, NULL, bytes, 0); | ||
482 | return 0; | 450 | return 0; |
451 | } | ||
452 | |||
453 | static int squashfs_readpage(struct file *file, struct page *page) | ||
454 | { | ||
455 | struct inode *inode = page->mapping->host; | ||
456 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
457 | int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); | ||
458 | int file_end = i_size_read(inode) >> msblk->block_log; | ||
459 | int res; | ||
460 | void *pageaddr; | ||
461 | |||
462 | TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", | ||
463 | page->index, squashfs_i(inode)->start); | ||
464 | |||
465 | if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> | ||
466 | PAGE_CACHE_SHIFT)) | ||
467 | goto out; | ||
468 | |||
469 | if (index < file_end || squashfs_i(inode)->fragment_block == | ||
470 | SQUASHFS_INVALID_BLK) { | ||
471 | u64 block = 0; | ||
472 | int bsize = read_blocklist(inode, index, &block); | ||
473 | if (bsize < 0) | ||
474 | goto error_out; | ||
475 | |||
476 | if (bsize == 0) | ||
477 | res = squashfs_readpage_sparse(page, index, file_end); | ||
478 | else | ||
479 | res = squashfs_readpage_block(page, block, bsize); | ||
480 | } else | ||
481 | res = squashfs_readpage_fragment(page); | ||
482 | |||
483 | if (!res) | ||
484 | return 0; | ||
483 | 485 | ||
484 | error_out: | 486 | error_out: |
485 | SetPageError(page); | 487 | SetPageError(page); |
diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c new file mode 100644 index 000000000000..f2310d2a2019 --- /dev/null +++ b/fs/squashfs/file_cache.c | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
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/fs.h> | ||
10 | #include <linux/vfs.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <linux/pagemap.h> | ||
15 | #include <linux/mutex.h> | ||
16 | |||
17 | #include "squashfs_fs.h" | ||
18 | #include "squashfs_fs_sb.h" | ||
19 | #include "squashfs_fs_i.h" | ||
20 | #include "squashfs.h" | ||
21 | |||
22 | /* Read separately compressed datablock and memcopy into page cache */ | ||
23 | int squashfs_readpage_block(struct page *page, u64 block, int bsize) | ||
24 | { | ||
25 | struct inode *i = page->mapping->host; | ||
26 | struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, | ||
27 | block, bsize); | ||
28 | int res = buffer->error; | ||
29 | |||
30 | if (res) | ||
31 | ERROR("Unable to read page, block %llx, size %x\n", block, | ||
32 | bsize); | ||
33 | else | ||
34 | squashfs_copy_cache(page, buffer, buffer->length, 0); | ||
35 | |||
36 | squashfs_cache_put(buffer); | ||
37 | return res; | ||
38 | } | ||
diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c new file mode 100644 index 000000000000..2943b2bfae48 --- /dev/null +++ b/fs/squashfs/file_direct.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
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/fs.h> | ||
10 | #include <linux/vfs.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <linux/pagemap.h> | ||
15 | #include <linux/mutex.h> | ||
16 | |||
17 | #include "squashfs_fs.h" | ||
18 | #include "squashfs_fs_sb.h" | ||
19 | #include "squashfs_fs_i.h" | ||
20 | #include "squashfs.h" | ||
21 | #include "page_actor.h" | ||
22 | |||
23 | static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, | ||
24 | int pages, struct page **page); | ||
25 | |||
26 | /* Read separately compressed datablock directly into page cache */ | ||
27 | int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) | ||
28 | |||
29 | { | ||
30 | struct inode *inode = target_page->mapping->host; | ||
31 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
32 | |||
33 | int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT; | ||
34 | int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; | ||
35 | int start_index = target_page->index & ~mask; | ||
36 | int end_index = start_index | mask; | ||
37 | int i, n, pages, missing_pages, bytes, res = -ENOMEM; | ||
38 | struct page **page; | ||
39 | struct squashfs_page_actor *actor; | ||
40 | void *pageaddr; | ||
41 | |||
42 | if (end_index > file_end) | ||
43 | end_index = file_end; | ||
44 | |||
45 | pages = end_index - start_index + 1; | ||
46 | |||
47 | page = kmalloc(sizeof(void *) * pages, GFP_KERNEL); | ||
48 | if (page == NULL) | ||
49 | return res; | ||
50 | |||
51 | /* | ||
52 | * Create a "page actor" which will kmap and kunmap the | ||
53 | * page cache pages appropriately within the decompressor | ||
54 | */ | ||
55 | actor = squashfs_page_actor_init_special(page, pages, 0); | ||
56 | if (actor == NULL) | ||
57 | goto out; | ||
58 | |||
59 | /* Try to grab all the pages covered by the Squashfs block */ | ||
60 | for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) { | ||
61 | page[i] = (n == target_page->index) ? target_page : | ||
62 | grab_cache_page_nowait(target_page->mapping, n); | ||
63 | |||
64 | if (page[i] == NULL) { | ||
65 | missing_pages++; | ||
66 | continue; | ||
67 | } | ||
68 | |||
69 | if (PageUptodate(page[i])) { | ||
70 | unlock_page(page[i]); | ||
71 | page_cache_release(page[i]); | ||
72 | page[i] = NULL; | ||
73 | missing_pages++; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | if (missing_pages) { | ||
78 | /* | ||
79 | * Couldn't get one or more pages, this page has either | ||
80 | * been VM reclaimed, but others are still in the page cache | ||
81 | * and uptodate, or we're racing with another thread in | ||
82 | * squashfs_readpage also trying to grab them. Fall back to | ||
83 | * using an intermediate buffer. | ||
84 | */ | ||
85 | res = squashfs_read_cache(target_page, block, bsize, pages, | ||
86 | page); | ||
87 | goto out; | ||
88 | } | ||
89 | |||
90 | /* Decompress directly into the page cache buffers */ | ||
91 | res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); | ||
92 | if (res < 0) | ||
93 | goto mark_errored; | ||
94 | |||
95 | /* Last page may have trailing bytes not filled */ | ||
96 | bytes = res % PAGE_CACHE_SIZE; | ||
97 | if (bytes) { | ||
98 | pageaddr = kmap_atomic(page[pages - 1]); | ||
99 | memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); | ||
100 | kunmap_atomic(pageaddr); | ||
101 | } | ||
102 | |||
103 | /* Mark pages as uptodate, unlock and release */ | ||
104 | for (i = 0; i < pages; i++) { | ||
105 | flush_dcache_page(page[i]); | ||
106 | SetPageUptodate(page[i]); | ||
107 | unlock_page(page[i]); | ||
108 | if (page[i] != target_page) | ||
109 | page_cache_release(page[i]); | ||
110 | } | ||
111 | |||
112 | kfree(actor); | ||
113 | kfree(page); | ||
114 | |||
115 | return 0; | ||
116 | |||
117 | mark_errored: | ||
118 | /* Decompression failed, mark pages as errored. Target_page is | ||
119 | * dealt with by the caller | ||
120 | */ | ||
121 | for (i = 0; i < pages; i++) { | ||
122 | if (page[i] == target_page) | ||
123 | continue; | ||
124 | flush_dcache_page(page[i]); | ||
125 | SetPageError(page[i]); | ||
126 | unlock_page(page[i]); | ||
127 | page_cache_release(page[i]); | ||
128 | } | ||
129 | |||
130 | out: | ||
131 | kfree(actor); | ||
132 | kfree(page); | ||
133 | return res; | ||
134 | } | ||
135 | |||
136 | |||
137 | static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, | ||
138 | int pages, struct page **page) | ||
139 | { | ||
140 | struct inode *i = target_page->mapping->host; | ||
141 | struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, | ||
142 | block, bsize); | ||
143 | int bytes = buffer->length, res = buffer->error, n, offset = 0; | ||
144 | void *pageaddr; | ||
145 | |||
146 | if (res) { | ||
147 | ERROR("Unable to read page, block %llx, size %x\n", block, | ||
148 | bsize); | ||
149 | goto out; | ||
150 | } | ||
151 | |||
152 | for (n = 0; n < pages && bytes > 0; n++, | ||
153 | bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { | ||
154 | int avail = min_t(int, bytes, PAGE_CACHE_SIZE); | ||
155 | |||
156 | if (page[n] == NULL) | ||
157 | continue; | ||
158 | |||
159 | pageaddr = kmap_atomic(page[n]); | ||
160 | squashfs_copy_data(pageaddr, buffer, offset, avail); | ||
161 | memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); | ||
162 | kunmap_atomic(pageaddr); | ||
163 | flush_dcache_page(page[n]); | ||
164 | SetPageUptodate(page[n]); | ||
165 | unlock_page(page[n]); | ||
166 | if (page[n] != target_page) | ||
167 | page_cache_release(page[n]); | ||
168 | } | ||
169 | |||
170 | out: | ||
171 | squashfs_cache_put(buffer); | ||
172 | return res; | ||
173 | } | ||
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c index 00f4dfc5f088..244b9fbfff7b 100644 --- a/fs/squashfs/lzo_wrapper.c +++ b/fs/squashfs/lzo_wrapper.c | |||
@@ -31,13 +31,14 @@ | |||
31 | #include "squashfs_fs_sb.h" | 31 | #include "squashfs_fs_sb.h" |
32 | #include "squashfs.h" | 32 | #include "squashfs.h" |
33 | #include "decompressor.h" | 33 | #include "decompressor.h" |
34 | #include "page_actor.h" | ||
34 | 35 | ||
35 | struct squashfs_lzo { | 36 | struct squashfs_lzo { |
36 | void *input; | 37 | void *input; |
37 | void *output; | 38 | void *output; |
38 | }; | 39 | }; |
39 | 40 | ||
40 | static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len) | 41 | static void *lzo_init(struct squashfs_sb_info *msblk, void *buff) |
41 | { | 42 | { |
42 | int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); | 43 | int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); |
43 | 44 | ||
@@ -74,22 +75,16 @@ static void lzo_free(void *strm) | |||
74 | } | 75 | } |
75 | 76 | ||
76 | 77 | ||
77 | static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer, | 78 | static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, |
78 | struct buffer_head **bh, int b, int offset, int length, int srclength, | 79 | struct buffer_head **bh, int b, int offset, int length, |
79 | int pages) | 80 | struct squashfs_page_actor *output) |
80 | { | 81 | { |
81 | struct squashfs_lzo *stream = msblk->stream; | 82 | struct squashfs_lzo *stream = strm; |
82 | void *buff = stream->input; | 83 | void *buff = stream->input, *data; |
83 | int avail, i, bytes = length, res; | 84 | int avail, i, bytes = length, res; |
84 | size_t out_len = srclength; | 85 | size_t out_len = output->length; |
85 | |||
86 | mutex_lock(&msblk->read_data_mutex); | ||
87 | 86 | ||
88 | for (i = 0; i < b; i++) { | 87 | for (i = 0; i < b; i++) { |
89 | wait_on_buffer(bh[i]); | ||
90 | if (!buffer_uptodate(bh[i])) | ||
91 | goto block_release; | ||
92 | |||
93 | avail = min(bytes, msblk->devblksize - offset); | 88 | avail = min(bytes, msblk->devblksize - offset); |
94 | memcpy(buff, bh[i]->b_data + offset, avail); | 89 | memcpy(buff, bh[i]->b_data + offset, avail); |
95 | buff += avail; | 90 | buff += avail; |
@@ -104,24 +99,24 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer, | |||
104 | goto failed; | 99 | goto failed; |
105 | 100 | ||
106 | res = bytes = (int)out_len; | 101 | res = bytes = (int)out_len; |
107 | for (i = 0, buff = stream->output; bytes && i < pages; i++) { | 102 | data = squashfs_first_page(output); |
108 | avail = min_t(int, bytes, PAGE_CACHE_SIZE); | 103 | buff = stream->output; |
109 | memcpy(buffer[i], buff, avail); | 104 | while (data) { |
110 | buff += avail; | 105 | if (bytes <= PAGE_CACHE_SIZE) { |
111 | bytes -= avail; | 106 | memcpy(data, buff, bytes); |
107 | break; | ||
108 | } else { | ||
109 | memcpy(data, buff, PAGE_CACHE_SIZE); | ||
110 | buff += PAGE_CACHE_SIZE; | ||
111 | bytes -= PAGE_CACHE_SIZE; | ||
112 | data = squashfs_next_page(output); | ||
113 | } | ||
112 | } | 114 | } |
115 | squashfs_finish_page(output); | ||
113 | 116 | ||
114 | mutex_unlock(&msblk->read_data_mutex); | ||
115 | return res; | 117 | return res; |
116 | 118 | ||
117 | block_release: | ||
118 | for (; i < b; i++) | ||
119 | put_bh(bh[i]); | ||
120 | |||
121 | failed: | 119 | failed: |
122 | mutex_unlock(&msblk->read_data_mutex); | ||
123 | |||
124 | ERROR("lzo decompression failed, data probably corrupt\n"); | ||
125 | return -EIO; | 120 | return -EIO; |
126 | } | 121 | } |
127 | 122 | ||
diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c new file mode 100644 index 000000000000..5a1c11f56441 --- /dev/null +++ b/fs/squashfs/page_actor.c | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
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> | ||
12 | #include "page_actor.h" | ||
13 | |||
14 | /* | ||
15 | * This file contains implementations of page_actor for decompressing into | ||
16 | * an intermediate buffer, and for decompressing directly into the | ||
17 | * page cache. | ||
18 | * | ||
19 | * Calling code should avoid sleeping between calls to squashfs_first_page() | ||
20 | * and squashfs_finish_page(). | ||
21 | */ | ||
22 | |||
23 | /* Implementation of page_actor for decompressing into intermediate buffer */ | ||
24 | static void *cache_first_page(struct squashfs_page_actor *actor) | ||
25 | { | ||
26 | actor->next_page = 1; | ||
27 | return actor->buffer[0]; | ||
28 | } | ||
29 | |||
30 | static void *cache_next_page(struct squashfs_page_actor *actor) | ||
31 | { | ||
32 | if (actor->next_page == actor->pages) | ||
33 | return NULL; | ||
34 | |||
35 | return actor->buffer[actor->next_page++]; | ||
36 | } | ||
37 | |||
38 | static void cache_finish_page(struct squashfs_page_actor *actor) | ||
39 | { | ||
40 | /* empty */ | ||
41 | } | ||
42 | |||
43 | struct squashfs_page_actor *squashfs_page_actor_init(void **buffer, | ||
44 | int pages, int length) | ||
45 | { | ||
46 | struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); | ||
47 | |||
48 | if (actor == NULL) | ||
49 | return NULL; | ||
50 | |||
51 | actor->length = length ? : pages * PAGE_CACHE_SIZE; | ||
52 | actor->buffer = buffer; | ||
53 | actor->pages = pages; | ||
54 | actor->next_page = 0; | ||
55 | actor->squashfs_first_page = cache_first_page; | ||
56 | actor->squashfs_next_page = cache_next_page; | ||
57 | actor->squashfs_finish_page = cache_finish_page; | ||
58 | return actor; | ||
59 | } | ||
60 | |||
61 | /* Implementation of page_actor for decompressing directly into page cache. */ | ||
62 | static void *direct_first_page(struct squashfs_page_actor *actor) | ||
63 | { | ||
64 | actor->next_page = 1; | ||
65 | return actor->pageaddr = kmap_atomic(actor->page[0]); | ||
66 | } | ||
67 | |||
68 | static void *direct_next_page(struct squashfs_page_actor *actor) | ||
69 | { | ||
70 | if (actor->pageaddr) | ||
71 | kunmap_atomic(actor->pageaddr); | ||
72 | |||
73 | return actor->pageaddr = actor->next_page == actor->pages ? NULL : | ||
74 | kmap_atomic(actor->page[actor->next_page++]); | ||
75 | } | ||
76 | |||
77 | static void direct_finish_page(struct squashfs_page_actor *actor) | ||
78 | { | ||
79 | if (actor->pageaddr) | ||
80 | kunmap_atomic(actor->pageaddr); | ||
81 | } | ||
82 | |||
83 | struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page, | ||
84 | int pages, int length) | ||
85 | { | ||
86 | struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); | ||
87 | |||
88 | if (actor == NULL) | ||
89 | return NULL; | ||
90 | |||
91 | actor->length = length ? : pages * PAGE_CACHE_SIZE; | ||
92 | actor->page = page; | ||
93 | actor->pages = pages; | ||
94 | actor->next_page = 0; | ||
95 | actor->pageaddr = NULL; | ||
96 | actor->squashfs_first_page = direct_first_page; | ||
97 | actor->squashfs_next_page = direct_next_page; | ||
98 | actor->squashfs_finish_page = direct_finish_page; | ||
99 | return actor; | ||
100 | } | ||
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h new file mode 100644 index 000000000000..26dd82008b82 --- /dev/null +++ b/fs/squashfs/page_actor.h | |||
@@ -0,0 +1,81 @@ | |||
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 | #ifndef CONFIG_SQUASHFS_FILE_DIRECT | ||
12 | struct squashfs_page_actor { | ||
13 | void **page; | ||
14 | int pages; | ||
15 | int length; | ||
16 | int next_page; | ||
17 | }; | ||
18 | |||
19 | static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page, | ||
20 | int pages, int length) | ||
21 | { | ||
22 | struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); | ||
23 | |||
24 | if (actor == NULL) | ||
25 | return NULL; | ||
26 | |||
27 | actor->length = length ? : pages * PAGE_CACHE_SIZE; | ||
28 | actor->page = page; | ||
29 | actor->pages = pages; | ||
30 | actor->next_page = 0; | ||
31 | return actor; | ||
32 | } | ||
33 | |||
34 | static inline void *squashfs_first_page(struct squashfs_page_actor *actor) | ||
35 | { | ||
36 | actor->next_page = 1; | ||
37 | return actor->page[0]; | ||
38 | } | ||
39 | |||
40 | static inline void *squashfs_next_page(struct squashfs_page_actor *actor) | ||
41 | { | ||
42 | return actor->next_page == actor->pages ? NULL : | ||
43 | actor->page[actor->next_page++]; | ||
44 | } | ||
45 | |||
46 | static inline void squashfs_finish_page(struct squashfs_page_actor *actor) | ||
47 | { | ||
48 | /* empty */ | ||
49 | } | ||
50 | #else | ||
51 | struct squashfs_page_actor { | ||
52 | union { | ||
53 | void **buffer; | ||
54 | struct page **page; | ||
55 | }; | ||
56 | void *pageaddr; | ||
57 | void *(*squashfs_first_page)(struct squashfs_page_actor *); | ||
58 | void *(*squashfs_next_page)(struct squashfs_page_actor *); | ||
59 | void (*squashfs_finish_page)(struct squashfs_page_actor *); | ||
60 | int pages; | ||
61 | int length; | ||
62 | int next_page; | ||
63 | }; | ||
64 | |||
65 | extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int); | ||
66 | extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page | ||
67 | **, int, int); | ||
68 | static inline void *squashfs_first_page(struct squashfs_page_actor *actor) | ||
69 | { | ||
70 | return actor->squashfs_first_page(actor); | ||
71 | } | ||
72 | static inline void *squashfs_next_page(struct squashfs_page_actor *actor) | ||
73 | { | ||
74 | return actor->squashfs_next_page(actor); | ||
75 | } | ||
76 | static inline void squashfs_finish_page(struct squashfs_page_actor *actor) | ||
77 | { | ||
78 | actor->squashfs_finish_page(actor); | ||
79 | } | ||
80 | #endif | ||
81 | #endif | ||
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index d1266516ed08..9e1bb79f7e6f 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h | |||
@@ -28,8 +28,8 @@ | |||
28 | #define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args) | 28 | #define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args) |
29 | 29 | ||
30 | /* block.c */ | 30 | /* block.c */ |
31 | extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *, | 31 | extern int squashfs_read_data(struct super_block *, u64, int, u64 *, |
32 | int, int); | 32 | struct squashfs_page_actor *); |
33 | 33 | ||
34 | /* cache.c */ | 34 | /* cache.c */ |
35 | extern struct squashfs_cache *squashfs_cache_init(char *, int, int); | 35 | extern struct squashfs_cache *squashfs_cache_init(char *, int, int); |
@@ -48,7 +48,14 @@ extern void *squashfs_read_table(struct super_block *, u64, int); | |||
48 | 48 | ||
49 | /* decompressor.c */ | 49 | /* decompressor.c */ |
50 | extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int); | 50 | extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int); |
51 | extern void *squashfs_decompressor_init(struct super_block *, unsigned short); | 51 | extern void *squashfs_decompressor_setup(struct super_block *, unsigned short); |
52 | |||
53 | /* decompressor_xxx.c */ | ||
54 | extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *); | ||
55 | extern void squashfs_decompressor_destroy(struct squashfs_sb_info *); | ||
56 | extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **, | ||
57 | int, int, int, struct squashfs_page_actor *); | ||
58 | extern int squashfs_max_decompressors(void); | ||
52 | 59 | ||
53 | /* export.c */ | 60 | /* export.c */ |
54 | extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64, | 61 | extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64, |
@@ -59,6 +66,13 @@ extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *); | |||
59 | extern __le64 *squashfs_read_fragment_index_table(struct super_block *, | 66 | extern __le64 *squashfs_read_fragment_index_table(struct super_block *, |
60 | u64, u64, unsigned int); | 67 | u64, u64, unsigned int); |
61 | 68 | ||
69 | /* file.c */ | ||
70 | void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int, | ||
71 | int); | ||
72 | |||
73 | /* file_xxx.c */ | ||
74 | extern int squashfs_readpage_block(struct page *, u64, int); | ||
75 | |||
62 | /* id.c */ | 76 | /* id.c */ |
63 | extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); | 77 | extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); |
64 | extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64, | 78 | extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64, |
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h index 52934a22f296..1da565cb50c3 100644 --- a/fs/squashfs/squashfs_fs_sb.h +++ b/fs/squashfs/squashfs_fs_sb.h | |||
@@ -50,6 +50,7 @@ struct squashfs_cache_entry { | |||
50 | wait_queue_head_t wait_queue; | 50 | wait_queue_head_t wait_queue; |
51 | struct squashfs_cache *cache; | 51 | struct squashfs_cache *cache; |
52 | void **data; | 52 | void **data; |
53 | struct squashfs_page_actor *actor; | ||
53 | }; | 54 | }; |
54 | 55 | ||
55 | struct squashfs_sb_info { | 56 | struct squashfs_sb_info { |
@@ -63,10 +64,9 @@ struct squashfs_sb_info { | |||
63 | __le64 *id_table; | 64 | __le64 *id_table; |
64 | __le64 *fragment_index; | 65 | __le64 *fragment_index; |
65 | __le64 *xattr_id_table; | 66 | __le64 *xattr_id_table; |
66 | struct mutex read_data_mutex; | ||
67 | struct mutex meta_index_mutex; | 67 | struct mutex meta_index_mutex; |
68 | struct meta_index *meta_index; | 68 | struct meta_index *meta_index; |
69 | void *stream; | 69 | struct squashfs_stream *stream; |
70 | __le64 *inode_lookup_table; | 70 | __le64 *inode_lookup_table; |
71 | u64 inode_table; | 71 | u64 inode_table; |
72 | u64 directory_table; | 72 | u64 directory_table; |
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 60553a9053ca..202df6312d4e 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c | |||
@@ -98,7 +98,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) | |||
98 | msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE); | 98 | msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE); |
99 | msblk->devblksize_log2 = ffz(~msblk->devblksize); | 99 | msblk->devblksize_log2 = ffz(~msblk->devblksize); |
100 | 100 | ||
101 | mutex_init(&msblk->read_data_mutex); | ||
102 | mutex_init(&msblk->meta_index_mutex); | 101 | mutex_init(&msblk->meta_index_mutex); |
103 | 102 | ||
104 | /* | 103 | /* |
@@ -206,13 +205,14 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) | |||
206 | goto failed_mount; | 205 | goto failed_mount; |
207 | 206 | ||
208 | /* Allocate read_page block */ | 207 | /* Allocate read_page block */ |
209 | msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size); | 208 | msblk->read_page = squashfs_cache_init("data", |
209 | squashfs_max_decompressors(), msblk->block_size); | ||
210 | if (msblk->read_page == NULL) { | 210 | if (msblk->read_page == NULL) { |
211 | ERROR("Failed to allocate read_page block\n"); | 211 | ERROR("Failed to allocate read_page block\n"); |
212 | goto failed_mount; | 212 | goto failed_mount; |
213 | } | 213 | } |
214 | 214 | ||
215 | msblk->stream = squashfs_decompressor_init(sb, flags); | 215 | msblk->stream = squashfs_decompressor_setup(sb, flags); |
216 | if (IS_ERR(msblk->stream)) { | 216 | if (IS_ERR(msblk->stream)) { |
217 | err = PTR_ERR(msblk->stream); | 217 | err = PTR_ERR(msblk->stream); |
218 | msblk->stream = NULL; | 218 | msblk->stream = NULL; |
@@ -336,7 +336,7 @@ failed_mount: | |||
336 | squashfs_cache_delete(msblk->block_cache); | 336 | squashfs_cache_delete(msblk->block_cache); |
337 | squashfs_cache_delete(msblk->fragment_cache); | 337 | squashfs_cache_delete(msblk->fragment_cache); |
338 | squashfs_cache_delete(msblk->read_page); | 338 | squashfs_cache_delete(msblk->read_page); |
339 | squashfs_decompressor_free(msblk, msblk->stream); | 339 | squashfs_decompressor_destroy(msblk); |
340 | kfree(msblk->inode_lookup_table); | 340 | kfree(msblk->inode_lookup_table); |
341 | kfree(msblk->fragment_index); | 341 | kfree(msblk->fragment_index); |
342 | kfree(msblk->id_table); | 342 | kfree(msblk->id_table); |
@@ -383,7 +383,7 @@ static void squashfs_put_super(struct super_block *sb) | |||
383 | squashfs_cache_delete(sbi->block_cache); | 383 | squashfs_cache_delete(sbi->block_cache); |
384 | squashfs_cache_delete(sbi->fragment_cache); | 384 | squashfs_cache_delete(sbi->fragment_cache); |
385 | squashfs_cache_delete(sbi->read_page); | 385 | squashfs_cache_delete(sbi->read_page); |
386 | squashfs_decompressor_free(sbi, sbi->stream); | 386 | squashfs_decompressor_destroy(sbi); |
387 | kfree(sbi->id_table); | 387 | kfree(sbi->id_table); |
388 | kfree(sbi->fragment_index); | 388 | kfree(sbi->fragment_index); |
389 | kfree(sbi->meta_index); | 389 | kfree(sbi->meta_index); |
diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c index 1760b7d108f6..c609624e4b8a 100644 --- a/fs/squashfs/xz_wrapper.c +++ b/fs/squashfs/xz_wrapper.c | |||
@@ -32,44 +32,70 @@ | |||
32 | #include "squashfs_fs_sb.h" | 32 | #include "squashfs_fs_sb.h" |
33 | #include "squashfs.h" | 33 | #include "squashfs.h" |
34 | #include "decompressor.h" | 34 | #include "decompressor.h" |
35 | #include "page_actor.h" | ||
35 | 36 | ||
36 | struct squashfs_xz { | 37 | struct squashfs_xz { |
37 | struct xz_dec *state; | 38 | struct xz_dec *state; |
38 | struct xz_buf buf; | 39 | struct xz_buf buf; |
39 | }; | 40 | }; |
40 | 41 | ||
41 | struct comp_opts { | 42 | struct disk_comp_opts { |
42 | __le32 dictionary_size; | 43 | __le32 dictionary_size; |
43 | __le32 flags; | 44 | __le32 flags; |
44 | }; | 45 | }; |
45 | 46 | ||
46 | static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff, | 47 | struct comp_opts { |
47 | int len) | 48 | int dict_size; |
49 | }; | ||
50 | |||
51 | static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, | ||
52 | void *buff, int len) | ||
48 | { | 53 | { |
49 | struct comp_opts *comp_opts = buff; | 54 | struct disk_comp_opts *comp_opts = buff; |
50 | struct squashfs_xz *stream; | 55 | struct comp_opts *opts; |
51 | int dict_size = msblk->block_size; | 56 | int err = 0, n; |
52 | int err, n; | 57 | |
58 | opts = kmalloc(sizeof(*opts), GFP_KERNEL); | ||
59 | if (opts == NULL) { | ||
60 | err = -ENOMEM; | ||
61 | goto out2; | ||
62 | } | ||
53 | 63 | ||
54 | if (comp_opts) { | 64 | if (comp_opts) { |
55 | /* check compressor options are the expected length */ | 65 | /* check compressor options are the expected length */ |
56 | if (len < sizeof(*comp_opts)) { | 66 | if (len < sizeof(*comp_opts)) { |
57 | err = -EIO; | 67 | err = -EIO; |
58 | goto failed; | 68 | goto out; |
59 | } | 69 | } |
60 | 70 | ||
61 | dict_size = le32_to_cpu(comp_opts->dictionary_size); | 71 | opts->dict_size = le32_to_cpu(comp_opts->dictionary_size); |
62 | 72 | ||
63 | /* the dictionary size should be 2^n or 2^n+2^(n+1) */ | 73 | /* the dictionary size should be 2^n or 2^n+2^(n+1) */ |
64 | n = ffs(dict_size) - 1; | 74 | n = ffs(opts->dict_size) - 1; |
65 | if (dict_size != (1 << n) && dict_size != (1 << n) + | 75 | if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) + |
66 | (1 << (n + 1))) { | 76 | (1 << (n + 1))) { |
67 | err = -EIO; | 77 | err = -EIO; |
68 | goto failed; | 78 | goto out; |
69 | } | 79 | } |
70 | } | 80 | } else |
81 | /* use defaults */ | ||
82 | opts->dict_size = max_t(int, msblk->block_size, | ||
83 | SQUASHFS_METADATA_SIZE); | ||
84 | |||
85 | return opts; | ||
86 | |||
87 | out: | ||
88 | kfree(opts); | ||
89 | out2: | ||
90 | return ERR_PTR(err); | ||
91 | } | ||
92 | |||
71 | 93 | ||
72 | dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE); | 94 | static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff) |
95 | { | ||
96 | struct comp_opts *comp_opts = buff; | ||
97 | struct squashfs_xz *stream; | ||
98 | int err; | ||
73 | 99 | ||
74 | stream = kmalloc(sizeof(*stream), GFP_KERNEL); | 100 | stream = kmalloc(sizeof(*stream), GFP_KERNEL); |
75 | if (stream == NULL) { | 101 | if (stream == NULL) { |
@@ -77,7 +103,7 @@ static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff, | |||
77 | goto failed; | 103 | goto failed; |
78 | } | 104 | } |
79 | 105 | ||
80 | stream->state = xz_dec_init(XZ_PREALLOC, dict_size); | 106 | stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size); |
81 | if (stream->state == NULL) { | 107 | if (stream->state == NULL) { |
82 | kfree(stream); | 108 | kfree(stream); |
83 | err = -ENOMEM; | 109 | err = -ENOMEM; |
@@ -103,42 +129,37 @@ static void squashfs_xz_free(void *strm) | |||
103 | } | 129 | } |
104 | 130 | ||
105 | 131 | ||
106 | static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer, | 132 | static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, |
107 | struct buffer_head **bh, int b, int offset, int length, int srclength, | 133 | struct buffer_head **bh, int b, int offset, int length, |
108 | int pages) | 134 | struct squashfs_page_actor *output) |
109 | { | 135 | { |
110 | enum xz_ret xz_err; | 136 | enum xz_ret xz_err; |
111 | int avail, total = 0, k = 0, page = 0; | 137 | int avail, total = 0, k = 0; |
112 | struct squashfs_xz *stream = msblk->stream; | 138 | struct squashfs_xz *stream = strm; |
113 | |||
114 | mutex_lock(&msblk->read_data_mutex); | ||
115 | 139 | ||
116 | xz_dec_reset(stream->state); | 140 | xz_dec_reset(stream->state); |
117 | stream->buf.in_pos = 0; | 141 | stream->buf.in_pos = 0; |
118 | stream->buf.in_size = 0; | 142 | stream->buf.in_size = 0; |
119 | stream->buf.out_pos = 0; | 143 | stream->buf.out_pos = 0; |
120 | stream->buf.out_size = PAGE_CACHE_SIZE; | 144 | stream->buf.out_size = PAGE_CACHE_SIZE; |
121 | stream->buf.out = buffer[page++]; | 145 | stream->buf.out = squashfs_first_page(output); |
122 | 146 | ||
123 | do { | 147 | do { |
124 | if (stream->buf.in_pos == stream->buf.in_size && k < b) { | 148 | if (stream->buf.in_pos == stream->buf.in_size && k < b) { |
125 | avail = min(length, msblk->devblksize - offset); | 149 | avail = min(length, msblk->devblksize - offset); |
126 | length -= avail; | 150 | length -= avail; |
127 | wait_on_buffer(bh[k]); | ||
128 | if (!buffer_uptodate(bh[k])) | ||
129 | goto release_mutex; | ||
130 | |||
131 | stream->buf.in = bh[k]->b_data + offset; | 151 | stream->buf.in = bh[k]->b_data + offset; |
132 | stream->buf.in_size = avail; | 152 | stream->buf.in_size = avail; |
133 | stream->buf.in_pos = 0; | 153 | stream->buf.in_pos = 0; |
134 | offset = 0; | 154 | offset = 0; |
135 | } | 155 | } |
136 | 156 | ||
137 | if (stream->buf.out_pos == stream->buf.out_size | 157 | if (stream->buf.out_pos == stream->buf.out_size) { |
138 | && page < pages) { | 158 | stream->buf.out = squashfs_next_page(output); |
139 | stream->buf.out = buffer[page++]; | 159 | if (stream->buf.out != NULL) { |
140 | stream->buf.out_pos = 0; | 160 | stream->buf.out_pos = 0; |
141 | total += PAGE_CACHE_SIZE; | 161 | total += PAGE_CACHE_SIZE; |
162 | } | ||
142 | } | 163 | } |
143 | 164 | ||
144 | xz_err = xz_dec_run(stream->state, &stream->buf); | 165 | xz_err = xz_dec_run(stream->state, &stream->buf); |
@@ -147,23 +168,14 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer, | |||
147 | put_bh(bh[k++]); | 168 | put_bh(bh[k++]); |
148 | } while (xz_err == XZ_OK); | 169 | } while (xz_err == XZ_OK); |
149 | 170 | ||
150 | if (xz_err != XZ_STREAM_END) { | 171 | squashfs_finish_page(output); |
151 | ERROR("xz_dec_run error, data probably corrupt\n"); | ||
152 | goto release_mutex; | ||
153 | } | ||
154 | |||
155 | if (k < b) { | ||
156 | ERROR("xz_uncompress error, input remaining\n"); | ||
157 | goto release_mutex; | ||
158 | } | ||
159 | 172 | ||
160 | total += stream->buf.out_pos; | 173 | if (xz_err != XZ_STREAM_END || k < b) |
161 | mutex_unlock(&msblk->read_data_mutex); | 174 | goto out; |
162 | return total; | ||
163 | 175 | ||
164 | release_mutex: | 176 | return total + stream->buf.out_pos; |
165 | mutex_unlock(&msblk->read_data_mutex); | ||
166 | 177 | ||
178 | out: | ||
167 | for (; k < b; k++) | 179 | for (; k < b; k++) |
168 | put_bh(bh[k]); | 180 | put_bh(bh[k]); |
169 | 181 | ||
@@ -172,6 +184,7 @@ release_mutex: | |||
172 | 184 | ||
173 | const struct squashfs_decompressor squashfs_xz_comp_ops = { | 185 | const struct squashfs_decompressor squashfs_xz_comp_ops = { |
174 | .init = squashfs_xz_init, | 186 | .init = squashfs_xz_init, |
187 | .comp_opts = squashfs_xz_comp_opts, | ||
175 | .free = squashfs_xz_free, | 188 | .free = squashfs_xz_free, |
176 | .decompress = squashfs_xz_uncompress, | 189 | .decompress = squashfs_xz_uncompress, |
177 | .id = XZ_COMPRESSION, | 190 | .id = XZ_COMPRESSION, |
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c index 55d918fd2d86..8727caba6882 100644 --- a/fs/squashfs/zlib_wrapper.c +++ b/fs/squashfs/zlib_wrapper.c | |||
@@ -32,8 +32,9 @@ | |||
32 | #include "squashfs_fs_sb.h" | 32 | #include "squashfs_fs_sb.h" |
33 | #include "squashfs.h" | 33 | #include "squashfs.h" |
34 | #include "decompressor.h" | 34 | #include "decompressor.h" |
35 | #include "page_actor.h" | ||
35 | 36 | ||
36 | static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len) | 37 | static void *zlib_init(struct squashfs_sb_info *dummy, void *buff) |
37 | { | 38 | { |
38 | z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); | 39 | z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); |
39 | if (stream == NULL) | 40 | if (stream == NULL) |
@@ -61,44 +62,37 @@ static void zlib_free(void *strm) | |||
61 | } | 62 | } |
62 | 63 | ||
63 | 64 | ||
64 | static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer, | 65 | static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, |
65 | struct buffer_head **bh, int b, int offset, int length, int srclength, | 66 | struct buffer_head **bh, int b, int offset, int length, |
66 | int pages) | 67 | struct squashfs_page_actor *output) |
67 | { | 68 | { |
68 | int zlib_err, zlib_init = 0; | 69 | int zlib_err, zlib_init = 0, k = 0; |
69 | int k = 0, page = 0; | 70 | z_stream *stream = strm; |
70 | z_stream *stream = msblk->stream; | ||
71 | |||
72 | mutex_lock(&msblk->read_data_mutex); | ||
73 | 71 | ||
74 | stream->avail_out = 0; | 72 | stream->avail_out = PAGE_CACHE_SIZE; |
73 | stream->next_out = squashfs_first_page(output); | ||
75 | stream->avail_in = 0; | 74 | stream->avail_in = 0; |
76 | 75 | ||
77 | do { | 76 | do { |
78 | if (stream->avail_in == 0 && k < b) { | 77 | if (stream->avail_in == 0 && k < b) { |
79 | int avail = min(length, msblk->devblksize - offset); | 78 | int avail = min(length, msblk->devblksize - offset); |
80 | length -= avail; | 79 | length -= avail; |
81 | wait_on_buffer(bh[k]); | ||
82 | if (!buffer_uptodate(bh[k])) | ||
83 | goto release_mutex; | ||
84 | |||
85 | stream->next_in = bh[k]->b_data + offset; | 80 | stream->next_in = bh[k]->b_data + offset; |
86 | stream->avail_in = avail; | 81 | stream->avail_in = avail; |
87 | offset = 0; | 82 | offset = 0; |
88 | } | 83 | } |
89 | 84 | ||
90 | if (stream->avail_out == 0 && page < pages) { | 85 | if (stream->avail_out == 0) { |
91 | stream->next_out = buffer[page++]; | 86 | stream->next_out = squashfs_next_page(output); |
92 | stream->avail_out = PAGE_CACHE_SIZE; | 87 | if (stream->next_out != NULL) |
88 | stream->avail_out = PAGE_CACHE_SIZE; | ||
93 | } | 89 | } |
94 | 90 | ||
95 | if (!zlib_init) { | 91 | if (!zlib_init) { |
96 | zlib_err = zlib_inflateInit(stream); | 92 | zlib_err = zlib_inflateInit(stream); |
97 | if (zlib_err != Z_OK) { | 93 | if (zlib_err != Z_OK) { |
98 | ERROR("zlib_inflateInit returned unexpected " | 94 | squashfs_finish_page(output); |
99 | "result 0x%x, srclength %d\n", | 95 | goto out; |
100 | zlib_err, srclength); | ||
101 | goto release_mutex; | ||
102 | } | 96 | } |
103 | zlib_init = 1; | 97 | zlib_init = 1; |
104 | } | 98 | } |
@@ -109,29 +103,21 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer, | |||
109 | put_bh(bh[k++]); | 103 | put_bh(bh[k++]); |
110 | } while (zlib_err == Z_OK); | 104 | } while (zlib_err == Z_OK); |
111 | 105 | ||
112 | if (zlib_err != Z_STREAM_END) { | 106 | squashfs_finish_page(output); |
113 | ERROR("zlib_inflate error, data probably corrupt\n"); | ||
114 | goto release_mutex; | ||
115 | } | ||
116 | 107 | ||
117 | zlib_err = zlib_inflateEnd(stream); | 108 | if (zlib_err != Z_STREAM_END) |
118 | if (zlib_err != Z_OK) { | 109 | goto out; |
119 | ERROR("zlib_inflate error, data probably corrupt\n"); | ||
120 | goto release_mutex; | ||
121 | } | ||
122 | 110 | ||
123 | if (k < b) { | 111 | zlib_err = zlib_inflateEnd(stream); |
124 | ERROR("zlib_uncompress error, data remaining\n"); | 112 | if (zlib_err != Z_OK) |
125 | goto release_mutex; | 113 | goto out; |
126 | } | ||
127 | 114 | ||
128 | length = stream->total_out; | 115 | if (k < b) |
129 | mutex_unlock(&msblk->read_data_mutex); | 116 | goto out; |
130 | return length; | ||
131 | 117 | ||
132 | release_mutex: | 118 | return stream->total_out; |
133 | mutex_unlock(&msblk->read_data_mutex); | ||
134 | 119 | ||
120 | out: | ||
135 | for (; k < b; k++) | 121 | for (; k < b; k++) |
136 | put_bh(bh[k]); | 122 | put_bh(bh[k]); |
137 | 123 | ||