Commit 255f41c59558a346d65a2012420a7573e36dc584
Exists in
master
and in
7 other branches
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
Showing 6 changed files Side-by-side Diff
fs/logfs/gc.c
... | ... | @@ -459,6 +459,14 @@ |
459 | 459 | struct logfs_block *block; |
460 | 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 | 470 | if (no_free_segments(sb) >= target && |
463 | 471 | super->s_no_object_aliases < MAX_OBJ_ALIASES) |
464 | 472 | return; |
fs/logfs/journal.c
... | ... | @@ -389,7 +389,10 @@ |
389 | 389 | static int journal_erase_segment(struct logfs_area *area) |
390 | 390 | { |
391 | 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 | 396 | u64 ofs; |
394 | 397 | int err; |
395 | 398 | |
396 | 399 | |
... | ... | @@ -397,20 +400,21 @@ |
397 | 400 | if (err) |
398 | 401 | return err; |
399 | 402 | |
400 | - sh.pad = 0; | |
401 | - sh.type = SEG_JOURNAL; | |
402 | - sh.level = 0; | |
403 | - sh.segno = cpu_to_be32(area->a_segno); | |
404 | - sh.ec = cpu_to_be32(area->a_erase_count); | |
405 | - sh.gec = cpu_to_be64(logfs_super(sb)->s_gec); | |
406 | - sh.crc = logfs_crc32(&sh, sizeof(sh), 4); | |
403 | + memset(&u, 0, sizeof(u)); | |
404 | + u.sh.pad = 0; | |
405 | + u.sh.type = SEG_JOURNAL; | |
406 | + u.sh.level = 0; | |
407 | + u.sh.segno = cpu_to_be32(area->a_segno); | |
408 | + u.sh.ec = cpu_to_be32(area->a_erase_count); | |
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 | 412 | /* This causes a bug in segment.c. Not yet. */ |
409 | 413 | //logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count, 0); |
410 | 414 | |
411 | 415 | ofs = dev_ofs(sb, area->a_segno, 0); |
412 | - area->a_used_bytes = ALIGN(sizeof(sh), 16); | |
413 | - logfs_buf_write(area, ofs, &sh, sizeof(sh)); | |
416 | + area->a_used_bytes = sizeof(u); | |
417 | + logfs_buf_write(area, ofs, &u, sizeof(u)); | |
414 | 418 | return 0; |
415 | 419 | } |
416 | 420 | |
... | ... | @@ -494,6 +498,8 @@ |
494 | 498 | |
495 | 499 | btree_grim_visitor64(&tree->new, (unsigned long)sb, account_shadow); |
496 | 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 | 504 | if (li->li_block) { |
499 | 505 | /* |
500 | 506 | |
... | ... | @@ -607,9 +613,9 @@ |
607 | 613 | if (len == 0) |
608 | 614 | return logfs_write_header(super, header, 0, type); |
609 | 615 | |
616 | + BUG_ON(len > sb->s_blocksize); | |
610 | 617 | compr_len = logfs_compress(buf, data, len, sb->s_blocksize); |
611 | 618 | if (compr_len < 0 || type == JE_ANCHOR) { |
612 | - BUG_ON(len > sb->s_blocksize); | |
613 | 619 | memcpy(data, buf, len); |
614 | 620 | compr_len = len; |
615 | 621 | compr = COMPR_NONE; |
... | ... | @@ -661,6 +667,7 @@ |
661 | 667 | if (ofs < 0) |
662 | 668 | return ofs; |
663 | 669 | logfs_buf_write(area, ofs, super->s_compressed_je, len); |
670 | + BUG_ON(super->s_no_je >= MAX_JOURNAL_ENTRIES); | |
664 | 671 | super->s_je_array[super->s_no_je++] = cpu_to_be64(ofs); |
665 | 672 | return 0; |
666 | 673 | } |
fs/logfs/logfs.h
... | ... | @@ -257,10 +257,14 @@ |
257 | 257 | * struct shadow_tree |
258 | 258 | * @new: shadows where old_ofs==0, indexed by new_ofs |
259 | 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 | 263 | struct shadow_tree { |
262 | 264 | struct btree_head64 new; |
263 | 265 | struct btree_head64 old; |
266 | + struct btree_head32 segment_map; | |
267 | + int no_shadowed_segments; | |
264 | 268 | }; |
265 | 269 | |
266 | 270 | struct object_alias_item { |
267 | 271 | |
... | ... | @@ -305,13 +309,14 @@ |
305 | 309 | level_t level, int child_no, __be64 val); |
306 | 310 | struct logfs_block_ops { |
307 | 311 | void (*write_block)(struct logfs_block *block); |
308 | - gc_level_t (*block_level)(struct logfs_block *block); | |
309 | 312 | void (*free_block)(struct super_block *sb, struct logfs_block*block); |
310 | 313 | int (*write_alias)(struct super_block *sb, |
311 | 314 | struct logfs_block *block, |
312 | 315 | write_alias_t *write_one_alias); |
313 | 316 | }; |
314 | 317 | |
318 | +#define MAX_JOURNAL_ENTRIES 256 | |
319 | + | |
315 | 320 | struct logfs_super { |
316 | 321 | struct mtd_info *s_mtd; /* underlying device */ |
317 | 322 | struct block_device *s_bdev; /* underlying device */ |
... | ... | @@ -378,7 +383,7 @@ |
378 | 383 | u32 s_journal_ec[LOGFS_JOURNAL_SEGS]; /* journal erasecounts */ |
379 | 384 | u64 s_last_version; |
380 | 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 | 387 | int s_no_je; |
383 | 388 | |
384 | 389 | int s_sum_index; /* for the 12 summaries */ |
... | ... | @@ -720,6 +725,12 @@ |
720 | 725 | gc_level_t gc_level) |
721 | 726 | { |
722 | 727 | return logfs_super(sb)->s_area[(__force u8)gc_level]; |
728 | +} | |
729 | + | |
730 | +static inline void logfs_mempool_destroy(mempool_t *pool) | |
731 | +{ | |
732 | + if (pool) | |
733 | + mempool_destroy(pool); | |
723 | 734 | } |
724 | 735 | |
725 | 736 | #endif |
fs/logfs/readwrite.c
... | ... | @@ -430,25 +430,6 @@ |
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 | 434 | * This silences a false, yet annoying gcc warning. I hate it when my editor |
454 | 435 | * jumps into bitops.h each time I recompile this file. |
455 | 436 | |
... | ... | @@ -587,14 +568,12 @@ |
587 | 568 | |
588 | 569 | static struct logfs_block_ops inode_block_ops = { |
589 | 570 | .write_block = inode_write_block, |
590 | - .block_level = inode_block_level, | |
591 | 571 | .free_block = inode_free_block, |
592 | 572 | .write_alias = inode_write_alias, |
593 | 573 | }; |
594 | 574 | |
595 | 575 | struct logfs_block_ops indirect_block_ops = { |
596 | 576 | .write_block = indirect_write_block, |
597 | - .block_level = indirect_block_level, | |
598 | 577 | .free_block = indirect_free_block, |
599 | 578 | .write_alias = indirect_write_alias, |
600 | 579 | }; |
... | ... | @@ -1241,6 +1220,18 @@ |
1241 | 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 | 1236 | * fill_shadow_tree - Propagate shadow tree changes due to a write |
1246 | 1237 | * @inode: Inode owning the page |
... | ... | @@ -1288,6 +1279,8 @@ |
1288 | 1279 | |
1289 | 1280 | super->s_dirty_used_bytes += shadow->new_len; |
1290 | 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 | |
1294 | 1287 | |
1295 | 1288 | |
1296 | 1289 | |
1297 | 1290 | |
... | ... | @@ -1845,19 +1838,37 @@ |
1845 | 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 | 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 | - err = __logfs_truncate(inode, size); | |
1855 | - if (!err) | |
1856 | - err = __logfs_write_inode(inode, 0); | |
1857 | - logfs_put_wblocks(sb, NULL, 1); | |
1854 | + size = ALIGN(size, TRUNCATE_STEP); | |
1855 | + while (size > target) { | |
1856 | + if (size > TRUNCATE_STEP) | |
1857 | + size -= TRUNCATE_STEP; | |
1858 | + else | |
1859 | + size = 0; | |
1860 | + if (size < target) | |
1861 | + size = target; | |
1858 | 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 | + } | |
1869 | + | |
1859 | 1870 | if (!err) |
1860 | - err = vmtruncate(inode, size); | |
1871 | + err = vmtruncate(inode, target); | |
1861 | 1872 | |
1862 | 1873 | /* I don't trust error recovery yet. */ |
1863 | 1874 | WARN_ON(err); |
... | ... | @@ -2251,9 +2262,7 @@ |
2251 | 2262 | struct logfs_super *super = logfs_super(sb); |
2252 | 2263 | |
2253 | 2264 | destroy_meta_inode(super->s_segfile_inode); |
2254 | - if (super->s_block_pool) | |
2255 | - mempool_destroy(super->s_block_pool); | |
2256 | - if (super->s_shadow_pool) | |
2257 | - mempool_destroy(super->s_shadow_pool); | |
2265 | + logfs_mempool_destroy(super->s_block_pool); | |
2266 | + logfs_mempool_destroy(super->s_shadow_pool); | |
2258 | 2267 | } |
fs/logfs/segment.c
... | ... | @@ -183,14 +183,8 @@ |
183 | 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 | 186 | static struct logfs_block_ops btree_block_ops = { |
192 | 187 | .write_block = btree_write_block, |
193 | - .block_level = btree_block_level, | |
194 | 188 | .free_block = __free_block, |
195 | 189 | .write_alias = btree_write_alias, |
196 | 190 | }; |
... | ... | @@ -919,7 +913,7 @@ |
919 | 913 | for (i--; i >= 0; i--) |
920 | 914 | free_area(super->s_area[i]); |
921 | 915 | free_area(super->s_journal_area); |
922 | - mempool_destroy(super->s_alias_pool); | |
916 | + logfs_mempool_destroy(super->s_alias_pool); | |
923 | 917 | return -ENOMEM; |
924 | 918 | } |
925 | 919 |
fs/logfs/super.c
... | ... | @@ -12,6 +12,7 @@ |
12 | 12 | #include "logfs.h" |
13 | 13 | #include <linux/bio.h> |
14 | 14 | #include <linux/slab.h> |
15 | +#include <linux/blkdev.h> | |
15 | 16 | #include <linux/mtd/mtd.h> |
16 | 17 | #include <linux/statfs.h> |
17 | 18 | #include <linux/buffer_head.h> |
... | ... | @@ -137,6 +138,10 @@ |
137 | 138 | sb->s_fs_info = super; |
138 | 139 | sb->s_mtd = super->s_mtd; |
139 | 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 | 145 | return 0; |
141 | 146 | } |
142 | 147 | |
... | ... | @@ -452,6 +457,8 @@ |
452 | 457 | |
453 | 458 | btree_init_mempool64(&super->s_shadow_tree.new, super->s_btree_pool); |
454 | 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 | 463 | ret = logfs_init_mapping(sb); |
457 | 464 | if (ret) |
... | ... | @@ -516,8 +523,8 @@ |
516 | 523 | if (super->s_erase_page) |
517 | 524 | __free_page(super->s_erase_page); |
518 | 525 | super->s_devops->put_device(sb); |
519 | - mempool_destroy(super->s_btree_pool); | |
520 | - mempool_destroy(super->s_alias_pool); | |
526 | + logfs_mempool_destroy(super->s_btree_pool); | |
527 | + logfs_mempool_destroy(super->s_alias_pool); | |
521 | 528 | kfree(super); |
522 | 529 | log_super("LogFS: Finished unmounting\n"); |
523 | 530 | } |