From 5a0e3ad6af8660be21ca98a971cd00f331318c05 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 24 Mar 2010 17:04:11 +0900 Subject: include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo Guess-its-ok-by: Christoph Lameter Cc: Ingo Molnar Cc: Lee Schermerhorn --- fs/logfs/dev_bdev.c | 1 + fs/logfs/dir.c | 2 +- fs/logfs/gc.c | 1 + fs/logfs/inode.c | 1 + fs/logfs/journal.c | 1 + fs/logfs/readwrite.c | 1 + fs/logfs/segment.c | 1 + fs/logfs/super.c | 1 + 8 files changed, 8 insertions(+), 1 deletion(-) (limited to 'fs/logfs') diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c index 9718c22f186d..18e8c144c7f1 100644 --- a/fs/logfs/dev_bdev.c +++ b/fs/logfs/dev_bdev.c @@ -9,6 +9,7 @@ #include #include #include +#include #define PAGE_OFS(ofs) ((ofs) & (PAGE_SIZE-1)) diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index 56a8bfbb0120..e1cb99566100 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -6,7 +6,7 @@ * Copyright (c) 2005-2008 Joern Engel */ #include "logfs.h" - +#include /* * Atomic dir operations diff --git a/fs/logfs/gc.c b/fs/logfs/gc.c index 92949f95a901..84e36f52fe95 100644 --- a/fs/logfs/gc.c +++ b/fs/logfs/gc.c @@ -7,6 +7,7 @@ */ #include "logfs.h" #include +#include /* * Wear leveling needs to kick in when the difference between low erase diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c index 33ec1aeaeec4..14ed27274da2 100644 --- a/fs/logfs/inode.c +++ b/fs/logfs/inode.c @@ -6,6 +6,7 @@ * Copyright (c) 2005-2008 Joern Engel */ #include "logfs.h" +#include #include #include diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c index 6ad30a4c9052..f186043e862a 100644 --- a/fs/logfs/journal.c +++ b/fs/logfs/journal.c @@ -6,6 +6,7 @@ * Copyright (c) 2005-2008 Joern Engel */ #include "logfs.h" +#include static void logfs_calc_free(struct super_block *sb) { diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 7a23b3e7c0a7..d5919af2c7a7 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -18,6 +18,7 @@ */ #include "logfs.h" #include +#include static u64 adjust_bix(u64 bix, level_t level) { diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c index 1a14f9910d55..614d7a6fda2d 100644 --- a/fs/logfs/segment.c +++ b/fs/logfs/segment.c @@ -10,6 +10,7 @@ * three kinds of objects: inodes, dentries and blocks, both data and indirect. */ #include "logfs.h" +#include static int logfs_mark_segment_bad(struct super_block *sb, u32 segno) { diff --git a/fs/logfs/super.c b/fs/logfs/super.c index c66beab78dee..46990eafe052 100644 --- a/fs/logfs/super.c +++ b/fs/logfs/super.c @@ -11,6 +11,7 @@ */ #include "logfs.h" #include +#include #include #include #include -- cgit v1.2.2 From e05c378f4973674a16d5b9636f2310cf88aca5f2 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 30 Mar 2010 18:25:17 +0200 Subject: [LogFS] Remove unused method All callers are long gone. Signed-off-by: Joern Engel --- fs/logfs/logfs.h | 1 - fs/logfs/readwrite.c | 21 --------------------- fs/logfs/segment.c | 6 ------ 3 files changed, 28 deletions(-) (limited to 'fs/logfs') diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index b84b0eec6024..97195b9e93a5 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -305,7 +305,6 @@ typedef int write_alias_t(struct super_block *sb, u64 ino, u64 bix, level_t level, int child_no, __be64 val); struct logfs_block_ops { void (*write_block)(struct logfs_block *block); - gc_level_t (*block_level)(struct logfs_block *block); void (*free_block)(struct super_block *sb, struct logfs_block*block); int (*write_alias)(struct super_block *sb, struct logfs_block *block, diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index c3a3a6814b84..3659c37fbd72 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -429,25 +429,6 @@ static void inode_write_block(struct logfs_block *block) } } -static gc_level_t inode_block_level(struct logfs_block *block) -{ - BUG_ON(block->inode->i_ino == LOGFS_INO_MASTER); - return GC_LEVEL(LOGFS_MAX_LEVELS); -} - -static gc_level_t indirect_block_level(struct logfs_block *block) -{ - struct page *page; - struct inode *inode; - u64 bix; - level_t level; - - page = block->page; - inode = page->mapping->host; - logfs_unpack_index(page->index, &bix, &level); - return expand_level(inode->i_ino, level); -} - /* * This silences a false, yet annoying gcc warning. I hate it when my editor * jumps into bitops.h each time I recompile this file. @@ -586,14 +567,12 @@ static void indirect_free_block(struct super_block *sb, static struct logfs_block_ops inode_block_ops = { .write_block = inode_write_block, - .block_level = inode_block_level, .free_block = inode_free_block, .write_alias = inode_write_alias, }; struct logfs_block_ops indirect_block_ops = { .write_block = indirect_write_block, - .block_level = indirect_block_level, .free_block = indirect_free_block, .write_alias = indirect_write_alias, }; diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c index 0ecd8f07c11e..02db22ebbf13 100644 --- a/fs/logfs/segment.c +++ b/fs/logfs/segment.c @@ -182,14 +182,8 @@ static int btree_write_alias(struct super_block *sb, struct logfs_block *block, return 0; } -static gc_level_t btree_block_level(struct logfs_block *block) -{ - return expand_level(block->ino, block->level); -} - static struct logfs_block_ops btree_block_ops = { .write_block = btree_write_block, - .block_level = btree_block_level, .free_block = __free_block, .write_alias = btree_write_alias, }; -- cgit v1.2.2 From 032d8f7268444a0f5d4ee02d9513d682d5b8edfc Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 13 Apr 2010 17:46:37 +0200 Subject: [LogFS] Prevent memory corruption on large deletes Removing sufficiently large files would create aliases for a large number of segments. This in turn results in a large number of journal entries and an overflow of s_je_array. Cheap fix is to add a BUG_ON, turning memory corruption into something annoying, but less dangerous. Real fix is to count the number of affected segments and prevent the problem completely. Signed-off-by: Joern Engel --- fs/logfs/gc.c | 8 ++++++++ fs/logfs/journal.c | 3 +++ fs/logfs/logfs.h | 8 +++++++- fs/logfs/readwrite.c | 14 ++++++++++++++ fs/logfs/super.c | 2 ++ 5 files changed, 34 insertions(+), 1 deletion(-) (limited to 'fs/logfs') diff --git a/fs/logfs/gc.c b/fs/logfs/gc.c index 92949f95a901..e8253e7fb6b2 100644 --- a/fs/logfs/gc.c +++ b/fs/logfs/gc.c @@ -458,6 +458,14 @@ static void __logfs_gc_pass(struct super_block *sb, int target) struct logfs_block *block; int round, progress, last_progress = 0; + /* + * Doing too many changes to the segfile at once would result + * in a large number of aliases. Write the journal before + * things get out of hand. + */ + if (super->s_shadow_tree.no_shadowed_segments >= MAX_OBJ_ALIASES) + logfs_write_anchor(sb); + if (no_free_segments(sb) >= target && super->s_no_object_aliases < MAX_OBJ_ALIASES) return; diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c index d57c7b07b60b..2c22a4ad5329 100644 --- a/fs/logfs/journal.c +++ b/fs/logfs/journal.c @@ -493,6 +493,8 @@ static void account_shadows(struct super_block *sb) btree_grim_visitor64(&tree->new, (unsigned long)sb, account_shadow); btree_grim_visitor64(&tree->old, (unsigned long)sb, account_shadow); + btree_grim_visitor32(&tree->segment_map, 0, NULL); + tree->no_shadowed_segments = 0; if (li->li_block) { /* @@ -660,6 +662,7 @@ static int logfs_write_je_buf(struct super_block *sb, void *buf, u16 type, if (ofs < 0) return ofs; logfs_buf_write(area, ofs, super->s_compressed_je, len); + BUG_ON(super->s_no_je >= MAX_JOURNAL_ENTRIES); super->s_je_array[super->s_no_je++] = cpu_to_be64(ofs); return 0; } diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index 97195b9e93a5..c9929eed80b1 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -257,10 +257,14 @@ struct logfs_shadow { * struct shadow_tree * @new: shadows where old_ofs==0, indexed by new_ofs * @old: shadows where old_ofs!=0, indexed by old_ofs + * @segment_map: bitfield of segments containing shadows + * @no_shadowed_segment: number of segments containing shadows */ struct shadow_tree { struct btree_head64 new; struct btree_head64 old; + struct btree_head32 segment_map; + int no_shadowed_segments; }; struct object_alias_item { @@ -311,6 +315,8 @@ struct logfs_block_ops { write_alias_t *write_one_alias); }; +#define MAX_JOURNAL_ENTRIES 256 + struct logfs_super { struct mtd_info *s_mtd; /* underlying device */ struct block_device *s_bdev; /* underlying device */ @@ -377,7 +383,7 @@ struct logfs_super { u32 s_journal_ec[LOGFS_JOURNAL_SEGS]; /* journal erasecounts */ u64 s_last_version; struct logfs_area *s_journal_area; /* open journal segment */ - __be64 s_je_array[64]; + __be64 s_je_array[MAX_JOURNAL_ENTRIES]; int s_no_je; int s_sum_index; /* for the 12 summaries */ diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 3659c37fbd72..7e0c39c49719 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -1219,6 +1219,18 @@ static void free_shadow(struct inode *inode, struct logfs_shadow *shadow) mempool_free(shadow, super->s_shadow_pool); } +static void mark_segment(struct shadow_tree *tree, u32 segno) +{ + int err; + + if (!btree_lookup32(&tree->segment_map, segno)) { + err = btree_insert32(&tree->segment_map, segno, (void *)1, + GFP_NOFS); + BUG_ON(err); + tree->no_shadowed_segments++; + } +} + /** * fill_shadow_tree - Propagate shadow tree changes due to a write * @inode: Inode owning the page @@ -1266,6 +1278,8 @@ static void fill_shadow_tree(struct inode *inode, struct page *page, super->s_dirty_used_bytes += shadow->new_len; super->s_dirty_free_bytes += shadow->old_len; + mark_segment(tree, shadow->old_ofs >> super->s_segshift); + mark_segment(tree, shadow->new_ofs >> super->s_segshift); } } diff --git a/fs/logfs/super.c b/fs/logfs/super.c index 9d856c49afc5..d6e1f4fc3115 100644 --- a/fs/logfs/super.c +++ b/fs/logfs/super.c @@ -451,6 +451,8 @@ static int logfs_read_sb(struct super_block *sb, int read_only) btree_init_mempool64(&super->s_shadow_tree.new, super->s_btree_pool); btree_init_mempool64(&super->s_shadow_tree.old, super->s_btree_pool); + btree_init_mempool32(&super->s_shadow_tree.segment_map, + super->s_btree_pool); ret = logfs_init_mapping(sb); if (ret) -- cgit v1.2.2 From d3a03f8031000f8297823b80e36db536fd020884 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 13 Apr 2010 17:54:27 +0200 Subject: [LogFS] Plug 8 byte information leak Within each journal segment, 8 bytes at offset 24 would remain uninitialized. Signed-off-by: Joern Engel --- fs/logfs/journal.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'fs/logfs') diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c index 2c22a4ad5329..2957bfc21927 100644 --- a/fs/logfs/journal.c +++ b/fs/logfs/journal.c @@ -388,7 +388,10 @@ static void journal_get_erase_count(struct logfs_area *area) static int journal_erase_segment(struct logfs_area *area) { struct super_block *sb = area->a_sb; - struct logfs_segment_header sh; + union { + struct logfs_segment_header sh; + unsigned char c[ALIGN(sizeof(struct logfs_segment_header), 16)]; + } u; u64 ofs; int err; @@ -396,20 +399,21 @@ static int journal_erase_segment(struct logfs_area *area) if (err) return err; - sh.pad = 0; - sh.type = SEG_JOURNAL; - sh.level = 0; - sh.segno = cpu_to_be32(area->a_segno); - sh.ec = cpu_to_be32(area->a_erase_count); - sh.gec = cpu_to_be64(logfs_super(sb)->s_gec); - sh.crc = logfs_crc32(&sh, sizeof(sh), 4); + memset(&u, 0, sizeof(u)); + u.sh.pad = 0; + u.sh.type = SEG_JOURNAL; + u.sh.level = 0; + u.sh.segno = cpu_to_be32(area->a_segno); + u.sh.ec = cpu_to_be32(area->a_erase_count); + u.sh.gec = cpu_to_be64(logfs_super(sb)->s_gec); + u.sh.crc = logfs_crc32(&u.sh, sizeof(u.sh), 4); /* This causes a bug in segment.c. Not yet. */ //logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count, 0); ofs = dev_ofs(sb, area->a_segno, 0); - area->a_used_bytes = ALIGN(sizeof(sh), 16); - logfs_buf_write(area, ofs, &sh, sizeof(sh)); + area->a_used_bytes = sizeof(u); + logfs_buf_write(area, ofs, &u, sizeof(u)); return 0; } -- cgit v1.2.2 From ead88af5f577fd2b399a0fcdfe52605116fac489 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 13 Apr 2010 17:57:21 +0200 Subject: [LogFS] Move assertion The assertion is valid independently of the condition. Signed-off-by: Joern Engel --- fs/logfs/journal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/logfs') diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c index 2957bfc21927..fd44eeea5135 100644 --- a/fs/logfs/journal.c +++ b/fs/logfs/journal.c @@ -612,9 +612,9 @@ static size_t __logfs_write_je(struct super_block *sb, void *buf, u16 type, if (len == 0) return logfs_write_header(super, header, 0, type); + BUG_ON(len > sb->s_blocksize); compr_len = logfs_compress(buf, data, len, sb->s_blocksize); if (compr_len < 0 || type == JE_ANCHOR) { - BUG_ON(len > sb->s_blocksize); memcpy(data, buf, len); compr_len = len; compr = COMPR_NONE; -- cgit v1.2.2 From 1f1b0008e8dd1930d6e89522c70f4a438374302a Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Thu, 15 Apr 2010 08:03:57 +0200 Subject: [LogFS] Prevent mempool_destroy NULL pointer dereference It would probably be better to just accept NULL pointers in mempool_destroy(). But for the current -rc series let's keep things simple. This patch was lost in the cracks for a while. Kevin Cernekee had to rediscover the problem and send a similar patch because of it. :( Signed-off-by: Joern Engel --- fs/logfs/logfs.h | 6 ++++++ fs/logfs/readwrite.c | 6 ++---- fs/logfs/segment.c | 2 +- fs/logfs/super.c | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) (limited to 'fs/logfs') diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index c9929eed80b1..0a3df1a0c936 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -727,4 +727,10 @@ static inline struct logfs_area *get_area(struct super_block *sb, return logfs_super(sb)->s_area[(__force u8)gc_level]; } +static inline void logfs_mempool_destroy(mempool_t *pool) +{ + if (pool) + mempool_destroy(pool); +} + #endif diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 7e0c39c49719..aca6c56a107a 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -2243,8 +2243,6 @@ void logfs_cleanup_rw(struct super_block *sb) struct logfs_super *super = logfs_super(sb); destroy_meta_inode(super->s_segfile_inode); - if (super->s_block_pool) - mempool_destroy(super->s_block_pool); - if (super->s_shadow_pool) - mempool_destroy(super->s_shadow_pool); + logfs_mempool_destroy(super->s_block_pool); + logfs_mempool_destroy(super->s_shadow_pool); } diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c index 02db22ebbf13..8c82fe05d3e1 100644 --- a/fs/logfs/segment.c +++ b/fs/logfs/segment.c @@ -912,7 +912,7 @@ err: for (i--; i >= 0; i--) free_area(super->s_area[i]); free_area(super->s_journal_area); - mempool_destroy(super->s_alias_pool); + logfs_mempool_destroy(super->s_alias_pool); return -ENOMEM; } diff --git a/fs/logfs/super.c b/fs/logfs/super.c index d6e1f4fc3115..d4531eb46d0a 100644 --- a/fs/logfs/super.c +++ b/fs/logfs/super.c @@ -517,8 +517,8 @@ static void logfs_kill_sb(struct super_block *sb) if (super->s_erase_page) __free_page(super->s_erase_page); super->s_devops->put_device(sb); - mempool_destroy(super->s_btree_pool); - mempool_destroy(super->s_alias_pool); + logfs_mempool_destroy(super->s_btree_pool); + logfs_mempool_destroy(super->s_alias_pool); kfree(super); log_super("LogFS: Finished unmounting\n"); } -- cgit v1.2.2 From b8639077abf034824046ed09e779b74c4393031f Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Sat, 17 Apr 2010 19:54:27 +0200 Subject: [LogFS] Set s_bdi Since 32a88aa1 sync() was turned into a NOP for logfs. Worse, sync() would not return an error, giving the illusion that writeout had actually happened. Afaics jffs2 was broken as well. Signed-off-by: Joern Engel --- fs/logfs/super.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs/logfs') diff --git a/fs/logfs/super.c b/fs/logfs/super.c index d4531eb46d0a..dacce3a917ae 100644 --- a/fs/logfs/super.c +++ b/fs/logfs/super.c @@ -11,6 +11,7 @@ */ #include "logfs.h" #include +#include #include #include #include @@ -136,6 +137,10 @@ static int logfs_sb_set(struct super_block *sb, void *_super) sb->s_fs_info = super; sb->s_mtd = super->s_mtd; sb->s_bdev = super->s_bdev; + if (sb->s_bdev) + sb->s_bdi = &bdev_get_queue(sb->s_bdev)->backing_dev_info; + if (sb->s_mtd) + sb->s_bdi = sb->s_mtd->backing_dev_info; return 0; } -- cgit v1.2.2 From b6349ac89eacb813f6963f7263da05bc3f483351 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Tue, 20 Apr 2010 21:44:10 +0200 Subject: [LogFS] Split large truncated into smaller chunks Truncate would do an almost limitless amount of work without invoking the garbage collector in between. Split it up into more manageable, though still large, chunks. Signed-off-by: Joern Engel --- fs/logfs/readwrite.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'fs/logfs') diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index aca6c56a107a..7e3a1e5fd76d 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -1837,19 +1837,37 @@ static int __logfs_truncate(struct inode *inode, u64 size) return logfs_truncate_direct(inode, size); } -int logfs_truncate(struct inode *inode, u64 size) +/* + * Truncate, by changing the segment file, can consume a fair amount + * of resources. So back off from time to time and do some GC. + * 8 or 2048 blocks should be well within safety limits even if + * every single block resided in a different segment. + */ +#define TRUNCATE_STEP (8 * 1024 * 1024) +int logfs_truncate(struct inode *inode, u64 target) { struct super_block *sb = inode->i_sb; - int err; + u64 size = i_size_read(inode); + int err = 0; - logfs_get_wblocks(sb, NULL, 1); - err = __logfs_truncate(inode, size); - if (!err) - err = __logfs_write_inode(inode, 0); - logfs_put_wblocks(sb, NULL, 1); + size = ALIGN(size, TRUNCATE_STEP); + while (size > target) { + if (size > TRUNCATE_STEP) + size -= TRUNCATE_STEP; + else + size = 0; + if (size < target) + size = target; + + logfs_get_wblocks(sb, NULL, 1); + err = __logfs_truncate(inode, target); + if (!err) + err = __logfs_write_inode(inode, 0); + logfs_put_wblocks(sb, NULL, 1); + } if (!err) - err = vmtruncate(inode, size); + err = vmtruncate(inode, target); /* I don't trust error recovery yet. */ WARN_ON(err); -- cgit v1.2.2