Commit 255f41c5 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 9befb55e b6349ac8
...@@ -459,6 +459,14 @@ static void __logfs_gc_pass(struct super_block *sb, int target) ...@@ -459,6 +459,14 @@ static void __logfs_gc_pass(struct super_block *sb, int target)
struct logfs_block *block; struct logfs_block *block;
int round, progress, last_progress = 0; 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 && if (no_free_segments(sb) >= target &&
super->s_no_object_aliases < MAX_OBJ_ALIASES) super->s_no_object_aliases < MAX_OBJ_ALIASES)
return; return;
......
...@@ -389,7 +389,10 @@ static void journal_get_erase_count(struct logfs_area *area) ...@@ -389,7 +389,10 @@ static void journal_get_erase_count(struct logfs_area *area)
static int journal_erase_segment(struct logfs_area *area) static int journal_erase_segment(struct logfs_area *area)
{ {
struct super_block *sb = area->a_sb; 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; u64 ofs;
int err; int err;
...@@ -397,20 +400,21 @@ static int journal_erase_segment(struct logfs_area *area) ...@@ -397,20 +400,21 @@ static int journal_erase_segment(struct logfs_area *area)
if (err) if (err)
return err; return err;
sh.pad = 0; memset(&u, 0, sizeof(u));
sh.type = SEG_JOURNAL; u.sh.pad = 0;
sh.level = 0; u.sh.type = SEG_JOURNAL;
sh.segno = cpu_to_be32(area->a_segno); u.sh.level = 0;
sh.ec = cpu_to_be32(area->a_erase_count); u.sh.segno = cpu_to_be32(area->a_segno);
sh.gec = cpu_to_be64(logfs_super(sb)->s_gec); u.sh.ec = cpu_to_be32(area->a_erase_count);
sh.crc = logfs_crc32(&sh, sizeof(sh), 4); 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. */ /* This causes a bug in segment.c. Not yet. */
//logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count, 0); //logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count, 0);
ofs = dev_ofs(sb, area->a_segno, 0); ofs = dev_ofs(sb, area->a_segno, 0);
area->a_used_bytes = ALIGN(sizeof(sh), 16); area->a_used_bytes = sizeof(u);
logfs_buf_write(area, ofs, &sh, sizeof(sh)); logfs_buf_write(area, ofs, &u, sizeof(u));
return 0; return 0;
} }
...@@ -494,6 +498,8 @@ static void account_shadows(struct super_block *sb) ...@@ -494,6 +498,8 @@ static void account_shadows(struct super_block *sb)
btree_grim_visitor64(&tree->new, (unsigned long)sb, account_shadow); btree_grim_visitor64(&tree->new, (unsigned long)sb, account_shadow);
btree_grim_visitor64(&tree->old, (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) { if (li->li_block) {
/* /*
...@@ -607,9 +613,9 @@ static size_t __logfs_write_je(struct super_block *sb, void *buf, u16 type, ...@@ -607,9 +613,9 @@ static size_t __logfs_write_je(struct super_block *sb, void *buf, u16 type,
if (len == 0) if (len == 0)
return logfs_write_header(super, header, 0, type); return logfs_write_header(super, header, 0, type);
BUG_ON(len > sb->s_blocksize);
compr_len = logfs_compress(buf, data, len, sb->s_blocksize); compr_len = logfs_compress(buf, data, len, sb->s_blocksize);
if (compr_len < 0 || type == JE_ANCHOR) { if (compr_len < 0 || type == JE_ANCHOR) {
BUG_ON(len > sb->s_blocksize);
memcpy(data, buf, len); memcpy(data, buf, len);
compr_len = len; compr_len = len;
compr = COMPR_NONE; compr = COMPR_NONE;
...@@ -661,6 +667,7 @@ static int logfs_write_je_buf(struct super_block *sb, void *buf, u16 type, ...@@ -661,6 +667,7 @@ static int logfs_write_je_buf(struct super_block *sb, void *buf, u16 type,
if (ofs < 0) if (ofs < 0)
return ofs; return ofs;
logfs_buf_write(area, ofs, super->s_compressed_je, len); 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); super->s_je_array[super->s_no_je++] = cpu_to_be64(ofs);
return 0; return 0;
} }
......
...@@ -257,10 +257,14 @@ struct logfs_shadow { ...@@ -257,10 +257,14 @@ struct logfs_shadow {
* struct shadow_tree * struct shadow_tree
* @new: shadows where old_ofs==0, indexed by new_ofs * @new: shadows where old_ofs==0, indexed by new_ofs
* @old: shadows where old_ofs!=0, indexed by old_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 shadow_tree {
struct btree_head64 new; struct btree_head64 new;
struct btree_head64 old; struct btree_head64 old;
struct btree_head32 segment_map;
int no_shadowed_segments;
}; };
struct object_alias_item { struct object_alias_item {
...@@ -305,13 +309,14 @@ typedef int write_alias_t(struct super_block *sb, u64 ino, u64 bix, ...@@ -305,13 +309,14 @@ typedef int write_alias_t(struct super_block *sb, u64 ino, u64 bix,
level_t level, int child_no, __be64 val); level_t level, int child_no, __be64 val);
struct logfs_block_ops { struct logfs_block_ops {
void (*write_block)(struct logfs_block *block); 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); void (*free_block)(struct super_block *sb, struct logfs_block*block);
int (*write_alias)(struct super_block *sb, int (*write_alias)(struct super_block *sb,
struct logfs_block *block, struct logfs_block *block,
write_alias_t *write_one_alias); write_alias_t *write_one_alias);
}; };
#define MAX_JOURNAL_ENTRIES 256
struct logfs_super { struct logfs_super {
struct mtd_info *s_mtd; /* underlying device */ struct mtd_info *s_mtd; /* underlying device */
struct block_device *s_bdev; /* underlying device */ struct block_device *s_bdev; /* underlying device */
...@@ -378,7 +383,7 @@ struct logfs_super { ...@@ -378,7 +383,7 @@ struct logfs_super {
u32 s_journal_ec[LOGFS_JOURNAL_SEGS]; /* journal erasecounts */ u32 s_journal_ec[LOGFS_JOURNAL_SEGS]; /* journal erasecounts */
u64 s_last_version; u64 s_last_version;
struct logfs_area *s_journal_area; /* open journal segment */ 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_no_je;
int s_sum_index; /* for the 12 summaries */ int s_sum_index; /* for the 12 summaries */
...@@ -722,4 +727,10 @@ static inline struct logfs_area *get_area(struct super_block *sb, ...@@ -722,4 +727,10 @@ static inline struct logfs_area *get_area(struct super_block *sb,
return logfs_super(sb)->s_area[(__force u8)gc_level]; 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 #endif
...@@ -430,25 +430,6 @@ static void inode_write_block(struct logfs_block *block) ...@@ -430,25 +430,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 * This silences a false, yet annoying gcc warning. I hate it when my editor
* jumps into bitops.h each time I recompile this file. * jumps into bitops.h each time I recompile this file.
...@@ -587,14 +568,12 @@ static void indirect_free_block(struct super_block *sb, ...@@ -587,14 +568,12 @@ static void indirect_free_block(struct super_block *sb,
static struct logfs_block_ops inode_block_ops = { static struct logfs_block_ops inode_block_ops = {
.write_block = inode_write_block, .write_block = inode_write_block,
.block_level = inode_block_level,
.free_block = inode_free_block, .free_block = inode_free_block,
.write_alias = inode_write_alias, .write_alias = inode_write_alias,
}; };
struct logfs_block_ops indirect_block_ops = { struct logfs_block_ops indirect_block_ops = {
.write_block = indirect_write_block, .write_block = indirect_write_block,
.block_level = indirect_block_level,
.free_block = indirect_free_block, .free_block = indirect_free_block,
.write_alias = indirect_write_alias, .write_alias = indirect_write_alias,
}; };
...@@ -1241,6 +1220,18 @@ static void free_shadow(struct inode *inode, struct logfs_shadow *shadow) ...@@ -1241,6 +1220,18 @@ static void free_shadow(struct inode *inode, struct logfs_shadow *shadow)
mempool_free(shadow, super->s_shadow_pool); 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 * fill_shadow_tree - Propagate shadow tree changes due to a write
* @inode: Inode owning the page * @inode: Inode owning the page
...@@ -1288,6 +1279,8 @@ static void fill_shadow_tree(struct inode *inode, struct page *page, ...@@ -1288,6 +1279,8 @@ static void fill_shadow_tree(struct inode *inode, struct page *page,
super->s_dirty_used_bytes += shadow->new_len; super->s_dirty_used_bytes += shadow->new_len;
super->s_dirty_free_bytes += shadow->old_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);
} }
} }
...@@ -1845,19 +1838,37 @@ static int __logfs_truncate(struct inode *inode, u64 size) ...@@ -1845,19 +1838,37 @@ static int __logfs_truncate(struct inode *inode, u64 size)
return logfs_truncate_direct(inode, 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; struct super_block *sb = inode->i_sb;
int err; u64 size = i_size_read(inode);
int err = 0;
logfs_get_wblocks(sb, NULL, 1); size = ALIGN(size, TRUNCATE_STEP);
err = __logfs_truncate(inode, size); while (size > target) {
if (!err) if (size > TRUNCATE_STEP)
err = __logfs_write_inode(inode, 0); size -= TRUNCATE_STEP;
logfs_put_wblocks(sb, NULL, 1); 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) if (!err)
err = vmtruncate(inode, size); err = vmtruncate(inode, target);
/* I don't trust error recovery yet. */ /* I don't trust error recovery yet. */
WARN_ON(err); WARN_ON(err);
...@@ -2251,8 +2262,6 @@ void logfs_cleanup_rw(struct super_block *sb) ...@@ -2251,8 +2262,6 @@ void logfs_cleanup_rw(struct super_block *sb)
struct logfs_super *super = logfs_super(sb); struct logfs_super *super = logfs_super(sb);
destroy_meta_inode(super->s_segfile_inode); destroy_meta_inode(super->s_segfile_inode);
if (super->s_block_pool) logfs_mempool_destroy(super->s_block_pool);
mempool_destroy(super->s_block_pool); logfs_mempool_destroy(super->s_shadow_pool);
if (super->s_shadow_pool)
mempool_destroy(super->s_shadow_pool);
} }
...@@ -183,14 +183,8 @@ static int btree_write_alias(struct super_block *sb, struct logfs_block *block, ...@@ -183,14 +183,8 @@ static int btree_write_alias(struct super_block *sb, struct logfs_block *block,
return 0; 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 = { static struct logfs_block_ops btree_block_ops = {
.write_block = btree_write_block, .write_block = btree_write_block,
.block_level = btree_block_level,
.free_block = __free_block, .free_block = __free_block,
.write_alias = btree_write_alias, .write_alias = btree_write_alias,
}; };
...@@ -919,7 +913,7 @@ int logfs_init_areas(struct super_block *sb) ...@@ -919,7 +913,7 @@ int logfs_init_areas(struct super_block *sb)
for (i--; i >= 0; i--) for (i--; i >= 0; i--)
free_area(super->s_area[i]); free_area(super->s_area[i]);
free_area(super->s_journal_area); free_area(super->s_journal_area);
mempool_destroy(super->s_alias_pool); logfs_mempool_destroy(super->s_alias_pool);
return -ENOMEM; return -ENOMEM;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "logfs.h" #include "logfs.h"
#include <linux/bio.h> #include <linux/bio.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/statfs.h> #include <linux/statfs.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
...@@ -137,6 +138,10 @@ static int logfs_sb_set(struct super_block *sb, void *_super) ...@@ -137,6 +138,10 @@ static int logfs_sb_set(struct super_block *sb, void *_super)
sb->s_fs_info = super; sb->s_fs_info = super;
sb->s_mtd = super->s_mtd; sb->s_mtd = super->s_mtd;
sb->s_bdev = super->s_bdev; 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; return 0;
} }
...@@ -452,6 +457,8 @@ static int logfs_read_sb(struct super_block *sb, int read_only) ...@@ -452,6 +457,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.new, super->s_btree_pool);
btree_init_mempool64(&super->s_shadow_tree.old, 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); ret = logfs_init_mapping(sb);
if (ret) if (ret)
...@@ -516,8 +523,8 @@ static void logfs_kill_sb(struct super_block *sb) ...@@ -516,8 +523,8 @@ static void logfs_kill_sb(struct super_block *sb)
if (super->s_erase_page) if (super->s_erase_page)
__free_page(super->s_erase_page); __free_page(super->s_erase_page);
super->s_devops->put_device(sb); super->s_devops->put_device(sb);
mempool_destroy(super->s_btree_pool); logfs_mempool_destroy(super->s_btree_pool);
mempool_destroy(super->s_alias_pool); logfs_mempool_destroy(super->s_alias_pool);
kfree(super); kfree(super);
log_super("LogFS: Finished unmounting\n"); log_super("LogFS: Finished unmounting\n");
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment