]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/udf/inode.c
udf: add extent cache support in case of file reading
[linux.git] / fs / udf / inode.c
index df88b957ccf04f39751686d3ff85f8f69ed8cdd2..7a12e48ad8196d51273fcc2ca66e89d077eb66ee 100644 (file)
@@ -67,6 +67,74 @@ static void udf_update_extents(struct inode *,
                               struct extent_position *);
 static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int);
 
+static void __udf_clear_extent_cache(struct inode *inode)
+{
+       struct udf_inode_info *iinfo = UDF_I(inode);
+
+       if (iinfo->cached_extent.lstart != -1) {
+               brelse(iinfo->cached_extent.epos.bh);
+               iinfo->cached_extent.lstart = -1;
+       }
+}
+
+/* Invalidate extent cache */
+static void udf_clear_extent_cache(struct inode *inode)
+{
+       struct udf_inode_info *iinfo = UDF_I(inode);
+
+       spin_lock(&iinfo->i_extent_cache_lock);
+       __udf_clear_extent_cache(inode);
+       spin_unlock(&iinfo->i_extent_cache_lock);
+}
+
+/* Return contents of extent cache */
+static int udf_read_extent_cache(struct inode *inode, loff_t bcount,
+                                loff_t *lbcount, struct extent_position *pos)
+{
+       struct udf_inode_info *iinfo = UDF_I(inode);
+       int ret = 0;
+
+       spin_lock(&iinfo->i_extent_cache_lock);
+       if ((iinfo->cached_extent.lstart <= bcount) &&
+           (iinfo->cached_extent.lstart != -1)) {
+               /* Cache hit */
+               *lbcount = iinfo->cached_extent.lstart;
+               memcpy(pos, &iinfo->cached_extent.epos,
+                      sizeof(struct extent_position));
+               if (pos->bh)
+                       get_bh(pos->bh);
+               ret = 1;
+       }
+       spin_unlock(&iinfo->i_extent_cache_lock);
+       return ret;
+}
+
+/* Add extent to extent cache */
+static void udf_update_extent_cache(struct inode *inode, loff_t estart,
+                                   struct extent_position *pos, int next_epos)
+{
+       struct udf_inode_info *iinfo = UDF_I(inode);
+
+       spin_lock(&iinfo->i_extent_cache_lock);
+       /* Invalidate previously cached extent */
+       __udf_clear_extent_cache(inode);
+       if (pos->bh)
+               get_bh(pos->bh);
+       memcpy(&iinfo->cached_extent.epos, pos,
+              sizeof(struct extent_position));
+       iinfo->cached_extent.lstart = estart;
+       if (next_epos)
+               switch (iinfo->i_alloc_type) {
+               case ICBTAG_FLAG_AD_SHORT:
+                       iinfo->cached_extent.epos.offset -=
+                       sizeof(struct short_ad);
+                       break;
+               case ICBTAG_FLAG_AD_LONG:
+                       iinfo->cached_extent.epos.offset -=
+                       sizeof(struct long_ad);
+               }
+       spin_unlock(&iinfo->i_extent_cache_lock);
+}
 
 void udf_evict_inode(struct inode *inode)
 {
@@ -90,6 +158,7 @@ void udf_evict_inode(struct inode *inode)
        }
        kfree(iinfo->i_ext.i_data);
        iinfo->i_ext.i_data = NULL;
+       udf_clear_extent_cache(inode);
        if (want_delete) {
                udf_free_inode(inode);
        }
@@ -105,6 +174,7 @@ static void udf_write_failed(struct address_space *mapping, loff_t to)
                truncate_pagecache(inode, to, isize);
                if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
                        down_write(&iinfo->i_data_sem);
+                       udf_clear_extent_cache(inode);
                        udf_truncate_extents(inode);
                        up_write(&iinfo->i_data_sem);
                }
@@ -372,7 +442,7 @@ static int udf_get_block(struct inode *inode, sector_t block,
                iinfo->i_next_alloc_goal++;
        }
 
-
+       udf_clear_extent_cache(inode);
        phys = inode_getblk(inode, block, &err, &new);
        if (!phys)
                goto abort;
