From 9734c971aa6be6db61226b0046e080ca10383748 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 17 Jan 2013 22:11:38 +0100 Subject: udf: Write LVID to disk after opening / closing So far we just marked the buffer as dirty and left writing on flusher thread but especially on opening that opens possible race window where we could write other modified fs structures to disk before we mark filesystem as open. So sync LVID buffer to disk after opening and closing fs. Reported-by: Steve Nickel Signed-off-by: Jan Kara --- fs/udf/super.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs/udf/super.c') diff --git a/fs/udf/super.c b/fs/udf/super.c index e9be396a558d..186adbf94b20 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1866,6 +1866,8 @@ static void udf_open_lvid(struct super_block *sb) mark_buffer_dirty(bh); sbi->s_lvid_dirty = 0; mutex_unlock(&sbi->s_alloc_mutex); + /* Make opening of filesystem visible on the media immediately */ + sync_dirty_buffer(bh); } static void udf_close_lvid(struct super_block *sb) @@ -1906,6 +1908,8 @@ static void udf_close_lvid(struct super_block *sb) mark_buffer_dirty(bh); sbi->s_lvid_dirty = 0; mutex_unlock(&sbi->s_alloc_mutex); + /* Make closing of filesystem visible on the media immediately */ + sync_dirty_buffer(bh); } u64 lvid_get_unique_id(struct super_block *sb) -- cgit v1.2.3 From 99600051b04bc4ec8bd4d16a8bf993ca54042db6 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 19 Jan 2013 11:17:14 +0900 Subject: 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 Signed-off-by: Ashish Sangwan Signed-off-by: Bonggil Bak Signed-off-by: Jan Kara --- fs/udf/inode.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- fs/udf/super.c | 2 ++ fs/udf/udf_i.h | 16 +++++++++++ fs/udf/udfdecl.h | 5 ---- 4 files changed, 98 insertions(+), 11 deletions(-) (limited to 'fs/udf/super.c') diff --git a/fs/udf/inode.c b/fs/udf/inode.c index cbae1ed0b7c1..7a12e48ad819 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -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; @@ -1171,6 +1241,7 @@ set_size: } 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)); @@ -1184,6 +1255,7 @@ set_size: 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); @@ -2156,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) { @@ -2170,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; diff --git a/fs/udf/super.c b/fs/udf/super.c index 186adbf94b20..da8ce9f14387 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -134,6 +134,8 @@ static struct inode *udf_alloc_inode(struct super_block *sb) ei->i_next_alloc_goal = 0; ei->i_strat4096 = 0; init_rwsem(&ei->i_data_sem); + ei->cached_extent.lstart = -1; + spin_lock_init(&ei->i_extent_cache_lock); return &ei->vfs_inode; } diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h index bb8309dcd5c1..b5cd8ed2aa12 100644 --- a/fs/udf/udf_i.h +++ b/fs/udf/udf_i.h @@ -1,6 +1,19 @@ #ifndef _UDF_I_H #define _UDF_I_H +struct extent_position { + struct buffer_head *bh; + uint32_t offset; + struct kernel_lb_addr block; +}; + +struct udf_ext_cache { + /* Extent position */ + struct extent_position epos; + /* Start logical offset in bytes */ + loff_t lstart; +}; + /* * The i_data_sem and i_mutex serve for protection of allocation information * of a regular files and symlinks. This includes all extents belonging to @@ -35,6 +48,9 @@ struct udf_inode_info { __u8 *i_data; } i_ext; struct rw_semaphore i_data_sem; + struct udf_ext_cache cached_extent; + /* Spinlock for protecting extent cache */ + spinlock_t i_extent_cache_lock; struct inode vfs_inode; }; diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index de038da6f6bd..be7dabbbcb49 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -113,11 +113,6 @@ struct ustr { uint8_t u_len; }; -struct extent_position { - struct buffer_head *bh; - uint32_t offset; - struct kernel_lb_addr block; -}; /* super.c */ -- cgit v1.2.3 From c60305b578674eefe333198c7476dba2178a9082 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 5 Feb 2013 14:08:57 +0100 Subject: udf: Make s_block_bitmap standard array struct udf_bitmap has array of buffer pointers attached to it. The code unnecessarily used s_block_bitmap as a pointer to the array instead of the standard trick of using 0 length array in the declaration. Change that to make code more readable and actually shrink the structure by one pointer. Signed-off-by: Jan Kara --- fs/udf/super.c | 1 - fs/udf/udf_sb.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/udf/super.c') diff --git a/fs/udf/super.c b/fs/udf/super.c index da8ce9f14387..f8830803d389 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1023,7 +1023,6 @@ static struct udf_bitmap *udf_sb_alloc_bitmap(struct super_block *sb, u32 index) if (bitmap == NULL) return NULL; - bitmap->s_block_bitmap = (struct buffer_head **)(bitmap + 1); bitmap->s_nr_groups = nr_groups; return bitmap; } diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index 8d1c9d4f439e..4f7ddb796991 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h @@ -83,7 +83,7 @@ struct udf_bitmap { __u32 s_extLength; __u32 s_extPosition; int s_nr_groups; - struct buffer_head **s_block_bitmap; + struct buffer_head *s_block_bitmap[0]; }; struct udf_part_map { -- cgit v1.2.3 From 288be96de66fa7c09f2cf00a7793db5b4bac4213 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 5 Feb 2013 13:58:29 +0100 Subject: udf: Remove unused s_extLength from udf_bitmap s_extLength was assigned to but the value was never really used. So just remove the field. Signed-off-by: Jan Kara --- fs/udf/super.c | 4 ---- fs/udf/udf_sb.h | 1 - 2 files changed, 5 deletions(-) (limited to 'fs/udf/super.c') diff --git a/fs/udf/super.c b/fs/udf/super.c index f8830803d389..bc5b30a819e8 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1080,8 +1080,6 @@ static int udf_fill_partdesc_info(struct super_block *sb, if (!bitmap) return 1; map->s_uspace.s_bitmap = bitmap; - bitmap->s_extLength = le32_to_cpu( - phd->unallocSpaceBitmap.extLength); bitmap->s_extPosition = le32_to_cpu( phd->unallocSpaceBitmap.extPosition); map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_BITMAP; @@ -1116,8 +1114,6 @@ static int udf_fill_partdesc_info(struct super_block *sb, if (!bitmap) return 1; map->s_fspace.s_bitmap = bitmap; - bitmap->s_extLength = le32_to_cpu( - phd->freedSpaceBitmap.extLength); bitmap->s_extPosition = le32_to_cpu( phd->freedSpaceBitmap.extPosition); map->s_partition_flags |= UDF_PART_FLAG_FREED_BITMAP; diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index 4f7ddb796991..ed401e94aa8c 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h @@ -80,7 +80,6 @@ struct udf_virtual_data { }; struct udf_bitmap { - __u32 s_extLength; __u32 s_extPosition; int s_nr_groups; struct buffer_head *s_block_bitmap[0]; -- cgit v1.2.3