aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/extents.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext4/extents.c')
-rw-r--r--fs/ext4/extents.c106
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
2141static 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 */
2147static 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;
2298insert: 2377insert:
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;
2307out: 2391out:
2308 return err ? err : allocated; 2392 return err ? err : allocated;
2393
2394fix_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/*