@@ -587,7 +657,6 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
 static sector_t inode_getblk(struct inode *inode, sector_t block,
                             int *err, int *new)
 {
-       static sector_t last_block;
        struct kernel_long_ad laarr[EXTENT_MERGE_SIZE];
        struct extent_position prev_epos, cur_epos, next_epos;
        int count = 0, startnum = 0, endnum = 0;
@@ -601,6 +670,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
        struct udf_inode_info *iinfo = UDF_I(inode);
        int goal = 0, pgoal = iinfo->i_location.logicalBlockNum;
        int lastblock = 0;
+       bool isBeyondEOF;
 
        *err = 0;
        *new = 0;
@@ -676,11 +746,10 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
                return newblock;
        }
 
-       last_block = block;
        /* Are we beyond EOF? */
        if (etype == -1) {
                int ret;
-
+               isBeyondEOF = 1;
                if (count) {
                        if (c)
                                laarr[0] = laarr[1];
@@ -718,11 +787,11 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
                        memset(&laarr[c].extLocation, 0x00,
                                sizeof(struct kernel_lb_addr));
                        count++;
-                       endnum++;
                }
                endnum = c + 1;
                lastblock = 1;
        } else {
+               isBeyondEOF = 0;
                endnum = startnum = ((count > 2) ? 2 : count);
 
                /* if the current extent is in position 0,
@@ -765,10 +834,13 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
                                goal, err);
                if (!newblocknum) {
                        brelse(prev_epos.bh);
+                       brelse(cur_epos.bh);
+                       brelse(next_epos.bh);
                        *err = -ENOSPC;
                        return 0;
                }
-               iinfo->i_lenExtents += inode->i_sb->s_blocksize;
+               if (isBeyondEOF)
+                       iinfo->i_lenExtents += inode->i_sb->s_blocksize;
        }
 
        /* if the extent the requsted block is located in contains multiple
@@ -795,6 +867,8 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
        udf_update_extents(inode, laarr, startnum, endnum, &prev_epos);
 
        brelse(prev_epos.bh);
+       brelse(cur_epos.bh);
+       brelse(next_epos.bh);
 
        newblock = udf_get_pblock(inode->i_sb, newblocknum,
                                iinfo->i_location.partitionReferenceNum, 0);
@@ -1167,6 +1241,7 @@ int udf_setsize(struct inode *inode, loff_t newsize)
        } else {
                if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
                        down_write(&iinfo->i_data_sem);
+                       udf_clear_extent_cache(inode);
                        memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr + newsize,
                               0x00, bsize - newsize -
                               udf_file_entry_alloc_offset(inode));
@@ -1180,6 +1255,7 @@ int udf_setsize(struct inode *inode, loff_t newsize)
                if (err)
                        return err;
                down_write(&iinfo->i_data_sem);
+               udf_clear_extent_cache(inode);
                truncate_setsize(inode, newsize);
                udf_truncate_extents(inode);
                up_write(&iinfo->i_data_sem);
@@ -2152,11 +2228,12 @@ int8_t inode_bmap(struct inode *inode, sector_t block,
        struct udf_inode_info *iinfo;
 
        iinfo = UDF_I(inode);
-       pos->offset = 0;
-       pos->block = iinfo->i_location;
-       pos->bh = NULL;
+       if (!udf_read_extent_cache(inode, bcount, &lbcount, pos)) {
+               pos->offset = 0;
+               pos->block = iinfo->i_location;
+               pos->bh = NULL;
+       }
        *elen = 0;
-
        do {
                etype = udf_next_aext(inode, pos, eloc, elen, 1);
                if (etype == -1) {
@@ -2166,7 +2243,8 @@ int8_t inode_bmap(struct inode *inode, sector_t block,
                }
                lbcount += *elen;
        } while (lbcount <= bcount);
-
+       /* update extent cache */
+       udf_update_extent_cache(inode, lbcount - *elen, pos, 1);
        *offset = (bcount + *elen - lbcount) >> blocksize_bits;
 
        return etype;