aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/ext4/ext4.h11
-rw-r--r--fs/ext4/inode.c224
2 files changed, 235 insertions, 0 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5c38120c389c..ccfa81f33bb0 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -529,6 +529,11 @@ struct ext4_new_group_data {
529#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008 529#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008
530 530
531/* 531/*
532 * Flags used by ext4_discard_partial_page_buffers
533 */
534#define EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED 0x0001
535
536/*
532 * ioctl commands 537 * ioctl commands
533 */ 538 */
534#define EXT4_IOC_GETFLAGS FS_IOC_GETFLAGS 539#define EXT4_IOC_GETFLAGS FS_IOC_GETFLAGS
@@ -1838,6 +1843,12 @@ extern int ext4_block_truncate_page(handle_t *handle,
1838 struct address_space *mapping, loff_t from); 1843 struct address_space *mapping, loff_t from);
1839extern int ext4_block_zero_page_range(handle_t *handle, 1844extern int ext4_block_zero_page_range(handle_t *handle,
1840 struct address_space *mapping, loff_t from, loff_t length); 1845 struct address_space *mapping, loff_t from, loff_t length);
1846extern int ext4_discard_partial_page_buffers(handle_t *handle,
1847 struct address_space *mapping, loff_t from,
1848 loff_t length, int flags);
1849extern int ext4_discard_partial_page_buffers_no_lock(handle_t *handle,
1850 struct inode *inode, struct page *page, loff_t from,
1851 loff_t length, int flags);
1841extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); 1852extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
1842extern qsize_t *ext4_get_reserved_space(struct inode *inode); 1853extern qsize_t *ext4_get_reserved_space(struct inode *inode);
1843extern void ext4_da_update_reserve_space(struct inode *inode, 1854extern void ext4_da_update_reserve_space(struct inode *inode,
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index b84f127c085d..d1b1ef71e5b7 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2966,6 +2966,230 @@ void ext4_set_aops(struct inode *inode)
2966 inode->i_mapping->a_ops = &ext4_journalled_aops; 2966 inode->i_mapping->a_ops = &ext4_journalled_aops;
2967} 2967}
2968 2968
2969
2970/*
2971 * ext4_discard_partial_page_buffers()
2972 * Wrapper function for ext4_discard_partial_page_buffers_no_lock.
2973 * This function finds and locks the page containing the offset
2974 * "from" and passes it to ext4_discard_partial_page_buffers_no_lock.
2975 * Calling functions that already have the page locked should call
2976 * ext4_discard_partial_page_buffers_no_lock directly.
2977 */
2978int ext4_discard_partial_page_buffers(handle_t *handle,
2979 struct address_space *mapping, loff_t from,
2980 loff_t length, int flags)
2981{
2982 struct inode *inode = mapping->host;
2983 struct page *page;
2984 int err = 0;
2985
2986 page = find_or_create_page(mapping, from >> PAGE_CACHE_SHIFT,
2987 mapping_gfp_mask(mapping) & ~__GFP_FS);
2988 if (!page)
2989 return -EINVAL;
2990
2991 err = ext4_discard_partial_page_buffers_no_lock(handle, inode, page,
2992 from, length, flags);
2993
2994 unlock_page(page);
2995 page_cache_release(page);
2996 return err;
2997}
2998
2999/*
3000 * ext4_discard_partial_page_buffers_no_lock()
3001 * Zeros a page range of length 'length' starting from offset 'from'.
3002 * Buffer heads that correspond to the block aligned regions of the
3003 * zeroed range will be unmapped. Unblock aligned regions
3004 * will have the corresponding buffer head mapped if needed so that
3005 * that region of the page can be updated with the partial zero out.
3006 *
3007 * This function assumes that the page has already been locked. The
3008 * The range to be discarded must be contained with in the given page.
3009 * If the specified range exceeds the end of the page it will be shortened
3010 * to the end of the page that corresponds to 'from'. This function is
3011 * appropriate for updating a page and it buffer heads to be unmapped and
3012 * zeroed for blocks that have been either released, or are going to be
3013 * released.
3014 *
3015 * handle: The journal handle
3016 * inode: The files inode
3017 * page: A locked page that contains the offset "from"
3018 * from: The starting byte offset (from the begining of the file)
3019 * to begin discarding
3020 * len: The length of bytes to discard
3021 * flags: Optional flags that may be used:
3022 *
3023 * EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED
3024 * Only zero the regions of the page whose buffer heads
3025 * have already been unmapped. This flag is appropriate
3026 * for updateing the contents of a page whose blocks may
3027 * have already been released, and we only want to zero
3028 * out the regions that correspond to those released blocks.
3029 *
3030 * Returns zero on sucess or negative on failure.
3031 */
3032int ext4_discard_partial_page_buffers_no_lock(handle_t *handle,
3033 struct inode *inode, struct page *page, loff_t from,
3034 loff_t length, int flags)
3035{
3036 ext4_fsblk_t index = from >> PAGE_CACHE_SHIFT;
3037 unsigned int offset = from & (PAGE_CACHE_SIZE-1);
3038 unsigned int blocksize, max, pos;
3039 unsigned int end_of_block, range_to_discard;
3040 ext4_lblk_t iblock;
3041 struct buffer_head *bh;
3042 int err = 0;
3043
3044 blocksize = inode->i_sb->s_blocksize;
3045 max = PAGE_CACHE_SIZE - offset;
3046
3047 if (index != page->index)
3048 return -EINVAL;
3049
3050 /*
3051 * correct length if it does not fall between
3052 * 'from' and the end of the page
3053 */
3054 if (length > max || length < 0)
3055 length = max;
3056
3057 iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
3058
3059 if (!page_has_buffers(page)) {
3060 /*
3061 * If the range to be discarded covers a partial block
3062 * we need to get the page buffers. This is because
3063 * partial blocks cannot be released and the page needs
3064 * to be updated with the contents of the block before
3065 * we write the zeros on top of it.
3066 */
3067 if (!(from & (blocksize - 1)) ||
3068 !((from + length) & (blocksize - 1))) {
3069 create_empty_buffers(page, blocksize, 0);
3070 } else {
3071 /*
3072 * If there are no partial blocks,
3073 * there is nothing to update,
3074 * so we can return now
3075 */
3076 return 0;
3077 }
3078 }
3079
3080 /* Find the buffer that contains "offset" */
3081 bh = page_buffers(page);
3082 pos = blocksize;
3083 while (offset >= pos) {
3084 bh = bh->b_this_page;
3085 iblock++;
3086 pos += blocksize;
3087 }
3088
3089 pos = offset;
3090 while (pos < offset + length) {
3091 err = 0;
3092
3093 /* The length of space left to zero and unmap */
3094 range_to_discard = offset + length - pos;
3095
3096 /* The length of space until the end of the block */
3097 end_of_block = blocksize - (pos & (blocksize-1));
3098
3099 /*
3100 * Do not unmap or zero past end of block
3101 * for this buffer head
3102 */
3103 if (range_to_discard > end_of_block)
3104 range_to_discard = end_of_block;
3105
3106
3107 /*
3108 * Skip this buffer head if we are only zeroing unampped
3109 * regions of the page
3110 */
3111 if (flags & EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED &&
3112 buffer_mapped(bh))
3113 goto next;
3114
3115 /* If the range is block aligned, unmap */
3116 if (range_to_discard == blocksize) {
3117 clear_buffer_dirty(bh);
3118 bh->b_bdev = NULL;
3119 clear_buffer_mapped(bh);
3120 clear_buffer_req(bh);
3121 clear_buffer_new(bh);
3122 clear_buffer_delay(bh);
3123 clear_buffer_unwritten(bh);
3124 clear_buffer_uptodate(bh);
3125 zero_user(page, pos, range_to_discard);
3126 BUFFER_TRACE(bh, "Buffer discarded");
3127 goto next;
3128 }
3129
3130 /*
3131 * If this block is not completely contained in the range
3132 * to be discarded, then it is not going to be released. Because
3133 * we need to keep this block, we need to make sure this part
3134 * of the page is uptodate before we modify it by writeing
3135 * partial zeros on it.
3136 */
3137 if (!buffer_mapped(bh)) {
3138 /*
3139 * Buffer head must be mapped before we can read
3140 * from the block
3141 */
3142 BUFFER_TRACE(bh, "unmapped");
3143 ext4_get_block(inode, iblock, bh, 0);
3144 /* unmapped? It's a hole - nothing to do */
3145 if (!buffer_mapped(bh)) {
3146 BUFFER_TRACE(bh, "still unmapped");
3147 goto next;
3148 }
3149 }
3150
3151 /* Ok, it's mapped. Make sure it's up-to-date */
3152 if (PageUptodate(page))
3153 set_buffer_uptodate(bh);
3154
3155 if (!buffer_uptodate(bh)) {
3156 err = -EIO;
3157 ll_rw_block(READ, 1, &bh);
3158 wait_on_buffer(bh);
3159 /* Uhhuh. Read error. Complain and punt.*/
3160 if (!buffer_uptodate(bh))
3161 goto next;
3162 }
3163
3164 if (ext4_should_journal_data(inode)) {
3165 BUFFER_TRACE(bh, "get write access");
3166 err = ext4_journal_get_write_access(handle, bh);
3167 if (err)
3168 goto next;
3169 }
3170
3171 zero_user(page, pos, range_to_discard);
3172
3173 err = 0;
3174 if (ext4_should_journal_data(inode)) {
3175 err = ext4_handle_dirty_metadata(handle, inode, bh);
3176 } else {
3177 if (ext4_should_order_data(inode) &&
3178 EXT4_I(inode)->jinode)
3179 err = ext4_jbd2_file_inode(handle, inode);
3180 mark_buffer_dirty(bh);
3181 }
3182
3183 BUFFER_TRACE(bh, "Partial buffer zeroed");
3184next:
3185 bh = bh->b_this_page;
3186 iblock++;
3187 pos += range_to_discard;
3188 }
3189
3190 return err;
3191}
3192
2969/* 3193/*
2970 * ext4_block_truncate_page() zeroes out a mapping from file offset `from' 3194 * ext4_block_truncate_page() zeroes out a mapping from file offset `from'
2971 * up to the end of the block which corresponds to `from'. 3195 * up to the end of the block which corresponds to `from'.