diff options
-rw-r--r-- | fs/ext4/ext4.h | 11 | ||||
-rw-r--r-- | fs/ext4/inode.c | 224 |
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); |
1839 | extern int ext4_block_zero_page_range(handle_t *handle, | 1844 | extern 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); |
1846 | extern int ext4_discard_partial_page_buffers(handle_t *handle, | ||
1847 | struct address_space *mapping, loff_t from, | ||
1848 | loff_t length, int flags); | ||
1849 | extern 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); | ||
1841 | extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); | 1852 | extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); |
1842 | extern qsize_t *ext4_get_reserved_space(struct inode *inode); | 1853 | extern qsize_t *ext4_get_reserved_space(struct inode *inode); |
1843 | extern void ext4_da_update_reserve_space(struct inode *inode, | 1854 | extern 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 | */ | ||
2978 | int 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 | */ | ||
3032 | int 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"); | ||
3184 | next: | ||
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'. |