diff options
Diffstat (limited to 'fs/squashfs/file.c')
-rw-r--r-- | fs/squashfs/file.c | 140 |
1 files changed, 105 insertions, 35 deletions
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index e5c9689062ba..6f5ef8d7e55a 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c | |||
@@ -47,12 +47,16 @@ | |||
47 | #include <linux/string.h> | 47 | #include <linux/string.h> |
48 | #include <linux/pagemap.h> | 48 | #include <linux/pagemap.h> |
49 | #include <linux/mutex.h> | 49 | #include <linux/mutex.h> |
50 | #include <linux/mm_inline.h> | ||
50 | 51 | ||
51 | #include "squashfs_fs.h" | 52 | #include "squashfs_fs.h" |
52 | #include "squashfs_fs_sb.h" | 53 | #include "squashfs_fs_sb.h" |
53 | #include "squashfs_fs_i.h" | 54 | #include "squashfs_fs_i.h" |
54 | #include "squashfs.h" | 55 | #include "squashfs.h" |
55 | 56 | ||
57 | // Backported from 4.5 | ||
58 | #define lru_to_page(head) (list_entry((head)->prev, struct page, lru)) | ||
59 | |||
56 | /* | 60 | /* |
57 | * Locate cache slot in range [offset, index] for specified inode. If | 61 | * Locate cache slot in range [offset, index] for specified inode. If |
58 | * there's more than one return the slot closest to index. | 62 | * there's more than one return the slot closest to index. |
@@ -438,6 +442,21 @@ static int squashfs_readpage_fragment(struct page *page) | |||
438 | return res; | 442 | return res; |
439 | } | 443 | } |
440 | 444 | ||
445 | static int squashfs_readpages_fragment(struct page *page, | ||
446 | struct list_head *readahead_pages, struct address_space *mapping) | ||
447 | { | ||
448 | if (!page) { | ||
449 | page = lru_to_page(readahead_pages); | ||
450 | list_del(&page->lru); | ||
451 | if (add_to_page_cache_lru(page, mapping, page->index, | ||
452 | mapping_gfp_constraint(mapping, GFP_KERNEL))) { | ||
453 | put_page(page); | ||
454 | return 0; | ||
455 | } | ||
456 | } | ||
457 | return squashfs_readpage_fragment(page); | ||
458 | } | ||
459 | |||
441 | static int squashfs_readpage_sparse(struct page *page, int index, int file_end) | 460 | static int squashfs_readpage_sparse(struct page *page, int index, int file_end) |
442 | { | 461 | { |
443 | struct inode *inode = page->mapping->host; | 462 | struct inode *inode = page->mapping->host; |
@@ -450,54 +469,105 @@ static int squashfs_readpage_sparse(struct page *page, int index, int file_end) | |||
450 | return 0; | 469 | return 0; |
451 | } | 470 | } |
452 | 471 | ||
453 | static int squashfs_readpage(struct file *file, struct page *page) | 472 | static int squashfs_readpages_sparse(struct page *page, |
473 | struct list_head *readahead_pages, int index, int file_end, | ||
474 | struct address_space *mapping) | ||
454 | { | 475 | { |
455 | struct inode *inode = page->mapping->host; | 476 | if (!page) { |
477 | page = lru_to_page(readahead_pages); | ||
478 | list_del(&page->lru); | ||
479 | if (add_to_page_cache_lru(page, mapping, page->index, | ||
480 | mapping_gfp_constraint(mapping, GFP_KERNEL))) { | ||
481 | put_page(page); | ||
482 | return 0; | ||
483 | } | ||
484 | } | ||
485 | return squashfs_readpage_sparse(page, index, file_end); | ||
486 | } | ||
487 | |||
488 | static int __squashfs_readpages(struct file *file, struct page *page, | ||
489 | struct list_head *readahead_pages, unsigned int nr_pages, | ||
490 | struct address_space *mapping) | ||
491 | { | ||
492 | struct inode *inode = mapping->host; | ||
456 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | 493 | 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; | 494 | int file_end = i_size_read(inode) >> msblk->block_log; |
459 | int res; | 495 | int res; |
460 | void *pageaddr; | ||
461 | 496 | ||
462 | TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", | 497 | do { |
463 | page->index, squashfs_i(inode)->start); | 498 | struct page *cur_page = page ? page |
499 | : lru_to_page(readahead_pages); | ||
500 | int page_index = cur_page->index; | ||
501 | int index = page_index >> (msblk->block_log - PAGE_CACHE_SHIFT); | ||
502 | |||
503 | if (page_index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> | ||
504 | PAGE_CACHE_SHIFT)) | ||
505 | return 1; | ||
506 | |||
507 | if (index < file_end || squashfs_i(inode)->fragment_block == | ||
508 | SQUASHFS_INVALID_BLK) { | ||
509 | u64 block = 0; | ||
510 | int bsize = read_blocklist(inode, index, &block); | ||
511 | |||
512 | if (bsize < 0) | ||
513 | return -1; | ||
514 | |||
515 | if (bsize == 0) { | ||
516 | res = squashfs_readpages_sparse(page, | ||
517 | readahead_pages, index, file_end, | ||
518 | mapping); | ||
519 | } else { | ||
520 | res = squashfs_readpages_block(page, | ||
521 | readahead_pages, &nr_pages, mapping, | ||
522 | page_index, block, bsize); | ||
523 | } | ||
524 | } else { | ||
525 | res = squashfs_readpages_fragment(page, | ||
526 | readahead_pages, mapping); | ||
527 | } | ||
528 | if (res) | ||
529 | return 0; | ||
530 | page = NULL; | ||
531 | } while (readahead_pages && !list_empty(readahead_pages)); | ||
464 | 532 | ||
465 | if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> | 533 | return 0; |
466 | PAGE_CACHE_SHIFT)) | 534 | } |
467 | goto out; | ||
468 | 535 | ||
469 | if (index < file_end || squashfs_i(inode)->fragment_block == | 536 | static int squashfs_readpage(struct file *file, struct page *page) |
470 | SQUASHFS_INVALID_BLK) { | 537 | { |
471 | u64 block = 0; | 538 | int ret; |
472 | int bsize = read_blocklist(inode, index, &block); | ||
473 | if (bsize < 0) | ||
474 | goto error_out; | ||
475 | 539 | ||
476 | if (bsize == 0) | 540 | TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", |
477 | res = squashfs_readpage_sparse(page, index, file_end); | 541 | page->index, squashfs_i(page->mapping->host)->start); |
542 | |||
543 | get_page(page); | ||
544 | |||
545 | ret = __squashfs_readpages(file, page, NULL, 1, page->mapping); | ||
546 | if (ret) { | ||
547 | flush_dcache_page(page); | ||
548 | if (ret < 0) | ||
549 | SetPageError(page); | ||
478 | else | 550 | else |
479 | res = squashfs_readpage_block(page, block, bsize); | 551 | SetPageUptodate(page); |
480 | } else | 552 | zero_user_segment(page, 0, PAGE_CACHE_SIZE); |
481 | res = squashfs_readpage_fragment(page); | 553 | unlock_page(page); |
482 | 554 | put_page(page); | |
483 | if (!res) | 555 | } |
484 | return 0; | ||
485 | |||
486 | error_out: | ||
487 | SetPageError(page); | ||
488 | out: | ||
489 | pageaddr = kmap_atomic(page); | ||
490 | memset(pageaddr, 0, PAGE_CACHE_SIZE); | ||
491 | kunmap_atomic(pageaddr); | ||
492 | flush_dcache_page(page); | ||
493 | if (!PageError(page)) | ||
494 | SetPageUptodate(page); | ||
495 | unlock_page(page); | ||
496 | 556 | ||
497 | return 0; | 557 | return 0; |
498 | } | 558 | } |
499 | 559 | ||
560 | static int squashfs_readpages(struct file *file, struct address_space *mapping, | ||
561 | struct list_head *pages, unsigned int nr_pages) | ||
562 | { | ||
563 | TRACE("Entered squashfs_readpages, %u pages, first page index %lx\n", | ||
564 | nr_pages, lru_to_page(pages)->index); | ||
565 | __squashfs_readpages(file, NULL, pages, nr_pages, mapping); | ||
566 | return 0; | ||
567 | } | ||
568 | |||
500 | 569 | ||
501 | const struct address_space_operations squashfs_aops = { | 570 | const struct address_space_operations squashfs_aops = { |
502 | .readpage = squashfs_readpage | 571 | .readpage = squashfs_readpage, |
572 | .readpages = squashfs_readpages, | ||
503 | }; | 573 | }; |