aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2008-04-29 08:11:12 -0400
committerTheodore Ts'o <tytso@mit.edu>2008-04-29 08:11:12 -0400
commit093a088b76352e0a6fdca84eb78b3aa65fbe6dd1 (patch)
tree18b9266ce90cb63726c8b6e0e7a026d11ed66a40 /fs
parent35802c0b2bab71695f131f981d95fcea7432c99b (diff)
ext4: ENOSPC error handling for writing to an uninitialized extent
This patch handles possible ENOSPC errors when writing to an uninitialized extent in case the filesystem is full. A write to a prealloc area causes the split of an unititalized extent into initialized and uninitialized extents. If we don't have space to add new extent information, instead of returning error, convert the existing uninitialized extent to initialized one. We need to zero out the blocks corresponding to the entire extent to prevent uninitialized data reaching userspace. Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: Mingming Cao <cmm@us.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-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/*