diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-04-21 15:31:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-04-21 15:31:12 -0400 |
commit | 255f41c59558a346d65a2012420a7573e36dc584 (patch) | |
tree | a802e21e60c7e342ad72319853b64e214091b6a9 /fs/logfs | |
parent | 9befb55ef51ad69dd67e279b0533d4cbc5e4c6c0 (diff) | |
parent | b6349ac89eacb813f6963f7263da05bc3f483351 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/joern/logfs
* git://git.kernel.org/pub/scm/linux/kernel/git/joern/logfs:
[LogFS] Split large truncated into smaller chunks
[LogFS] Set s_bdi
[LogFS] Prevent mempool_destroy NULL pointer dereference
[LogFS] Move assertion
[LogFS] Plug 8 byte information leak
[LogFS] Prevent memory corruption on large deletes
[LogFS] Remove unused method
Fix trivial conflict with added header includes in fs/logfs/super.c
Diffstat (limited to 'fs/logfs')
-rw-r--r-- | fs/logfs/gc.c | 8 | ||||
-rw-r--r-- | fs/logfs/journal.c | 29 | ||||
-rw-r--r-- | fs/logfs/logfs.h | 15 | ||||
-rw-r--r-- | fs/logfs/readwrite.c | 75 | ||||
-rw-r--r-- | fs/logfs/segment.c | 8 | ||||
-rw-r--r-- | fs/logfs/super.c | 11 |
6 files changed, 91 insertions, 55 deletions
diff --git a/fs/logfs/gc.c b/fs/logfs/gc.c index 84e36f52fe95..76c242fbe1b0 100644 --- a/fs/logfs/gc.c +++ b/fs/logfs/gc.c | |||
@@ -459,6 +459,14 @@ static void __logfs_gc_pass(struct super_block *sb, int target) | |||
459 | struct logfs_block *block; | 459 | struct logfs_block *block; |
460 | int round, progress, last_progress = 0; | 460 | int round, progress, last_progress = 0; |
461 | 461 | ||
462 | /* | ||
463 | * Doing too many changes to the segfile at once would result | ||
464 | * in a large number of aliases. Write the journal before | ||
465 | * things get out of hand. | ||
466 | */ | ||
467 | if (super->s_shadow_tree.no_shadowed_segments >= MAX_OBJ_ALIASES) | ||
468 | logfs_write_anchor(sb); | ||
469 | |||
462 | if (no_free_segments(sb) >= target && | 470 | if (no_free_segments(sb) >= target && |
463 | super->s_no_object_aliases < MAX_OBJ_ALIASES) | 471 | super->s_no_object_aliases < MAX_OBJ_ALIASES) |
464 | return; | 472 | return; |
diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c index 33bd260b8309..fb0a613f885b 100644 --- a/fs/logfs/journal.c +++ b/fs/logfs/journal.c | |||
@@ -389,7 +389,10 @@ static void journal_get_erase_count(struct logfs_area *area) | |||
389 | static int journal_erase_segment(struct logfs_area *area) | 389 | static int journal_erase_segment(struct logfs_area *area) |
390 | { | 390 | { |
391 | struct super_block *sb = area->a_sb; | 391 | struct super_block *sb = area->a_sb; |
392 | struct logfs_segment_header sh; | 392 | union { |
393 | struct logfs_segment_header sh; | ||
394 | unsigned char c[ALIGN(sizeof(struct logfs_segment_header), 16)]; | ||
395 | } u; | ||
393 | u64 ofs; | 396 | u64 ofs; |
394 | int err; | 397 | int err; |
395 | 398 | ||
@@ -397,20 +400,21 @@ static int journal_erase_segment(struct logfs_area *area) | |||
397 | if (err) | 400 | if (err) |
398 | return err; | 401 | return err; |
399 | 402 | ||
400 | sh.pad = 0; | 403 | memset(&u, 0, sizeof(u)); |
401 | sh.type = SEG_JOURNAL; | 404 | u.sh.pad = 0; |
402 | sh.level = 0; | 405 | u.sh.type = SEG_JOURNAL; |
403 | sh.segno = cpu_to_be32(area->a_segno); | 406 | u.sh.level = 0; |
404 | sh.ec = cpu_to_be32(area->a_erase_count); | 407 | u.sh.segno = cpu_to_be32(area->a_segno); |
405 | sh.gec = cpu_to_be64(logfs_super(sb)->s_gec); | 408 | u.sh.ec = cpu_to_be32(area->a_erase_count); |
406 | sh.crc = logfs_crc32(&sh, sizeof(sh), 4); | 409 | u.sh.gec = cpu_to_be64(logfs_super(sb)->s_gec); |
410 | u.sh.crc = logfs_crc32(&u.sh, sizeof(u.sh), 4); | ||
407 | 411 | ||
408 | /* This causes a bug in segment.c. Not yet. */ | 412 | /* This causes a bug in segment.c. Not yet. */ |
409 | //logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count, 0); | 413 | //logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count, 0); |
410 | 414 | ||
411 | ofs = dev_ofs(sb, area->a_segno, 0); | 415 | ofs = dev_ofs(sb, area->a_segno, 0); |
412 | area->a_used_bytes = ALIGN(sizeof(sh), 16); | 416 | area->a_used_bytes = sizeof(u); |
413 | logfs_buf_write(area, ofs, &sh, sizeof(sh)); | 417 | logfs_buf_write(area, ofs, &u, sizeof(u)); |
414 | return 0; | 418 | return 0; |
415 | } | 419 | } |
416 | 420 | ||
@@ -494,6 +498,8 @@ static void account_shadows(struct super_block *sb) | |||
494 | 498 | ||
495 | btree_grim_visitor64(&tree->new, (unsigned long)sb, account_shadow); | 499 | btree_grim_visitor64(&tree->new, (unsigned long)sb, account_shadow); |
496 | btree_grim_visitor64(&tree->old, (unsigned long)sb, account_shadow); | 500 | btree_grim_visitor64(&tree->old, (unsigned long)sb, account_shadow); |
501 | btree_grim_visitor32(&tree->segment_map, 0, NULL); | ||
502 | tree->no_shadowed_segments = 0; | ||
497 | 503 | ||
498 | if (li->li_block) { | 504 | if (li->li_block) { |
499 | /* | 505 | /* |
@@ -607,9 +613,9 @@ static size_t __logfs_write_je(struct super_block *sb, void *buf, u16 type, | |||
607 | if (len == 0) | 613 | if (len == 0) |
608 | return logfs_write_header(super, header, 0, type); | 614 | return logfs_write_header(super, header, 0, type); |
609 | 615 | ||
616 | BUG_ON(len > sb->s_blocksize); | ||
610 | compr_len = logfs_compress(buf, data, len, sb->s_blocksize); | 617 | compr_len = logfs_compress(buf, data, len, sb->s_blocksize); |
611 | if (compr_len < 0 || type == JE_ANCHOR) { | 618 | if (compr_len < 0 || type == JE_ANCHOR) { |
612 | BUG_ON(len > sb->s_blocksize); | ||
613 | memcpy(data, buf, len); | 619 | memcpy(data, buf, len); |
614 | compr_len = len; | 620 | compr_len = len; |
615 | compr = COMPR_NONE; | 621 | compr = COMPR_NONE; |
@@ -661,6 +667,7 @@ static int logfs_write_je_buf(struct super_block *sb, void *buf, u16 type, | |||
661 | if (ofs < 0) | 667 | if (ofs < 0) |
662 | return ofs; | 668 | return ofs; |
663 | logfs_buf_write(area, ofs, super->s_compressed_je, len); | 669 | logfs_buf_write(area, ofs, super->s_compressed_je, len); |
670 | BUG_ON(super->s_no_je >= MAX_JOURNAL_ENTRIES); | ||
664 | super->s_je_array[super->s_no_je++] = cpu_to_be64(ofs); | 671 | super->s_je_array[super->s_no_je++] = cpu_to_be64(ofs); |
665 | return 0; | 672 | return 0; |
666 | } | 673 | } |
diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index b84b0eec6024..0a3df1a0c936 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h | |||
@@ -257,10 +257,14 @@ struct logfs_shadow { | |||
257 | * struct shadow_tree | 257 | * struct shadow_tree |
258 | * @new: shadows where old_ofs==0, indexed by new_ofs | 258 | * @new: shadows where old_ofs==0, indexed by new_ofs |
259 | * @old: shadows where old_ofs!=0, indexed by old_ofs | 259 | * @old: shadows where old_ofs!=0, indexed by old_ofs |
260 | * @segment_map: bitfield of segments containing shadows | ||
261 | * @no_shadowed_segment: number of segments containing shadows | ||
260 | */ | 262 | */ |
261 | struct shadow_tree { | 263 | struct shadow_tree { |
262 | struct btree_head64 new; | 264 | struct btree_head64 new; |
263 | struct btree_head64 old; | 265 | struct btree_head64 old; |
266 | struct btree_head32 segment_map; | ||
267 | int no_shadowed_segments; | ||
264 | }; | 268 | }; |
265 | 269 | ||
266 | struct object_alias_item { | 270 | struct object_alias_item { |
@@ -305,13 +309,14 @@ typedef int write_alias_t(struct super_block *sb, u64 ino, u64 bix, | |||
305 | level_t level, int child_no, __be64 val); | 309 | level_t level, int child_no, __be64 val); |
306 | struct logfs_block_ops { | 310 | struct logfs_block_ops { |
307 | void (*write_block)(struct logfs_block *block); | 311 | void (*write_block)(struct logfs_block *block); |
308 | gc_level_t (*block_level)(struct logfs_block *block); | ||
309 | void (*free_block)(struct super_block *sb, struct logfs_block*block); | 312 | void (*free_block)(struct super_block *sb, struct logfs_block*block); |
310 | int (*write_alias)(struct super_block *sb, | 313 | int (*write_alias)(struct super_block *sb, |
311 | struct logfs_block *block, | 314 | struct logfs_block *block, |
312 | write_alias_t *write_one_alias); | 315 | write_alias_t *write_one_alias); |
313 | }; | 316 | }; |
314 | 317 | ||
318 | #define MAX_JOURNAL_ENTRIES 256 | ||
319 | |||
315 | struct logfs_super { | 320 | struct logfs_super { |
316 | struct mtd_info *s_mtd; /* underlying device */ | 321 | struct mtd_info *s_mtd; /* underlying device */ |
317 | struct block_device *s_bdev; /* underlying device */ | 322 | struct block_device *s_bdev; /* underlying device */ |
@@ -378,7 +383,7 @@ struct logfs_super { | |||
378 | u32 s_journal_ec[LOGFS_JOURNAL_SEGS]; /* journal erasecounts */ | 383 | u32 s_journal_ec[LOGFS_JOURNAL_SEGS]; /* journal erasecounts */ |
379 | u64 s_last_version; | 384 | u64 s_last_version; |
380 | struct logfs_area *s_journal_area; /* open journal segment */ | 385 | struct logfs_area *s_journal_area; /* open journal segment */ |
381 | __be64 s_je_array[64]; | 386 | __be64 s_je_array[MAX_JOURNAL_ENTRIES]; |
382 | int s_no_je; | 387 | int s_no_je; |
383 | 388 | ||
384 | int s_sum_index; /* for the 12 summaries */ | 389 | int s_sum_index; /* for the 12 summaries */ |
@@ -722,4 +727,10 @@ static inline struct logfs_area *get_area(struct super_block *sb, | |||
722 | return logfs_super(sb)->s_area[(__force u8)gc_level]; | 727 | return logfs_super(sb)->s_area[(__force u8)gc_level]; |
723 | } | 728 | } |
724 | 729 | ||
730 | static inline void logfs_mempool_destroy(mempool_t *pool) | ||
731 | { | ||
732 | if (pool) | ||
733 | mempool_destroy(pool); | ||
734 | } | ||
735 | |||
725 | #endif | 736 | #endif |
diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index bff40253dfb2..3159db6958e5 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c | |||
@@ -430,25 +430,6 @@ static void inode_write_block(struct logfs_block *block) | |||
430 | } | 430 | } |
431 | } | 431 | } |
432 | 432 | ||
433 | static gc_level_t inode_block_level(struct logfs_block *block) | ||
434 | { | ||
435 | BUG_ON(block->inode->i_ino == LOGFS_INO_MASTER); | ||
436 | return GC_LEVEL(LOGFS_MAX_LEVELS); | ||
437 | } | ||
438 | |||
439 | static gc_level_t indirect_block_level(struct logfs_block *block) | ||
440 | { | ||
441 | struct page *page; | ||
442 | struct inode *inode; | ||
443 | u64 bix; | ||
444 | level_t level; | ||
445 | |||
446 | page = block->page; | ||
447 | inode = page->mapping->host; | ||
448 | logfs_unpack_index(page->index, &bix, &level); | ||
449 | return expand_level(inode->i_ino, level); | ||
450 | } | ||
451 | |||
452 | /* | 433 | /* |
453 | * This silences a false, yet annoying gcc warning. I hate it when my editor | 434 | * This silences a false, yet annoying gcc warning. I hate it when my editor |
454 | * jumps into bitops.h each time I recompile this file. | 435 | * jumps into bitops.h each time I recompile this file. |
@@ -587,14 +568,12 @@ static void indirect_free_block(struct super_block *sb, | |||
587 | 568 | ||
588 | static struct logfs_block_ops inode_block_ops = { | 569 | static struct logfs_block_ops inode_block_ops = { |
589 | .write_block = inode_write_block, | 570 | .write_block = inode_write_block, |
590 | .block_level = inode_block_level, | ||
591 | .free_block = inode_free_block, | 571 | .free_block = inode_free_block, |
592 | .write_alias = inode_write_alias, | 572 | .write_alias = inode_write_alias, |
593 | }; | 573 | }; |
594 | 574 | ||
595 | struct logfs_block_ops indirect_block_ops = { | 575 | struct logfs_block_ops indirect_block_ops = { |
596 | .write_block = indirect_write_block, | 576 | .write_block = indirect_write_block, |
597 | .block_level = indirect_block_level, | ||
598 | .free_block = indirect_free_block, | 577 | .free_block = indirect_free_block, |
599 | .write_alias = indirect_write_alias, | 578 | .write_alias = indirect_write_alias, |
600 | }; | 579 | }; |
@@ -1241,6 +1220,18 @@ static void free_shadow(struct inode *inode, struct logfs_shadow *shadow) | |||
1241 | mempool_free(shadow, super->s_shadow_pool); | 1220 | mempool_free(shadow, super->s_shadow_pool); |
1242 | } | 1221 | } |
1243 | 1222 | ||
1223 | static void mark_segment(struct shadow_tree *tree, u32 segno) | ||
1224 | { | ||
1225 | int err; | ||
1226 | |||
1227 | if (!btree_lookup32(&tree->segment_map, segno)) { | ||
1228 | err = btree_insert32(&tree->segment_map, segno, (void *)1, | ||
1229 | GFP_NOFS); | ||
1230 | BUG_ON(err); | ||
1231 | tree->no_shadowed_segments++; | ||
1232 | } | ||
1233 | } | ||
1234 | |||
1244 | /** | 1235 | /** |
1245 | * fill_shadow_tree - Propagate shadow tree changes due to a write | 1236 | * fill_shadow_tree - Propagate shadow tree changes due to a write |
1246 | * @inode: Inode owning the page | 1237 | * @inode: Inode owning the page |
@@ -1288,6 +1279,8 @@ static void fill_shadow_tree(struct inode *inode, struct page *page, | |||
1288 | 1279 | ||
1289 | super->s_dirty_used_bytes += shadow->new_len; | 1280 | super->s_dirty_used_bytes += shadow->new_len; |
1290 | super->s_dirty_free_bytes += shadow->old_len; | 1281 | super->s_dirty_free_bytes += shadow->old_len; |
1282 | mark_segment(tree, shadow->old_ofs >> super->s_segshift); | ||
1283 | mark_segment(tree, shadow->new_ofs >> super->s_segshift); | ||
1291 | } | 1284 | } |
1292 | } | 1285 | } |
1293 | 1286 | ||
@@ -1845,19 +1838,37 @@ static int __logfs_truncate(struct inode *inode, u64 size) | |||
1845 | return logfs_truncate_direct(inode, size); | 1838 | return logfs_truncate_direct(inode, size); |
1846 | } | 1839 | } |
1847 | 1840 | ||
1848 | int logfs_truncate(struct inode *inode, u64 size) | 1841 | /* |
1842 | * Truncate, by changing the segment file, can consume a fair amount | ||
1843 | * of resources. So back off from time to time and do some GC. | ||
1844 | * 8 or 2048 blocks should be well within safety limits even if | ||
1845 | * every single block resided in a different segment. | ||
1846 | */ | ||
1847 | #define TRUNCATE_STEP (8 * 1024 * 1024) | ||
1848 | int logfs_truncate(struct inode *inode, u64 target) | ||
1849 | { | 1849 | { |
1850 | struct super_block *sb = inode->i_sb; | 1850 | struct super_block *sb = inode->i_sb; |
1851 | int err; | 1851 | u64 size = i_size_read(inode); |
1852 | int err = 0; | ||
1852 | 1853 | ||
1853 | logfs_get_wblocks(sb, NULL, 1); | 1854 | size = ALIGN(size, TRUNCATE_STEP); |
1854 | err = __logfs_truncate(inode, size); | 1855 | while (size > target) { |
1855 | if (!err) | 1856 | if (size > TRUNCATE_STEP) |
1856 | err = __logfs_write_inode(inode, 0); | 1857 | size -= TRUNCATE_STEP; |
1857 | logfs_put_wblocks(sb, NULL, 1); | 1858 | else |
1859 | size = 0; | ||
1860 | if (size < target) | ||
1861 | size = target; | ||
1862 | |||
1863 | logfs_get_wblocks(sb, NULL, 1); | ||
1864 | err = __logfs_truncate(inode, target); | ||
1865 | if (!err) | ||
1866 | err = __logfs_write_inode(inode, 0); | ||
1867 | logfs_put_wblocks(sb, NULL, 1); | ||
1868 | } | ||
1858 | 1869 | ||
1859 | if (!err) | 1870 | if (!err) |
1860 | err = vmtruncate(inode, size); | 1871 | err = vmtruncate(inode, target); |
1861 | 1872 | ||
1862 | /* I don't trust error recovery yet. */ | 1873 | /* I don't trust error recovery yet. */ |
1863 | WARN_ON(err); | 1874 | WARN_ON(err); |
@@ -2251,8 +2262,6 @@ void logfs_cleanup_rw(struct super_block *sb) | |||
2251 | struct logfs_super *super = logfs_super(sb); | 2262 | struct logfs_super *super = logfs_super(sb); |
2252 | 2263 | ||
2253 | destroy_meta_inode(super->s_segfile_inode); | 2264 | destroy_meta_inode(super->s_segfile_inode); |
2254 | if (super->s_block_pool) | 2265 | logfs_mempool_destroy(super->s_block_pool); |
2255 | mempool_destroy(super->s_block_pool); | 2266 | logfs_mempool_destroy(super->s_shadow_pool); |
2256 | if (super->s_shadow_pool) | ||
2257 | mempool_destroy(super->s_shadow_pool); | ||
2258 | } | 2267 | } |
diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c index 801a3a141625..f77ce2b470ba 100644 --- a/fs/logfs/segment.c +++ b/fs/logfs/segment.c | |||
@@ -183,14 +183,8 @@ static int btree_write_alias(struct super_block *sb, struct logfs_block *block, | |||
183 | return 0; | 183 | return 0; |
184 | } | 184 | } |
185 | 185 | ||
186 | static gc_level_t btree_block_level(struct logfs_block *block) | ||
187 | { | ||
188 | return expand_level(block->ino, block->level); | ||
189 | } | ||
190 | |||
191 | static struct logfs_block_ops btree_block_ops = { | 186 | static struct logfs_block_ops btree_block_ops = { |
192 | .write_block = btree_write_block, | 187 | .write_block = btree_write_block, |
193 | .block_level = btree_block_level, | ||
194 | .free_block = __free_block, | 188 | .free_block = __free_block, |
195 | .write_alias = btree_write_alias, | 189 | .write_alias = btree_write_alias, |
196 | }; | 190 | }; |
@@ -919,7 +913,7 @@ err: | |||
919 | for (i--; i >= 0; i--) | 913 | for (i--; i >= 0; i--) |
920 | free_area(super->s_area[i]); | 914 | free_area(super->s_area[i]); |
921 | free_area(super->s_journal_area); | 915 | free_area(super->s_journal_area); |
922 | mempool_destroy(super->s_alias_pool); | 916 | logfs_mempool_destroy(super->s_alias_pool); |
923 | return -ENOMEM; | 917 | return -ENOMEM; |
924 | } | 918 | } |
925 | 919 | ||
diff --git a/fs/logfs/super.c b/fs/logfs/super.c index b60bfac3263c..5866ee6e1327 100644 --- a/fs/logfs/super.c +++ b/fs/logfs/super.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "logfs.h" | 12 | #include "logfs.h" |
13 | #include <linux/bio.h> | 13 | #include <linux/bio.h> |
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/blkdev.h> | ||
15 | #include <linux/mtd/mtd.h> | 16 | #include <linux/mtd/mtd.h> |
16 | #include <linux/statfs.h> | 17 | #include <linux/statfs.h> |
17 | #include <linux/buffer_head.h> | 18 | #include <linux/buffer_head.h> |
@@ -137,6 +138,10 @@ static int logfs_sb_set(struct super_block *sb, void *_super) | |||
137 | sb->s_fs_info = super; | 138 | sb->s_fs_info = super; |
138 | sb->s_mtd = super->s_mtd; | 139 | sb->s_mtd = super->s_mtd; |
139 | sb->s_bdev = super->s_bdev; | 140 | sb->s_bdev = super->s_bdev; |
141 | if (sb->s_bdev) | ||
142 | sb->s_bdi = &bdev_get_queue(sb->s_bdev)->backing_dev_info; | ||
143 | if (sb->s_mtd) | ||
144 | sb->s_bdi = sb->s_mtd->backing_dev_info; | ||
140 | return 0; | 145 | return 0; |
141 | } | 146 | } |
142 | 147 | ||
@@ -452,6 +457,8 @@ static int logfs_read_sb(struct super_block *sb, int read_only) | |||
452 | 457 | ||
453 | btree_init_mempool64(&super->s_shadow_tree.new, super->s_btree_pool); | 458 | btree_init_mempool64(&super->s_shadow_tree.new, super->s_btree_pool); |
454 | btree_init_mempool64(&super->s_shadow_tree.old, super->s_btree_pool); | 459 | btree_init_mempool64(&super->s_shadow_tree.old, super->s_btree_pool); |
460 | btree_init_mempool32(&super->s_shadow_tree.segment_map, | ||
461 | super->s_btree_pool); | ||
455 | 462 | ||
456 | ret = logfs_init_mapping(sb); | 463 | ret = logfs_init_mapping(sb); |
457 | if (ret) | 464 | if (ret) |
@@ -516,8 +523,8 @@ static void logfs_kill_sb(struct super_block *sb) | |||
516 | if (super->s_erase_page) | 523 | if (super->s_erase_page) |
517 | __free_page(super->s_erase_page); | 524 | __free_page(super->s_erase_page); |
518 | super->s_devops->put_device(sb); | 525 | super->s_devops->put_device(sb); |
519 | mempool_destroy(super->s_btree_pool); | 526 | logfs_mempool_destroy(super->s_btree_pool); |
520 | mempool_destroy(super->s_alias_pool); | 527 | logfs_mempool_destroy(super->s_alias_pool); |
521 | kfree(super); | 528 | kfree(super); |
522 | log_super("LogFS: Finished unmounting\n"); | 529 | log_super("LogFS: Finished unmounting\n"); |
523 | } | 530 | } |