]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/ext4/inode.c
ext4: journal credits calulation cleanup and fix for non-extent writepage
[linux-2.6-omap-h63xx.git] / fs / ext4 / inode.c
index 0080999d2cd4dff2a51d5b21e6841d797bf392d3..a271290651446f8143918f2ac2765295e8ad3187 100644 (file)
@@ -1005,6 +1005,9 @@ static int ext4_indirect_calc_metadata_amount(struct inode *inode, int blocks)
  */
 static int ext4_calc_metadata_amount(struct inode *inode, int blocks)
 {
+       if (!blocks)
+               return 0;
+
        if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)
                return ext4_ext_calc_metadata_amount(inode, blocks);
 
@@ -1424,12 +1427,8 @@ static int ext4_ordered_write_end(struct file *file,
 {
        handle_t *handle = ext4_journal_current_handle();
        struct inode *inode = mapping->host;
-       unsigned from, to;
        int ret = 0, ret2;
 
-       from = pos & (PAGE_CACHE_SIZE - 1);
-       to = from + len;
-
        ret = ext4_jbd2_file_inode(handle, inode);
 
        if (ret == 0) {
@@ -1563,7 +1562,25 @@ static void ext4_da_release_space(struct inode *inode, int to_free)
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        int total, mdb, mdb_free, release;
 
+       if (!to_free)
+               return;         /* Nothing to release, exit */
+
        spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
+
+       if (!EXT4_I(inode)->i_reserved_data_blocks) {
+               /*
+                * if there is no reserved blocks, but we try to free some
+                * then the counter is messed up somewhere.
+                * but since this function is called from invalidate
+                * page, it's harmless to return without any action
+                */
+               printk(KERN_INFO "ext4 delalloc try to release %d reserved "
+                           "blocks for inode %lu, but there is no reserved "
+                           "data blocks\n", to_free, inode->i_ino);
+               spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
+               return;
+       }
+
        /* recalculate the number of metablocks still need to be reserved */
        total = EXT4_I(inode)->i_reserved_data_blocks - to_free;
        mdb = ext4_calc_metadata_amount(inode, total);
@@ -1745,6 +1762,13 @@ static void mpage_put_bnr_to_bhs(struct mpage_da_data *mpd, sector_t logical,
                                if (buffer_delay(bh)) {
                                        bh->b_blocknr = pblock;
                                        clear_buffer_delay(bh);
+                                       bh->b_bdev = inode->i_sb->s_bdev;
+                               } else if (buffer_unwritten(bh)) {
+                                       bh->b_blocknr = pblock;
+                                       clear_buffer_unwritten(bh);
+                                       set_buffer_mapped(bh);
+                                       set_buffer_new(bh);
+                                       bh->b_bdev = inode->i_sb->s_bdev;
                                } else if (buffer_mapped(bh))
                                        BUG_ON(bh->b_blocknr != pblock);
 
@@ -1818,7 +1842,7 @@ static void mpage_da_map_blocks(struct mpage_da_data *mpd)
                 * If blocks are delayed marked, we need to
                 * put actual blocknr and drop delayed bit
                 */
-               if (buffer_delay(lbh))
+               if (buffer_delay(lbh) || buffer_unwritten(lbh))
                        mpage_put_bnr_to_bhs(mpd, next, &new);
 
                /* go for the remaining blocks */
@@ -1827,7 +1851,8 @@ static void mpage_da_map_blocks(struct mpage_da_data *mpd)
        }
 }
 
-#define BH_FLAGS ((1 << BH_Uptodate) | (1 << BH_Mapped) | (1 << BH_Delay))
+#define BH_FLAGS ((1 << BH_Uptodate) | (1 << BH_Mapped) | \
+               (1 << BH_Delay) | (1 << BH_Unwritten))
 
 /*
  * mpage_add_bh_to_extent - try to add one more block to extent of blocks
@@ -3490,6 +3515,9 @@ void ext4_truncate(struct inode *inode)
         * modify the block allocation tree.
         */
        down_write(&ei->i_data_sem);
+
+       ext4_discard_reservation(inode);
+
        /*
         * The orphan list entry will now protect us from any crash which
         * occurs before the truncate completes, so it is now safe to propagate
@@ -3559,8 +3587,6 @@ do_indirects:
                ;
        }
 
-       ext4_discard_reservation(inode);
-
        up_write(&ei->i_data_sem);
        inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
        ext4_mark_inode_dirty(handle, inode);
@@ -4328,56 +4354,119 @@ int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
        return 0;
 }
 
+static int ext4_indirect_trans_blocks(struct inode *inode, int nrblocks,
+                                     int chunk)
+{
+       int indirects;
+
+       /* if nrblocks are contiguous */
+       if (chunk) {
+               /*
+                * With N contiguous data blocks, it need at most
+                * N/EXT4_ADDR_PER_BLOCK(inode->i_sb) indirect blocks
+                * 2 dindirect blocks
+                * 1 tindirect block
+                */
+               indirects = nrblocks / EXT4_ADDR_PER_BLOCK(inode->i_sb);
+               return indirects + 3;
+       }
+       /*
+        * if nrblocks are not contiguous, worse case, each block touch
+        * a indirect block, and each indirect block touch a double indirect
+        * block, plus a triple indirect block
+        */
+       indirects = nrblocks * 2 + 1;
+       return indirects;
+}
+
+static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
+{
+       if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL))
+               return ext4_indirect_trans_blocks(inode, nrblocks, 0);
+       return ext4_ext_index_trans_blocks(inode, nrblocks, 0);
+}
 /*
- * How many blocks doth make a writepage()?
+ * Account for index blocks, block groups bitmaps and block group
+ * descriptor blocks if modify datablocks and index blocks
+ * worse case, the indexs blocks spread over different block groups
  *
- * With N blocks per page, it may be:
- * N data blocks
- * 2 indirect block
- * 2 dindirect
- * 1 tindirect
- * N+5 bitmap blocks (from the above)
- * N+5 group descriptor summary blocks
- * 1 inode block
- * 1 superblock.
- * 2 * EXT4_SINGLEDATA_TRANS_BLOCKS for the quote files
+ * If datablocks are discontiguous, they are possible to spread over
+ * different block groups too. If they are contiugous, with flexbg,
+ * they could still across block group boundary.
  *
- * 3 * (N + 5) + 2 + 2 * EXT4_SINGLEDATA_TRANS_BLOCKS
+ * Also account for superblock, inode, quota and xattr blocks
+ */
+int ext4_meta_trans_blocks(struct inode *inode, int nrblocks, int chunk)
+{
+       int groups, gdpblocks;
+       int idxblocks;
+       int ret = 0;
+
+       /*
+        * How many index blocks need to touch to modify nrblocks?
+        * The "Chunk" flag indicating whether the nrblocks is
+        * physically contiguous on disk
+        *
+        * For Direct IO and fallocate, they calls get_block to allocate
+        * one single extent at a time, so they could set the "Chunk" flag
+        */
+       idxblocks = ext4_index_trans_blocks(inode, nrblocks, chunk);
+
+       ret = idxblocks;
+
+       /*
+        * Now let's see how many group bitmaps and group descriptors need
+        * to account
+        */
+       groups = idxblocks;
+       if (chunk)
+               groups += 1;
+       else
+               groups += nrblocks;
+
+       gdpblocks = groups;
+       if (groups > EXT4_SB(inode->i_sb)->s_groups_count)
+               groups = EXT4_SB(inode->i_sb)->s_groups_count;
+       if (groups > EXT4_SB(inode->i_sb)->s_gdb_count)
+               gdpblocks = EXT4_SB(inode->i_sb)->s_gdb_count;
+
+       /* bitmaps and block group descriptor blocks */
+       ret += groups + gdpblocks;
+
+       /* Blocks for super block, inode, quota and xattr blocks */
+       ret += EXT4_META_TRANS_BLOCKS(inode->i_sb);
+
+       return ret;
+}
+
+/*
+ * Calulate the total number of credits to reserve to fit
+ * the modification of a single pages into a single transaction
  *
- * With ordered or writeback data it's the same, less the N data blocks.
+ * This could be called via ext4_write_begin() or later
+ * ext4_da_writepages() in delalyed allocation case.
  *
- * If the inode's direct blocks can hold an integral number of pages then a
- * page cannot straddle two indirect blocks, and we can only touch one indirect
- * and dindirect block, and the "5" above becomes "3".
+ * In both case it's possible that we could allocating multiple
+ * chunks of blocks. We need to consider the worse case, when
+ * one new block per extent.
  *
- * This still overestimates under most circumstances.  If we were to pass the
- * start and end offsets in here as well we could do block_to_path() on each
- * block and work out the exact number of indirects which are touched.  Pah.
+ * For Direct IO and fallocate, the journal credits reservation
+ * is based on one single extent allocation, so they could use
+ * EXT4_DATA_TRANS_BLOCKS to get the needed credit to log a single
+ * chunk of allocation needs.
  */
-
 int ext4_writepage_trans_blocks(struct inode *inode)
 {
        int bpp = ext4_journal_blocks_per_page(inode);
-       int indirects = (EXT4_NDIR_BLOCKS % bpp) ? 5 : 3;
        int ret;
 
-       if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)
-               return ext4_ext_writepage_trans_blocks(inode, bpp);
+       ret = ext4_meta_trans_blocks(inode, bpp, 0);
 
+       /* Account for data blocks for journalled mode */
        if (ext4_should_journal_data(inode))
-               ret = 3 * (bpp + indirects) + 2;
-       else
-               ret = 2 * (bpp + indirects) + 2;
-
-#ifdef CONFIG_QUOTA
-       /* We know that structure was already allocated during DQUOT_INIT so
-        * we will be updating only the data blocks + inodes */
-       ret += 2*EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb);
-#endif
-
+               ret += bpp;
        return ret;
 }
-
 /*
  * The caller must have previously called ext4_reserve_inode_write().
  * Give this, we know that the caller already has write access to iloc->bh.