Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

udf: add extent cache support in case of file reading

This patch implements extent caching in case of file reading.
While reading a file, currently, UDF reads metadata serially
which takes a lot of time depending on the number of extents present
in the file. Caching last accessd extent improves metadata read time.
Instead of reading file metadata from start, now we read from
the cached extent.

This patch considerably improves the time spent by CPU in kernel mode.
For example, while reading a 10.9 GB file using dd:
Time before applying patch:
11677022208 bytes (10.9GB) copied, 1529.748921 seconds, 7.3MB/s
real 25m 29.85s
user 0m 12.41s
sys 15m 34.75s

Time after applying patch:
11677022208 bytes (10.9GB) copied, 1469.338231 seconds, 7.6MB/s
real 24m 29.44s
user 0m 15.73s
sys 3m 27.61s

[JK: Fix bh refcounting issues, simplify initialization]

Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
Signed-off-by: Ashish Sangwan <a.sangwan@samsung.com>
Signed-off-by: Bonggil Bak <bgbak@samsung.com>
Signed-off-by: Jan Kara <jack@suse.cz>

authored by

Namjae Jeon and committed by
Jan Kara
99600051 9734c971

+98 -11
+80 -6
fs/udf/inode.c
··· 67 67 struct extent_position *); 68 68 static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); 69 69 70 + static void __udf_clear_extent_cache(struct inode *inode) 71 + { 72 + struct udf_inode_info *iinfo = UDF_I(inode); 73 + 74 + if (iinfo->cached_extent.lstart != -1) { 75 + brelse(iinfo->cached_extent.epos.bh); 76 + iinfo->cached_extent.lstart = -1; 77 + } 78 + } 79 + 80 + /* Invalidate extent cache */ 81 + static void udf_clear_extent_cache(struct inode *inode) 82 + { 83 + struct udf_inode_info *iinfo = UDF_I(inode); 84 + 85 + spin_lock(&iinfo->i_extent_cache_lock); 86 + __udf_clear_extent_cache(inode); 87 + spin_unlock(&iinfo->i_extent_cache_lock); 88 + } 89 + 90 + /* Return contents of extent cache */ 91 + static int udf_read_extent_cache(struct inode *inode, loff_t bcount, 92 + loff_t *lbcount, struct extent_position *pos) 93 + { 94 + struct udf_inode_info *iinfo = UDF_I(inode); 95 + int ret = 0; 96 + 97 + spin_lock(&iinfo->i_extent_cache_lock); 98 + if ((iinfo->cached_extent.lstart <= bcount) && 99 + (iinfo->cached_extent.lstart != -1)) { 100 + /* Cache hit */ 101 + *lbcount = iinfo->cached_extent.lstart; 102 + memcpy(pos, &iinfo->cached_extent.epos, 103 + sizeof(struct extent_position)); 104 + if (pos->bh) 105 + get_bh(pos->bh); 106 + ret = 1; 107 + } 108 + spin_unlock(&iinfo->i_extent_cache_lock); 109 + return ret; 110 + } 111 + 112 + /* Add extent to extent cache */ 113 + static void udf_update_extent_cache(struct inode *inode, loff_t estart, 114 + struct extent_position *pos, int next_epos) 115 + { 116 + struct udf_inode_info *iinfo = UDF_I(inode); 117 + 118 + spin_lock(&iinfo->i_extent_cache_lock); 119 + /* Invalidate previously cached extent */ 120 + __udf_clear_extent_cache(inode); 121 + if (pos->bh) 122 + get_bh(pos->bh); 123 + memcpy(&iinfo->cached_extent.epos, pos, 124 + sizeof(struct extent_position)); 125 + iinfo->cached_extent.lstart = estart; 126 + if (next_epos) 127 + switch (iinfo->i_alloc_type) { 128 + case ICBTAG_FLAG_AD_SHORT: 129 + iinfo->cached_extent.epos.offset -= 130 + sizeof(struct short_ad); 131 + break; 132 + case ICBTAG_FLAG_AD_LONG: 133 + iinfo->cached_extent.epos.offset -= 134 + sizeof(struct long_ad); 135 + } 136 + spin_unlock(&iinfo->i_extent_cache_lock); 137 + } 70 138 71 139 void udf_evict_inode(struct inode *inode) 72 140 { ··· 158 90 } 159 91 kfree(iinfo->i_ext.i_data); 160 92 iinfo->i_ext.i_data = NULL; 93 + udf_clear_extent_cache(inode); 161 94 if (want_delete) { 162 95 udf_free_inode(inode); 163 96 } ··· 174 105 truncate_pagecache(inode, to, isize); 175 106 if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { 176 107 down_write(&iinfo->i_data_sem); 108 + udf_clear_extent_cache(inode); 177 109 udf_truncate_extents(inode); 178 110 up_write(&iinfo->i_data_sem); 179 111 } ··· 442 372 iinfo->i_next_alloc_goal++; 443 373 } 444 374 445 - 375 + udf_clear_extent_cache(inode); 446 376 phys = inode_getblk(inode, block, &err, &new); 447 377 if (!phys) 448 378 goto abort; ··· 1241 1171 } else { 1242 1172 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 1243 1173 down_write(&iinfo->i_data_sem); 1174 + udf_clear_extent_cache(inode); 1244 1175 memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr + newsize, 1245 1176 0x00, bsize - newsize - 1246 1177 udf_file_entry_alloc_offset(inode)); ··· 1255 1184 if (err) 1256 1185 return err; 1257 1186 down_write(&iinfo->i_data_sem); 1187 + udf_clear_extent_cache(inode); 1258 1188 truncate_setsize(inode, newsize); 1259 1189 udf_truncate_extents(inode); 1260 1190 up_write(&iinfo->i_data_sem); ··· 2228 2156 struct udf_inode_info *iinfo; 2229 2157 2230 2158 iinfo = UDF_I(inode); 2231 - pos->offset = 0; 2232 - pos->block = iinfo->i_location; 2233 - pos->bh = NULL; 2159 + if (!udf_read_extent_cache(inode, bcount, &lbcount, pos)) { 2160 + pos->offset = 0; 2161 + pos->block = iinfo->i_location; 2162 + pos->bh = NULL; 2163 + } 2234 2164 *elen = 0; 2235 - 2236 2165 do { 2237 2166 etype = udf_next_aext(inode, pos, eloc, elen, 1); 2238 2167 if (etype == -1) { ··· 2243 2170 } 2244 2171 lbcount += *elen; 2245 2172 } while (lbcount <= bcount); 2246 - 2173 + /* update extent cache */ 2174 + udf_update_extent_cache(inode, lbcount - *elen, pos, 1); 2247 2175 *offset = (bcount + *elen - lbcount) >> blocksize_bits; 2248 2176 2249 2177 return etype;
+2
fs/udf/super.c
··· 134 134 ei->i_next_alloc_goal = 0; 135 135 ei->i_strat4096 = 0; 136 136 init_rwsem(&ei->i_data_sem); 137 + ei->cached_extent.lstart = -1; 138 + spin_lock_init(&ei->i_extent_cache_lock); 137 139 138 140 return &ei->vfs_inode; 139 141 }
+16
fs/udf/udf_i.h
··· 1 1 #ifndef _UDF_I_H 2 2 #define _UDF_I_H 3 3 4 + struct extent_position { 5 + struct buffer_head *bh; 6 + uint32_t offset; 7 + struct kernel_lb_addr block; 8 + }; 9 + 10 + struct udf_ext_cache { 11 + /* Extent position */ 12 + struct extent_position epos; 13 + /* Start logical offset in bytes */ 14 + loff_t lstart; 15 + }; 16 + 4 17 /* 5 18 * The i_data_sem and i_mutex serve for protection of allocation information 6 19 * of a regular files and symlinks. This includes all extents belonging to ··· 48 35 __u8 *i_data; 49 36 } i_ext; 50 37 struct rw_semaphore i_data_sem; 38 + struct udf_ext_cache cached_extent; 39 + /* Spinlock for protecting extent cache */ 40 + spinlock_t i_extent_cache_lock; 51 41 struct inode vfs_inode; 52 42 }; 53 43
-5
fs/udf/udfdecl.h
··· 113 113 uint8_t u_len; 114 114 }; 115 115 116 - struct extent_position { 117 - struct buffer_head *bh; 118 - uint32_t offset; 119 - struct kernel_lb_addr block; 120 - }; 121 116 122 117 /* super.c */ 123 118