|
@@ -2138,6 +2138,80 @@ void ext4_ext_release(struct super_block *sb)
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+static void bi_complete(struct bio *bio, int error)
|
|
|
+{
|
|
|
+ complete((struct completion *)bio->bi_private);
|
|
|
+}
|
|
|
+
|
|
|
+/* FIXME!! we need to try to merge to left or right after zero-out */
|
|
|
+static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
|
|
|
+{
|
|
|
+ int ret = -EIO;
|
|
|
+ struct bio *bio;
|
|
|
+ int blkbits, blocksize;
|
|
|
+ sector_t ee_pblock;
|
|
|
+ struct completion event;
|
|
|
+ unsigned int ee_len, len, done, offset;
|
|
|
+
|
|
|
+
|
|
|
+ blkbits = inode->i_blkbits;
|
|
|
+ blocksize = inode->i_sb->s_blocksize;
|
|
|
+ ee_len = ext4_ext_get_actual_len(ex);
|
|
|
+ ee_pblock = ext_pblock(ex);
|
|
|
+
|
|
|
+ /* convert ee_pblock to 512 byte sectors */
|
|
|
+ ee_pblock = ee_pblock << (blkbits - 9);
|
|
|
+
|
|
|
+ while (ee_len > 0) {
|
|
|
+
|
|
|
+ if (ee_len > BIO_MAX_PAGES)
|
|
|
+ len = BIO_MAX_PAGES;
|
|
|
+ else
|
|
|
+ len = ee_len;
|
|
|
+
|
|
|
+ bio = bio_alloc(GFP_NOIO, len);
|
|
|
+ if (!bio)
|
|
|
+ return -ENOMEM;
|
|
|
+ bio->bi_sector = ee_pblock;
|
|
|
+ bio->bi_bdev = inode->i_sb->s_bdev;
|
|
|
+
|
|
|
+ done = 0;
|
|
|
+ offset = 0;
|
|
|
+ while (done < len) {
|
|
|
+ ret = bio_add_page(bio, ZERO_PAGE(0),
|
|
|
+ blocksize, offset);
|
|
|
+ if (ret != blocksize) {
|
|
|
+ /*
|
|
|
+ * We can't add any more pages because of
|
|
|
+ * hardware limitations. Start a new bio.
|
|
|
+ */
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ done++;
|
|
|
+ offset += blocksize;
|
|
|
+ if (offset >= PAGE_CACHE_SIZE)
|
|
|
+ offset = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ init_completion(&event);
|
|
|
+ bio->bi_private = &event;
|
|
|
+ bio->bi_end_io = bi_complete;
|
|
|
+ submit_bio(WRITE, bio);
|
|
|
+ wait_for_completion(&event);
|
|
|
+
|
|
|
+ if (test_bit(BIO_UPTODATE, &bio->bi_flags))
|
|
|
+ ret = 0;
|
|
|
+ else {
|
|
|
+ ret = -EIO;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ bio_put(bio);
|
|
|
+ ee_len -= done;
|
|
|
+ ee_pblock += done << (blkbits - 9);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* This function is called by ext4_ext_get_blocks() if someone tries to write
|
|
|
* 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,
|
|
|
ex3->ee_len = cpu_to_le16(allocated - max_blocks);
|
|
|
ext4_ext_mark_uninitialized(ex3);
|
|
|
err = ext4_ext_insert_extent(handle, inode, path, ex3);
|
|
|
- if (err) {
|
|
|
+ if (err == -ENOSPC) {
|
|
|
+ err = ext4_ext_zeroout(inode, &orig_ex);
|
|
|
+ if (err)
|
|
|
+ goto fix_extent_len;
|
|
|
+ /* update the extent length and mark as initialized */
|
|
|
ex->ee_block = orig_ex.ee_block;
|
|
|
ex->ee_len = orig_ex.ee_len;
|
|
|
ext4_ext_store_pblock(ex, ext_pblock(&orig_ex));
|
|
|
- ext4_ext_mark_uninitialized(ex);
|
|
|
ext4_ext_dirty(handle, inode, path + depth);
|
|
|
- goto out;
|
|
|
- }
|
|
|
+ return le16_to_cpu(ex->ee_len);
|
|
|
+
|
|
|
+ } else if (err)
|
|
|
+ goto fix_extent_len;
|
|
|
/*
|
|
|
* The depth, and hence eh & ex might change
|
|
|
* as part of the insert above.
|
|
@@ -2297,15 +2376,28 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
|
|
|
goto out;
|
|
|
insert:
|
|
|
err = ext4_ext_insert_extent(handle, inode, path, &newex);
|
|
|
- if (err) {
|
|
|
+ if (err == -ENOSPC) {
|
|
|
+ err = ext4_ext_zeroout(inode, &orig_ex);
|
|
|
+ if (err)
|
|
|
+ goto fix_extent_len;
|
|
|
+ /* update the extent length and mark as initialized */
|
|
|
ex->ee_block = orig_ex.ee_block;
|
|
|
ex->ee_len = orig_ex.ee_len;
|
|
|
ext4_ext_store_pblock(ex, ext_pblock(&orig_ex));
|
|
|
- ext4_ext_mark_uninitialized(ex);
|
|
|
ext4_ext_dirty(handle, inode, path + depth);
|
|
|
- }
|
|
|
+ return le16_to_cpu(ex->ee_len);
|
|
|
+ } else if (err)
|
|
|
+ goto fix_extent_len;
|
|
|
out:
|
|
|
return err ? err : allocated;
|
|
|
+
|
|
|
+fix_extent_len:
|
|
|
+ ex->ee_block = orig_ex.ee_block;
|
|
|
+ ex->ee_len = orig_ex.ee_len;
|
|
|
+ ext4_ext_store_pblock(ex, ext_pblock(&orig_ex));
|
|
|
+ ext4_ext_mark_uninitialized(ex);
|
|
|
+ ext4_ext_dirty(handle, inode, path + depth);
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
/*
|