diff options
Diffstat (limited to 'fs/ext4/extents.c')
-rw-r--r-- | fs/ext4/extents.c | 106 |
1 files changed, 99 insertions, 7 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index a38f32427826..d279ea8c4661 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -2138,6 +2138,80 @@ void ext4_ext_release(struct super_block *sb) | |||
2138 | #endif | 2138 | #endif |
2139 | } | 2139 | } |
2140 | 2140 | ||
2141 | static void bi_complete(struct bio *bio, int error) | ||
2142 | { | ||
2143 | complete((struct completion *)bio->bi_private); | ||
2144 | } | ||
2145 | |||
2146 | /* FIXME!! we need to try to merge to left or right after zero-out */ | ||
2147 | static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) | ||
2148 | { | ||
2149 | int ret = -EIO; | ||
2150 | struct bio *bio; | ||
2151 | int blkbits, blocksize; | ||
2152 | sector_t ee_pblock; | ||
2153 | struct completion event; | ||
2154 | unsigned int ee_len, len, done, offset; | ||
2155 | |||
2156 | |||
2157 | blkbits = inode->i_blkbits; | ||
2158 | blocksize = inode->i_sb->s_blocksize; | ||
2159 | ee_len = ext4_ext_get_actual_len(ex); | ||
2160 | ee_pblock = ext_pblock(ex); | ||
2161 | |||
2162 | /* convert ee_pblock to 512 byte sectors */ | ||
2163 | ee_pblock = ee_pblock << (blkbits - 9); | ||
2164 | |||
2165 | while (ee_len > 0) { | ||
2166 | |||
2167 | if (ee_len > BIO_MAX_PAGES) | ||
2168 | len = BIO_MAX_PAGES; | ||
2169 | else | ||
2170 | len = ee_len; | ||
2171 | |||
2172 | bio = bio_alloc(GFP_NOIO, len); | ||
2173 | if (!bio) | ||
2174 | return -ENOMEM; | ||
2175 | bio->bi_sector = ee_pblock; | ||
2176 | bio->bi_bdev = inode->i_sb->s_bdev; | ||
2177 | |||
2178 | done = 0; | ||
2179 | offset = 0; | ||
2180 | while (done < len) { | ||
2181 | ret = bio_add_page(bio, ZERO_PAGE(0), | ||
2182 | blocksize, offset); | ||
2183 | if (ret != blocksize) { | ||
2184 | /* | ||
2185 | * We can't add any more pages because of | ||
2186 | * hardware limitations. Start a new bio. | ||
2187 | */ | ||
2188 | break; | ||
2189 | } | ||
2190 | done++; | ||
2191 | offset += blocksize; | ||
2192 | if (offset >= PAGE_CACHE_SIZE) | ||
2193 | offset = 0; | ||
2194 | } | ||
2195 | |||
2196 | init_completion(&event); | ||
2197 | bio->bi_private = &event; | ||
2198 | bio->bi_end_io = bi_complete; | ||
2199 | submit_bio(WRITE, bio); | ||
2200 | wait_for_completion(&event); | ||
2201 | |||
2202 | if (test_bit(BIO_UPTODATE, &bio->bi_flags)) | ||
2203 | ret = 0; | ||
2204 | else { | ||
2205 | ret = -EIO; | ||
2206 | break; | ||
2207 | } | ||
2208 | bio_put(bio); | ||
2209 | ee_len -= done; | ||
2210 | ee_pblock += done << (blkbits - 9); | ||
2211 | } | ||
2212 | return ret; | ||
2213 | } | ||
2214 | |||
2141 | /* | 2215 | /* |
2142 | * This function is called by ext4_ext_get_blocks() if someone tries to write | 2216 | * This function is called by ext4_ext_get_blocks() if someone tries to write |
2143 | * to an uninitialized extent. It may result in splitting the uninitialized | 2217 | * to an uninitialized extent. It may result in splitting the uninitialized |
@@ -2204,14 +2278,19 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, | |||
2204 | ex3->ee_len = cpu_to_le16(allocated - max_blocks); | 2278 | ex3->ee_len = cpu_to_le16(allocated - max_blocks); |
2205 | ext4_ext_mark_uninitialized(ex3); | 2279 | ext4_ext_mark_uninitialized(ex3); |
2206 | err = ext4_ext_insert_extent(handle, inode, path, ex3); | 2280 | err = ext4_ext_insert_extent(handle, inode, path, ex3); |
2207 | if (err) { | 2281 | if (err == -ENOSPC) { |
2282 | err = ext4_ext_zeroout(inode, &orig_ex); | ||
2283 | if (err) | ||
2284 | goto fix_extent_len; | ||
2285 | /* update the extent length and mark as initialized */ | ||
2208 | ex->ee_block = orig_ex.ee_block; | 2286 | ex->ee_block = orig_ex.ee_block; |
2209 | ex->ee_len = orig_ex.ee_len; | 2287 | ex->ee_len = orig_ex.ee_len; |
2210 | ext4_ext_store_pblock(ex, ext_pblock(&orig_ex)); | 2288 | ext4_ext_store_pblock(ex, ext_pblock(&orig_ex)); |
2211 | ext4_ext_mark_uninitialized(ex); | ||
2212 | ext4_ext_dirty(handle, inode, path + depth); | 2289 | ext4_ext_dirty(handle, inode, path + depth); |
2213 | goto out; | 2290 | return le16_to_cpu(ex->ee_len); |
2214 | } | 2291 | |
2292 | } else if (err) | ||
2293 | goto fix_extent_len; | ||
2215 | /* | 2294 | /* |
2216 | * The depth, and hence eh & ex might change | 2295 | * The depth, and hence eh & ex might change |
2217 | * as part of the insert above. | 2296 | * as part of the insert above. |
@@ -2297,15 +2376,28 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, | |||
2297 | goto out; | 2376 | goto out; |
2298 | insert: | 2377 | insert: |
2299 | err = ext4_ext_insert_extent(handle, inode, path, &newex); | 2378 | err = ext4_ext_insert_extent(handle, inode, path, &newex); |
2300 | if (err) { | 2379 | if (err == -ENOSPC) { |
2380 | err = ext4_ext_zeroout(inode, &orig_ex); | ||
2381 | if (err) | ||
2382 | goto fix_extent_len; | ||
2383 | /* update the extent length and mark as initialized */ | ||
2301 | ex->ee_block = orig_ex.ee_block; | 2384 | ex->ee_block = orig_ex.ee_block; |
2302 | ex->ee_len = orig_ex.ee_len; | 2385 | ex->ee_len = orig_ex.ee_len; |
2303 | ext4_ext_store_pblock(ex, ext_pblock(&orig_ex)); | 2386 | ext4_ext_store_pblock(ex, ext_pblock(&orig_ex)); |
2304 | ext4_ext_mark_uninitialized(ex); | ||
2305 | ext4_ext_dirty(handle, inode, path + depth); | 2387 | ext4_ext_dirty(handle, inode, path + depth); |
2306 | } | 2388 | return le16_to_cpu(ex->ee_len); |
2389 | } else if (err) | ||
2390 | goto fix_extent_len; | ||
2307 | out: | 2391 | out: |
2308 | return err ? err : allocated; | 2392 | return err ? err : allocated; |
2393 | |||
2394 | fix_extent_len: | ||
2395 | ex->ee_block = orig_ex.ee_block; | ||
2396 | ex->ee_len = orig_ex.ee_len; | ||
2397 | ext4_ext_store_pblock(ex, ext_pblock(&orig_ex)); | ||
2398 | ext4_ext_mark_uninitialized(ex); | ||
2399 | ext4_ext_dirty(handle, inode, path + depth); | ||
2400 | return err; | ||
2309 | } | 2401 | } |
2310 | 2402 | ||
2311 | /* | 2403 | /* |