diff options
| author | Phillip Lougher <phillip@squashfs.org.uk> | 2013-10-31 15:24:27 -0400 |
|---|---|---|
| committer | Phillip Lougher <phillip@squashfs.org.uk> | 2013-11-19 22:59:07 -0500 |
| commit | 5f55dbc0c5c466a9cdfa4da7ac1bfe351c7fc52a (patch) | |
| tree | dccfe66c090bfd05c75f5d6a1cc31bf138bd76b1 | |
| parent | 846b730e99518a1c9945afcb2afbe4d08a02ed80 (diff) | |
Squashfs: Restructure squashfs_readpage()
Restructure squashfs_readpage() splitting it into separate
functions for datablocks, fragments and sparse blocks.
Move the memcpying (from squashfs cache entry) implementation of
squashfs_readpage_block into file_cache.c
This allows different implementations to be supported.
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
Reviewed-by: Minchan Kim <minchan@kernel.org>
| -rw-r--r-- | fs/squashfs/Makefile | 2 | ||||
| -rw-r--r-- | fs/squashfs/file.c | 142 | ||||
| -rw-r--r-- | fs/squashfs/file_cache.c | 38 | ||||
| -rw-r--r-- | fs/squashfs/squashfs.h | 7 |
4 files changed, 118 insertions, 71 deletions
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index 5833b96ee69c..e01ba1126c89 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 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 file_cache.o |
| 8 | squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o | 8 | squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o |
| 9 | squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o | 9 | squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o |
| 10 | squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o | 10 | squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o |
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/squashfs.h b/fs/squashfs/squashfs.h index 6a97e63ca173..9e1bb79f7e6f 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h | |||
| @@ -66,6 +66,13 @@ extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *); | |||
| 66 | extern __le64 *squashfs_read_fragment_index_table(struct super_block *, | 66 | extern __le64 *squashfs_read_fragment_index_table(struct super_block *, |
| 67 | u64, u64, unsigned int); | 67 | u64, u64, unsigned int); |
| 68 | 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 | |||
| 69 | /* id.c */ | 76 | /* id.c */ |
| 70 | 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 *); |
| 71 | 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, |
