| /* |
| * fs/mpage.c |
| * |
| * Copyright (C) 2002, Linus Torvalds. |
| * |
| * Contains functions related to preparing and submitting BIOs which contain |
| * multiple pagecache pages. |
| * |
| * 15May2002 Andrew Morton |
| * Initial version |
| * 27Jun2002 axboe@suse.de |
| * use bio_add_page() to build bio's just the right size |
| */ |
| |
| /* |
| * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| /************************************************************************/ |
| /* */ |
| /* PROJECT : exFAT & FAT12/16/32 File System */ |
| /* FILE : core.c */ |
| /* PURPOSE : sdFAT glue layer for supporting VFS */ |
| /* */ |
| /*----------------------------------------------------------------------*/ |
| /* NOTES */ |
| /* */ |
| /* */ |
| /************************************************************************/ |
| |
| #include <linux/version.h> |
| #include <linux/module.h> |
| #include <linux/time.h> |
| #include <linux/buffer_head.h> |
| #include <linux/exportfs.h> |
| #include <linux/mount.h> |
| #include <linux/vfs.h> |
| #include <linux/parser.h> |
| #include <linux/uio.h> |
| #include <linux/writeback.h> |
| #include <linux/log2.h> |
| #include <linux/hash.h> |
| #include <linux/backing-dev.h> |
| #include <linux/sched.h> |
| #include <linux/fs_struct.h> |
| #include <linux/namei.h> |
| #include <linux/bio.h> |
| #include <linux/blkdev.h> |
| #include <linux/swap.h> /* for mark_page_accessed() */ |
| #include <asm/current.h> |
| #include <asm/unaligned.h> |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) |
| #include <linux/aio.h> |
| #endif |
| |
| #include "sdfat.h" |
| |
| #ifdef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE |
| |
| #define MIN_ALIGNED_SIZE (PAGE_SIZE) |
| #define MIN_ALIGNED_SIZE_MASK (MIN_ALIGNED_SIZE - 1) |
| |
| /************************************************************************* |
| * INNER FUNCTIONS FOR FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY |
| *************************************************************************/ |
| static void __mpage_write_end_io(struct bio *bio, int err); |
| |
| /************************************************************************* |
| * FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY |
| *************************************************************************/ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) |
| /* EMPTY */ |
| #else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) */ |
| static inline void bio_set_dev(struct bio *bio, struct block_device *bdev) |
| { |
| bio->bi_bdev = bdev; |
| } |
| #endif |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) |
| static inline void __sdfat_clean_bdev_aliases(struct block_device *bdev, sector_t block) |
| { |
| clean_bdev_aliases(bdev, block, 1); |
| } |
| #else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0) */ |
| static inline void __sdfat_clean_bdev_aliases(struct block_device *bdev, sector_t block) |
| { |
| unmap_underlying_metadata(bdev, block); |
| } |
| |
| static inline int wbc_to_write_flags(struct writeback_control *wbc) |
| { |
| if (wbc->sync_mode == WB_SYNC_ALL) |
| return WRITE_SYNC; |
| |
| return 0; |
| } |
| #endif |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) |
| static inline void __sdfat_submit_bio_write2(int flags, struct bio *bio) |
| { |
| bio_set_op_attrs(bio, REQ_OP_WRITE, flags); |
| submit_bio(bio); |
| } |
| #else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) */ |
| static inline void __sdfat_submit_bio_write2(int flags, struct bio *bio) |
| { |
| submit_bio(WRITE | flags, bio); |
| } |
| #endif |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) |
| static inline int bio_get_nr_vecs(struct block_device *bdev) |
| { |
| return BIO_MAX_PAGES; |
| } |
| #else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) */ |
| /* EMPTY */ |
| #endif |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) |
| static inline sector_t __sdfat_bio_sector(struct bio *bio) |
| { |
| return bio->bi_iter.bi_sector; |
| } |
| |
| static inline void __sdfat_set_bio_sector(struct bio *bio, sector_t sector) |
| { |
| bio->bi_iter.bi_sector = sector; |
| } |
| |
| static inline unsigned int __sdfat_bio_size(struct bio *bio) |
| { |
| return bio->bi_iter.bi_size; |
| } |
| |
| static inline void __sdfat_set_bio_size(struct bio *bio, unsigned int size) |
| { |
| bio->bi_iter.bi_size = size; |
| } |
| #else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */ |
| static inline sector_t __sdfat_bio_sector(struct bio *bio) |
| { |
| return bio->bi_sector; |
| } |
| |
| static inline void __sdfat_set_bio_sector(struct bio *bio, sector_t sector) |
| { |
| bio->bi_sector = sector; |
| } |
| |
| static inline unsigned int __sdfat_bio_size(struct bio *bio) |
| { |
| return bio->bi_size; |
| } |
| |
| static inline void __sdfat_set_bio_size(struct bio *bio, unsigned int size) |
| { |
| bio->bi_size = size; |
| } |
| #endif |
| |
| /************************************************************************* |
| * MORE FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY |
| *************************************************************************/ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) |
| static void mpage_write_end_io(struct bio *bio) |
| { |
| __mpage_write_end_io(bio, blk_status_to_errno(bio->bi_status)); |
| } |
| #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) |
| static void mpage_write_end_io(struct bio *bio) |
| { |
| __mpage_write_end_io(bio, bio->bi_error); |
| } |
| #else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,3,0) */ |
| static void mpage_write_end_io(struct bio *bio, int err) |
| { |
| if (test_bit(BIO_UPTODATE, &bio->bi_flags)) |
| err = 0; |
| __mpage_write_end_io(bio, err); |
| } |
| #endif |
| |
| /* __check_dfr_on() and __dfr_writepage_end_io() functions |
| * are copied from sdfat.c |
| * Each function should be same perfectly |
| */ |
| static inline int __check_dfr_on(struct inode *inode, loff_t start, loff_t end, const char *fname) |
| { |
| #ifdef CONFIG_SDFAT_DFR |
| struct defrag_info *ino_dfr = &(SDFAT_I(inode)->dfr_info); |
| |
| if ((atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ) && |
| fsapi_dfr_check_dfr_on(inode, start, end, 0, fname)) |
| return 1; |
| #endif |
| return 0; |
| } |
| |
| static inline int __dfr_writepage_end_io(struct page *page) |
| { |
| #ifdef CONFIG_SDFAT_DFR |
| struct defrag_info *ino_dfr = &(SDFAT_I(page->mapping->host)->dfr_info); |
| |
| if (atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ) |
| fsapi_dfr_writepage_endio(page); |
| #endif |
| return 0; |
| } |
| |
| |
| static inline unsigned int __calc_size_to_align(struct super_block *sb) |
| { |
| struct block_device *bdev = sb->s_bdev; |
| struct gendisk *disk; |
| struct request_queue *queue; |
| struct queue_limits *limit; |
| unsigned int max_sectors; |
| unsigned int aligned = 0; |
| |
| disk = bdev->bd_disk; |
| if (!disk) |
| goto out; |
| |
| queue = disk->queue; |
| if (!queue) |
| goto out; |
| |
| limit = &queue->limits; |
| max_sectors = limit->max_sectors; |
| aligned = 1 << ilog2(max_sectors); |
| |
| if (aligned && (max_sectors & (aligned - 1))) |
| aligned = 0; |
| |
| if (aligned && aligned < (MIN_ALIGNED_SIZE >> SECTOR_SIZE_BITS)) |
| aligned = 0; |
| out: |
| return aligned; |
| } |
| |
| struct mpage_data { |
| struct bio *bio; |
| sector_t last_block_in_bio; |
| get_block_t *get_block; |
| unsigned int use_writepage; |
| unsigned int size_to_align; |
| }; |
| |
| /* |
| * After completing I/O on a page, call this routine to update the page |
| * flags appropriately |
| */ |
| static void __page_write_endio(struct page *page, int err) |
| { |
| if (err) { |
| struct address_space *mapping; |
| |
| SetPageError(page); |
| mapping = page_mapping(page); |
| if (mapping) |
| mapping_set_error(mapping, err); |
| } |
| __dfr_writepage_end_io(page); |
| end_page_writeback(page); |
| } |
| |
| /* |
| * I/O completion handler for multipage BIOs. |
| * |
| * The mpage code never puts partial pages into a BIO (except for end-of-file). |
| * If a page does not map to a contiguous run of blocks then it simply falls |
| * back to block_read_full_page(). |
| * |
| * Why is this? If a page's completion depends on a number of different BIOs |
| * which can complete in any order (or at the same time) then determining the |
| * status of that page is hard. See end_buffer_async_read() for the details. |
| * There is no point in duplicating all that complexity. |
| */ |
| static void __mpage_write_end_io(struct bio *bio, int err) |
| { |
| struct bio_vec *bv; |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) |
| struct bvec_iter_all iter_all; |
| |
| ASSERT(bio_data_dir(bio) == WRITE); /* only write */ |
| |
| /* Use bio_for_each_segemnt_all() to support multi-page bvec */ |
| bio_for_each_segment_all(bv, bio, iter_all) |
| __page_write_endio(bv->bv_page, err); |
| #elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) |
| struct bvec_iter_all iter_all; |
| int i; |
| |
| ASSERT(bio_data_dir(bio) == WRITE); /* only write */ |
| |
| /* Use bio_for_each_segemnt_all() to support multi-page bvec */ |
| bio_for_each_segment_all(bv, bio, i, iter_all) |
| __page_write_endio(bv->bv_page, err); |
| #else |
| ASSERT(bio_data_dir(bio) == WRITE); /* only write */ |
| bv = bio->bi_io_vec + bio->bi_vcnt - 1; |
| |
| do { |
| struct page *page = bv->bv_page; |
| |
| if (--bv >= bio->bi_io_vec) |
| prefetchw(&bv->bv_page->flags); |
| |
| __page_write_endio(page, err); |
| } while (bv >= bio->bi_io_vec); |
| #endif |
| bio_put(bio); |
| } |
| |
| static struct bio *mpage_bio_submit_write(int flags, struct bio *bio) |
| { |
| bio->bi_end_io = mpage_write_end_io; |
| __sdfat_submit_bio_write2(flags, bio); |
| return NULL; |
| } |
| |
| static struct bio * |
| mpage_alloc(struct block_device *bdev, |
| sector_t first_sector, int nr_vecs, |
| gfp_t gfp_flags) |
| { |
| struct bio *bio; |
| |
| bio = bio_alloc(gfp_flags, nr_vecs); |
| |
| if (bio == NULL && (current->flags & PF_MEMALLOC)) { |
| while (!bio && (nr_vecs /= 2)) |
| bio = bio_alloc(gfp_flags, nr_vecs); |
| } |
| |
| if (bio) { |
| bio_set_dev(bio, bdev); |
| __sdfat_set_bio_sector(bio, first_sector); |
| } |
| return bio; |
| } |
| |
| |
| #if IS_BUILTIN(CONFIG_SDFAT_FS) |
| #define __write_boundary_block write_boundary_block |
| #define sdfat_buffer_heads_over_limit buffer_heads_over_limit |
| #else |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) |
| /* |
| * Called when we've recently written block `bblock', and it is known that |
| * `bblock' was for a buffer_boundary() buffer. This means that the block at |
| * `bblock + 1' is probably a dirty indirect block. Hunt it down and, if it's |
| * dirty, schedule it for IO. So that indirects merge nicely with their data. |
| */ |
| static void __write_boundary_block(struct block_device *bdev, |
| sector_t bblock, unsigned int blocksize) |
| { |
| struct buffer_head *bh = __find_get_block(bdev, bblock + 1, blocksize); |
| |
| if (bh) { |
| if (buffer_dirty(bh)) |
| ll_rw_block(REQ_OP_WRITE, 0, 1, &bh); |
| put_bh(bh); |
| } |
| } |
| #else |
| #warning "Need an alternative of write_boundary_block function" |
| #define __write_boundary_block write_boundary_block |
| #endif |
| |
| #warning "sdfat could not check buffer_heads_over_limit on module. Assumed zero" |
| #define sdfat_buffer_heads_over_limit (0) |
| #endif |
| |
| static void clean_buffers(struct page *page, unsigned int first_unmapped) |
| { |
| unsigned int buffer_counter = 0; |
| struct buffer_head *bh, *head; |
| |
| if (!page_has_buffers(page)) |
| return; |
| head = page_buffers(page); |
| bh = head; |
| |
| do { |
| if (buffer_counter++ == first_unmapped) |
| break; |
| clear_buffer_dirty(bh); |
| bh = bh->b_this_page; |
| } while (bh != head); |
| |
| /* |
| * we cannot drop the bh if the page is not uptodate or a concurrent |
| * readpage would fail to serialize with the bh and it would read from |
| * disk before we reach the platter. |
| */ |
| if (sdfat_buffer_heads_over_limit && PageUptodate(page)) |
| try_to_free_buffers(page); |
| } |
| |
| static int sdfat_mpage_writepage(struct page *page, |
| struct writeback_control *wbc, void *data) |
| { |
| struct mpage_data *mpd = data; |
| struct bio *bio = mpd->bio; |
| struct address_space *mapping = page->mapping; |
| struct inode *inode = page->mapping->host; |
| const unsigned int blkbits = inode->i_blkbits; |
| const unsigned int blocks_per_page = PAGE_SIZE >> blkbits; |
| sector_t last_block; |
| sector_t block_in_file; |
| sector_t blocks[MAX_BUF_PER_PAGE]; |
| unsigned int page_block; |
| unsigned int first_unmapped = blocks_per_page; |
| struct block_device *bdev = NULL; |
| int boundary = 0; |
| sector_t boundary_block = 0; |
| struct block_device *boundary_bdev = NULL; |
| int length; |
| struct buffer_head map_bh; |
| loff_t i_size = i_size_read(inode); |
| unsigned long end_index = i_size >> PAGE_SHIFT; |
| int ret = 0; |
| int op_flags = wbc_to_write_flags(wbc); |
| |
| if (page_has_buffers(page)) { |
| struct buffer_head *head = page_buffers(page); |
| struct buffer_head *bh = head; |
| |
| /* If they're all mapped and dirty, do it */ |
| page_block = 0; |
| do { |
| BUG_ON(buffer_locked(bh)); |
| if (!buffer_mapped(bh)) { |
| /* |
| * unmapped dirty buffers are created by |
| * __set_page_dirty_buffers -> mmapped data |
| */ |
| if (buffer_dirty(bh)) |
| goto confused; |
| if (first_unmapped == blocks_per_page) |
| first_unmapped = page_block; |
| continue; |
| } |
| |
| if (first_unmapped != blocks_per_page) |
| goto confused; /* hole -> non-hole */ |
| |
| if (!buffer_dirty(bh) || !buffer_uptodate(bh)) |
| goto confused; |
| |
| /* bh should be mapped if delay is set */ |
| if (buffer_delay(bh)) { |
| sector_t blk_in_file = |
| (sector_t)(page->index << (PAGE_SHIFT - blkbits)) + page_block; |
| |
| BUG_ON(bh->b_size != (1 << blkbits)); |
| if (page->index > end_index) { |
| MMSG("%s(inode:%p) " |
| "over end with delayed buffer" |
| "(page_idx:%u, end_idx:%u)\n", |
| __func__, inode, |
| (u32)page->index, |
| (u32)end_index); |
| goto confused; |
| } |
| |
| ret = mpd->get_block(inode, blk_in_file, bh, 1); |
| if (ret) { |
| MMSG("%s(inode:%p) " |
| "failed to getblk(ret:%d)\n", |
| __func__, inode, ret); |
| goto confused; |
| } |
| |
| BUG_ON(buffer_delay(bh)); |
| |
| if (buffer_new(bh)) { |
| clear_buffer_new(bh); |
| __sdfat_clean_bdev_aliases(bh->b_bdev, bh->b_blocknr); |
| } |
| } |
| |
| if (page_block) { |
| if (bh->b_blocknr != blocks[page_block-1] + 1) { |
| MMSG("%s(inode:%p) pblk(%d) " |
| "no_seq(prev:%lld, new:%lld)\n", |
| __func__, inode, page_block, |
| (u64)blocks[page_block-1], |
| (u64)bh->b_blocknr); |
| goto confused; |
| } |
| } |
| blocks[page_block++] = bh->b_blocknr; |
| boundary = buffer_boundary(bh); |
| if (boundary) { |
| boundary_block = bh->b_blocknr; |
| boundary_bdev = bh->b_bdev; |
| } |
| bdev = bh->b_bdev; |
| } while ((bh = bh->b_this_page) != head); |
| |
| if (first_unmapped) |
| goto page_is_mapped; |
| |
| /* |
| * Page has buffers, but they are all unmapped. The page was |
| * created by pagein or read over a hole which was handled by |
| * block_read_full_page(). If this address_space is also |
| * using mpage_readpages then this can rarely happen. |
| */ |
| goto confused; |
| } |
| |
| /* |
| * The page has no buffers: map it to disk |
| */ |
| BUG_ON(!PageUptodate(page)); |
| block_in_file = (sector_t)page->index << (PAGE_SHIFT - blkbits); |
| last_block = (i_size - 1) >> blkbits; |
| map_bh.b_page = page; |
| for (page_block = 0; page_block < blocks_per_page; ) { |
| |
| map_bh.b_state = 0; |
| map_bh.b_size = 1 << blkbits; |
| if (mpd->get_block(inode, block_in_file, &map_bh, 1)) |
| goto confused; |
| |
| if (buffer_new(&map_bh)) |
| __sdfat_clean_bdev_aliases(map_bh.b_bdev, map_bh.b_blocknr); |
| if (buffer_boundary(&map_bh)) { |
| boundary_block = map_bh.b_blocknr; |
| boundary_bdev = map_bh.b_bdev; |
| } |
| |
| if (page_block) { |
| if (map_bh.b_blocknr != blocks[page_block-1] + 1) |
| goto confused; |
| } |
| blocks[page_block++] = map_bh.b_blocknr; |
| boundary = buffer_boundary(&map_bh); |
| bdev = map_bh.b_bdev; |
| if (block_in_file == last_block) |
| break; |
| block_in_file++; |
| } |
| BUG_ON(page_block == 0); |
| |
| first_unmapped = page_block; |
| |
| page_is_mapped: |
| if (page->index >= end_index) { |
| /* |
| * The page straddles i_size. It must be zeroed out on each |
| * and every writepage invocation because it may be mmapped. |
| * "A file is mapped in multiples of the page size. For a file |
| * that is not a multiple of the page size, the remaining memory |
| * is zeroed when mapped, and writes to that region are not |
| * written out to the file." |
| */ |
| unsigned int offset = i_size & (PAGE_SIZE - 1); |
| |
| if (page->index > end_index || !offset) { |
| MMSG("%s(inode:%p) over end " |
| "(page_idx:%u, end_idx:%u off:%u)\n", |
| __func__, inode, (u32)page->index, |
| (u32)end_index, (u32)offset); |
| goto confused; |
| } |
| zero_user_segment(page, offset, PAGE_SIZE); |
| } |
| |
| /* |
| * This page will go to BIO. Do we need to send this BIO off first? |
| * |
| * REMARK : added ELSE_IF for ALIGNMENT_MPAGE_WRITE of SDFAT |
| */ |
| if (bio) { |
| if (mpd->last_block_in_bio != blocks[0] - 1) { |
| bio = mpage_bio_submit_write(op_flags, bio); |
| } else if (mpd->size_to_align) { |
| unsigned int mask = mpd->size_to_align - 1; |
| sector_t max_end_block = |
| (__sdfat_bio_sector(bio) & ~(mask)) + mask; |
| |
| if ((__sdfat_bio_size(bio) & MIN_ALIGNED_SIZE_MASK) && |
| (mpd->last_block_in_bio == max_end_block)) { |
| int op_nomerge = op_flags | REQ_NOMERGE; |
| |
| MMSG("%s(inode:%p) alignment mpage_bio_submit" |
| "(start:%u, len:%u size:%u aligned:%u)\n", |
| __func__, inode, |
| (unsigned int)__sdfat_bio_sector(bio), |
| (unsigned int)(mpd->last_block_in_bio - |
| __sdfat_bio_sector(bio) + 1), |
| (unsigned int)__sdfat_bio_size(bio), |
| (unsigned int)mpd->size_to_align); |
| bio = mpage_bio_submit_write(op_nomerge, bio); |
| } |
| } |
| } |
| |
| alloc_new: |
| if (!bio) { |
| bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9), |
| bio_get_nr_vecs(bdev), GFP_NOFS|__GFP_HIGH); |
| if (!bio) |
| goto confused; |
| } |
| |
| /* |
| * Must try to add the page before marking the buffer clean or |
| * the confused fail path above (OOM) will be very confused when |
| * it finds all bh marked clean (i.e. it will not write anything) |
| */ |
| length = first_unmapped << blkbits; |
| if (bio_add_page(bio, page, length, 0) < length) { |
| bio = mpage_bio_submit_write(op_flags, bio); |
| goto alloc_new; |
| } |
| |
| /* |
| * OK, we have our BIO, so we can now mark the buffers clean. Make |
| * sure to only clean buffers which we know we'll be writing. |
| */ |
| clean_buffers(page, first_unmapped); |
| |
| BUG_ON(PageWriteback(page)); |
| set_page_writeback(page); |
| |
| /* |
| * FIXME FOR DEFRAGMENTATION : CODE REVIEW IS REQUIRED |
| * |
| * Turn off MAPPED flag in victim's bh if defrag on. |
| * Another write_begin can starts after get_block for defrag victims |
| * called. |
| * In this case, write_begin calls get_block and get original block |
| * number and previous defrag will be canceled. |
| */ |
| if (unlikely(__check_dfr_on(inode, (loff_t)(page->index << PAGE_SHIFT), |
| (loff_t)((page->index + 1) << PAGE_SHIFT), __func__))) { |
| struct buffer_head *head = page_buffers(page); |
| struct buffer_head *bh = head; |
| |
| do { |
| clear_buffer_mapped(bh); |
| bh = bh->b_this_page; |
| } while (bh != head); |
| } |
| |
| unlock_page(page); |
| if (boundary || (first_unmapped != blocks_per_page)) { |
| bio = mpage_bio_submit_write(op_flags, bio); |
| if (boundary_block) { |
| __write_boundary_block(boundary_bdev, |
| boundary_block, 1 << blkbits); |
| } |
| } else { |
| mpd->last_block_in_bio = blocks[blocks_per_page - 1]; |
| } |
| |
| goto out; |
| |
| confused: |
| if (bio) |
| bio = mpage_bio_submit_write(op_flags, bio); |
| |
| if (mpd->use_writepage) { |
| ret = mapping->a_ops->writepage(page, wbc); |
| } else { |
| ret = -EAGAIN; |
| goto out; |
| } |
| /* |
| * The caller has a ref on the inode, so *mapping is stable |
| */ |
| mapping_set_error(mapping, ret); |
| out: |
| mpd->bio = bio; |
| return ret; |
| } |
| |
| int sdfat_mpage_writepages(struct address_space *mapping, |
| struct writeback_control *wbc, get_block_t *get_block) |
| { |
| struct blk_plug plug; |
| int ret; |
| struct mpage_data mpd = { |
| .bio = NULL, |
| .last_block_in_bio = 0, |
| .get_block = get_block, |
| .use_writepage = 1, |
| .size_to_align = __calc_size_to_align(mapping->host->i_sb), |
| }; |
| |
| BUG_ON(!get_block); |
| blk_start_plug(&plug); |
| ret = write_cache_pages(mapping, wbc, sdfat_mpage_writepage, &mpd); |
| if (mpd.bio) { |
| int op_flags = wbc_to_write_flags(wbc); |
| |
| mpage_bio_submit_write(op_flags, mpd.bio); |
| } |
| blk_finish_plug(&plug); |
| return ret; |
| } |
| |
| #endif /* CONFIG_SDFAT_ALIGNED_MPAGE_WRITE */ |
| |