diff options
| author | Ingo Molnar <mingo@kernel.org> | 2013-12-17 09:27:08 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2013-12-17 09:27:08 -0500 |
| commit | bb799d3b980eb803ca2da4a4eefbd9308f8d988a (patch) | |
| tree | 69fbe0cd6d47b23a50f5e1d87bf7489532fae149 /fs/squashfs | |
| parent | 919fc6e34831d1c2b58bfb5ae261dc3facc9b269 (diff) | |
| parent | 319e2e3f63c348a9b66db4667efa73178e18b17d (diff) | |
Merge tag 'v3.13-rc4' into core/locking
Merge Linux 3.13-rc4, to refresh this rather old tree with the latest fixes.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'fs/squashfs')
| -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 | 176 | ||||
| -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, 1148 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..62a0de6632e1 --- /dev/null +++ b/fs/squashfs/file_direct.c | |||
| @@ -0,0 +1,176 @@ | |||
| 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 | if (res < 0) | ||
| 88 | goto mark_errored; | ||
| 89 | |||
| 90 | goto out; | ||
| 91 | } | ||
| 92 | |||
| 93 | /* Decompress directly into the page cache buffers */ | ||
| 94 | res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); | ||
| 95 | if (res < 0) | ||
| 96 | goto mark_errored; | ||
| 97 | |||
| 98 | /* Last page may have trailing bytes not filled */ | ||
| 99 | bytes = res % PAGE_CACHE_SIZE; | ||
| 100 | if (bytes) { | ||
| 101 | pageaddr = kmap_atomic(page[pages - 1]); | ||
| 102 | memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); | ||
| 103 | kunmap_atomic(pageaddr); | ||
| 104 | } | ||
| 105 | |||
| 106 | /* Mark pages as uptodate, unlock and release */ | ||
| 107 | for (i = 0; i < pages; i++) { | ||
| 108 | flush_dcache_page(page[i]); | ||
| 109 | SetPageUptodate(page[i]); | ||
| 110 | unlock_page(page[i]); | ||
| 111 | if (page[i] != target_page) | ||
| 112 | page_cache_release(page[i]); | ||
| 113 | } | ||
| 114 | |||
| 115 | kfree(actor); | ||
| 116 | kfree(page); | ||
| 117 | |||
| 118 | return 0; | ||
| 119 | |||
| 120 | mark_errored: | ||
| 121 | /* Decompression failed, mark pages as errored. Target_page is | ||
| 122 | * dealt with by the caller | ||
| 123 | */ | ||
| 124 | for (i = 0; i < pages; i++) { | ||
| 125 | if (page[i] == NULL || page[i] == target_page) | ||
| 126 | continue; | ||
| 127 | flush_dcache_page(page[i]); | ||
| 128 | SetPageError(page[i]); | ||
| 129 | unlock_page(page[i]); | ||
| 130 | page_cache_release(page[i]); | ||
| 131 | } | ||
| 132 | |||
| 133 | out: | ||
| 134 | kfree(actor); | ||
| 135 | kfree(page); | ||
| 136 | return res; | ||
| 137 | } | ||
| 138 | |||
| 139 | |||
| 140 | static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, | ||
| 141 | int pages, struct page **page) | ||
| 142 | { | ||
| 143 | struct inode *i = target_page->mapping->host; | ||
| 144 | struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, | ||
| 145 | block, bsize); | ||
| 146 | int bytes = buffer->length, res = buffer->error, n, offset = 0; | ||
| 147 | void *pageaddr; | ||
| 148 | |||
| 149 | if (res) { | ||
| 150 | ERROR("Unable to read page, block %llx, size %x\n", block, | ||
| 151 | bsize); | ||
| 152 | goto out; | ||
| 153 | } | ||
| 154 | |||
| 155 | for (n = 0; n < pages && bytes > 0; n++, | ||
| 156 | bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { | ||
| 157 | int avail = min_t(int, bytes, PAGE_CACHE_SIZE); | ||
| 158 | |||
| 159 | if (page[n] == NULL) | ||
| 160 | continue; | ||
| 161 | |||
| 162 | pageaddr = kmap_atomic(page[n]); | ||
| 163 | squashfs_copy_data(pageaddr, buffer, offset, avail); | ||
| 164 | memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); | ||
| 165 | kunmap_atomic(pageaddr); | ||
| 166 | flush_dcache_page(page[n]); | ||
| 167 | SetPageUptodate(page[n]); | ||
| 168 | unlock_page(page[n]); | ||
| 169 | if (page[n] != target_page) | ||
| 170 | page_cache_release(page[n]); | ||
| 171 | } | ||
| 172 | |||
| 173 | out: | ||
| 174 | squashfs_cache_put(buffer); | ||
| 175 | return res; | ||
| 176 | } | ||
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 | ||
