Commit 4660d3d2 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] Erase new journal segments
  [LogFS] Move reserved segments with journal
  [LogFS] Clear PagePrivate when moving journal
  Simplify and fix pad_wbuf
  Prevent data corruption in logfs_rewrite_block()
  Use deactivate_locked_super
  Fix logfs_get_sb_final error path
  Write out both superblocks on mismatch
  Prevent schedule while atomic in __logfs_readdir
  Plug memory leak in writeseg_end_io
  Limit max_pages for insane devices
  Open segment file before using it
parents be3fd3cc 6be7fa06
...@@ -80,6 +80,7 @@ static void writeseg_end_io(struct bio *bio, int err) ...@@ -80,6 +80,7 @@ static void writeseg_end_io(struct bio *bio, int err)
prefetchw(&bvec->bv_page->flags); prefetchw(&bvec->bv_page->flags);
end_page_writeback(page); end_page_writeback(page);
page_cache_release(page);
} while (bvec >= bio->bi_io_vec); } while (bvec >= bio->bi_io_vec);
bio_put(bio); bio_put(bio);
if (atomic_dec_and_test(&super->s_pending_writes)) if (atomic_dec_and_test(&super->s_pending_writes))
...@@ -97,8 +98,10 @@ static int __bdev_writeseg(struct super_block *sb, u64 ofs, pgoff_t index, ...@@ -97,8 +98,10 @@ static int __bdev_writeseg(struct super_block *sb, u64 ofs, pgoff_t index,
unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9); unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
int i; int i;
if (max_pages > BIO_MAX_PAGES)
max_pages = BIO_MAX_PAGES;
bio = bio_alloc(GFP_NOFS, max_pages); bio = bio_alloc(GFP_NOFS, max_pages);
BUG_ON(!bio); /* FIXME: handle this */ BUG_ON(!bio);
for (i = 0; i < nr_pages; i++) { for (i = 0; i < nr_pages; i++) {
if (i >= max_pages) { if (i >= max_pages) {
...@@ -191,8 +194,10 @@ static int do_erase(struct super_block *sb, u64 ofs, pgoff_t index, ...@@ -191,8 +194,10 @@ static int do_erase(struct super_block *sb, u64 ofs, pgoff_t index,
unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9); unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
int i; int i;
if (max_pages > BIO_MAX_PAGES)
max_pages = BIO_MAX_PAGES;
bio = bio_alloc(GFP_NOFS, max_pages); bio = bio_alloc(GFP_NOFS, max_pages);
BUG_ON(!bio); /* FIXME: handle this */ BUG_ON(!bio);
for (i = 0; i < nr_pages; i++) { for (i = 0; i < nr_pages; i++) {
if (i >= max_pages) { if (i >= max_pages) {
......
...@@ -303,12 +303,12 @@ static int __logfs_readdir(struct file *file, void *buf, filldir_t filldir) ...@@ -303,12 +303,12 @@ static int __logfs_readdir(struct file *file, void *buf, filldir_t filldir)
(filler_t *)logfs_readpage, NULL); (filler_t *)logfs_readpage, NULL);
if (IS_ERR(page)) if (IS_ERR(page))
return PTR_ERR(page); return PTR_ERR(page);
dd = kmap_atomic(page, KM_USER0); dd = kmap(page);
BUG_ON(dd->namelen == 0); BUG_ON(dd->namelen == 0);
full = filldir(buf, (char *)dd->name, be16_to_cpu(dd->namelen), full = filldir(buf, (char *)dd->name, be16_to_cpu(dd->namelen),
pos, be64_to_cpu(dd->ino), dd->type); pos, be64_to_cpu(dd->ino), dd->type);
kunmap_atomic(dd, KM_USER0); kunmap(page);
page_cache_release(page); page_cache_release(page);
if (full) if (full)
break; break;
......
...@@ -800,6 +800,7 @@ void do_logfs_journal_wl_pass(struct super_block *sb) ...@@ -800,6 +800,7 @@ void do_logfs_journal_wl_pass(struct super_block *sb)
{ {
struct logfs_super *super = logfs_super(sb); struct logfs_super *super = logfs_super(sb);
struct logfs_area *area = super->s_journal_area; struct logfs_area *area = super->s_journal_area;
struct btree_head32 *head = &super->s_reserved_segments;
u32 segno, ec; u32 segno, ec;
int i, err; int i, err;
...@@ -807,6 +808,7 @@ void do_logfs_journal_wl_pass(struct super_block *sb) ...@@ -807,6 +808,7 @@ void do_logfs_journal_wl_pass(struct super_block *sb)
/* Drop old segments */ /* Drop old segments */
journal_for_each(i) journal_for_each(i)
if (super->s_journal_seg[i]) { if (super->s_journal_seg[i]) {
btree_remove32(head, super->s_journal_seg[i]);
logfs_set_segment_unreserved(sb, logfs_set_segment_unreserved(sb,
super->s_journal_seg[i], super->s_journal_seg[i],
super->s_journal_ec[i]); super->s_journal_ec[i]);
...@@ -819,8 +821,13 @@ void do_logfs_journal_wl_pass(struct super_block *sb) ...@@ -819,8 +821,13 @@ void do_logfs_journal_wl_pass(struct super_block *sb)
super->s_journal_seg[i] = segno; super->s_journal_seg[i] = segno;
super->s_journal_ec[i] = ec; super->s_journal_ec[i] = ec;
logfs_set_segment_reserved(sb, segno); logfs_set_segment_reserved(sb, segno);
err = btree_insert32(head, segno, (void *)1, GFP_KERNEL);
BUG_ON(err); /* mempool should prevent this */
err = logfs_erase_segment(sb, segno, 1);
BUG_ON(err); /* FIXME: remount-ro would be nicer */
} }
/* Manually move journal_area */ /* Manually move journal_area */
freeseg(sb, area->a_segno);
area->a_segno = super->s_journal_seg[0]; area->a_segno = super->s_journal_seg[0];
area->a_is_open = 0; area->a_is_open = 0;
area->a_used_bytes = 0; area->a_used_bytes = 0;
......
...@@ -587,6 +587,7 @@ void move_page_to_btree(struct page *page); ...@@ -587,6 +587,7 @@ void move_page_to_btree(struct page *page);
int logfs_init_mapping(struct super_block *sb); int logfs_init_mapping(struct super_block *sb);
void logfs_sync_area(struct logfs_area *area); void logfs_sync_area(struct logfs_area *area);
void logfs_sync_segments(struct super_block *sb); void logfs_sync_segments(struct super_block *sb);
void freeseg(struct super_block *sb, u32 segno);
/* area handling */ /* area handling */
int logfs_init_areas(struct super_block *sb); int logfs_init_areas(struct super_block *sb);
......
...@@ -1594,7 +1594,6 @@ int logfs_delete(struct inode *inode, pgoff_t index, ...@@ -1594,7 +1594,6 @@ int logfs_delete(struct inode *inode, pgoff_t index,
return ret; return ret;
} }
/* Rewrite cannot mark the inode dirty but has to write it immediatly. */
int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs, int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs,
gc_level_t gc_level, long flags) gc_level_t gc_level, long flags)
{ {
...@@ -1611,6 +1610,18 @@ int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs, ...@@ -1611,6 +1610,18 @@ int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs,
if (level != 0) if (level != 0)
alloc_indirect_block(inode, page, 0); alloc_indirect_block(inode, page, 0);
err = logfs_write_buf(inode, page, flags); err = logfs_write_buf(inode, page, flags);
if (!err && shrink_level(gc_level) == 0) {
/* Rewrite cannot mark the inode dirty but has to
* write it immediatly.
* Q: Can't we just create an alias for the inode
* instead? And if not, why not?
*/
if (inode->i_ino == LOGFS_INO_MASTER)
logfs_write_anchor(inode->i_sb);
else {
err = __logfs_write_inode(inode, flags);
}
}
} }
logfs_put_write_page(page); logfs_put_write_page(page);
return err; return err;
......
...@@ -93,49 +93,57 @@ void __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len, ...@@ -93,49 +93,57 @@ void __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len,
} while (len); } while (len);
} }
/* static void pad_partial_page(struct logfs_area *area)
* bdev_writeseg will write full pages. Memset the tail to prevent data leaks.
*/
static void pad_wbuf(struct logfs_area *area, int final)
{ {
struct super_block *sb = area->a_sb; struct super_block *sb = area->a_sb;
struct logfs_super *super = logfs_super(sb);
struct page *page; struct page *page;
u64 ofs = dev_ofs(sb, area->a_segno, area->a_used_bytes); u64 ofs = dev_ofs(sb, area->a_segno, area->a_used_bytes);
pgoff_t index = ofs >> PAGE_SHIFT; pgoff_t index = ofs >> PAGE_SHIFT;
long offset = ofs & (PAGE_SIZE-1); long offset = ofs & (PAGE_SIZE-1);
u32 len = PAGE_SIZE - offset; u32 len = PAGE_SIZE - offset;
if (len == PAGE_SIZE) { if (len % PAGE_SIZE) {
/* The math in this function can surely use some love */ page = get_mapping_page(sb, index, 0);
len = 0;
}
if (len) {
BUG_ON(area->a_used_bytes >= super->s_segsize);
page = get_mapping_page(area->a_sb, index, 0);
BUG_ON(!page); /* FIXME: reserve a pool */ BUG_ON(!page); /* FIXME: reserve a pool */
memset(page_address(page) + offset, 0xff, len); memset(page_address(page) + offset, 0xff, len);
SetPagePrivate(page); SetPagePrivate(page);
page_cache_release(page); page_cache_release(page);
} }
}
if (!final) static void pad_full_pages(struct logfs_area *area)
return; {
struct super_block *sb = area->a_sb;
struct logfs_super *super = logfs_super(sb);
u64 ofs = dev_ofs(sb, area->a_segno, area->a_used_bytes);
u32 len = super->s_segsize - area->a_used_bytes;
pgoff_t index = PAGE_CACHE_ALIGN(ofs) >> PAGE_CACHE_SHIFT;
pgoff_t no_indizes = len >> PAGE_CACHE_SHIFT;
struct page *page;
area->a_used_bytes += len; while (no_indizes) {
for ( ; area->a_used_bytes < super->s_segsize; page = get_mapping_page(sb, index, 0);
area->a_used_bytes += PAGE_SIZE) {
/* Memset another page */
index++;
page = get_mapping_page(area->a_sb, index, 0);
BUG_ON(!page); /* FIXME: reserve a pool */ BUG_ON(!page); /* FIXME: reserve a pool */
memset(page_address(page), 0xff, PAGE_SIZE); SetPageUptodate(page);
memset(page_address(page), 0xff, PAGE_CACHE_SIZE);
SetPagePrivate(page); SetPagePrivate(page);
page_cache_release(page); page_cache_release(page);
index++;
no_indizes--;
} }
} }
/*
* bdev_writeseg will write full pages. Memset the tail to prevent data leaks.
* Also make sure we allocate (and memset) all pages for final writeout.
*/
static void pad_wbuf(struct logfs_area *area, int final)
{
pad_partial_page(area);
if (final)
pad_full_pages(area);
}
/* /*
* We have to be careful with the alias tree. Since lookup is done by bix, * We have to be careful with the alias tree. Since lookup is done by bix,
* it needs to be normalized, so 14, 15, 16, etc. all match when dealing with * it needs to be normalized, so 14, 15, 16, etc. all match when dealing with
...@@ -683,7 +691,7 @@ int logfs_segment_delete(struct inode *inode, struct logfs_shadow *shadow) ...@@ -683,7 +691,7 @@ int logfs_segment_delete(struct inode *inode, struct logfs_shadow *shadow)
return 0; return 0;
} }
static void freeseg(struct super_block *sb, u32 segno) void freeseg(struct super_block *sb, u32 segno)
{ {
struct logfs_super *super = logfs_super(sb); struct logfs_super *super = logfs_super(sb);
struct address_space *mapping = super->s_mapping_inode->i_mapping; struct address_space *mapping = super->s_mapping_inode->i_mapping;
......
...@@ -277,7 +277,7 @@ static int logfs_recover_sb(struct super_block *sb) ...@@ -277,7 +277,7 @@ static int logfs_recover_sb(struct super_block *sb)
} }
if (valid0 && valid1 && ds_cmp(ds0, ds1)) { if (valid0 && valid1 && ds_cmp(ds0, ds1)) {
printk(KERN_INFO"Superblocks don't match - fixing.\n"); printk(KERN_INFO"Superblocks don't match - fixing.\n");
return write_one_sb(sb, super->s_devops->find_last_sb); return logfs_write_sb(sb);
} }
/* If neither is valid now, something's wrong. Didn't we properly /* If neither is valid now, something's wrong. Didn't we properly
* check them before?!? */ * check them before?!? */
...@@ -289,6 +289,10 @@ static int logfs_make_writeable(struct super_block *sb) ...@@ -289,6 +289,10 @@ static int logfs_make_writeable(struct super_block *sb)
{ {
int err; int err;
err = logfs_open_segfile(sb);
if (err)
return err;
/* Repair any broken superblock copies */ /* Repair any broken superblock copies */
err = logfs_recover_sb(sb); err = logfs_recover_sb(sb);
if (err) if (err)
...@@ -299,10 +303,6 @@ static int logfs_make_writeable(struct super_block *sb) ...@@ -299,10 +303,6 @@ static int logfs_make_writeable(struct super_block *sb)
if (err) if (err)
return err; return err;
err = logfs_open_segfile(sb);
if (err)
return err;
/* Do one GC pass before any data gets dirtied */ /* Do one GC pass before any data gets dirtied */
logfs_gc_pass(sb); logfs_gc_pass(sb);
...@@ -328,7 +328,7 @@ static int logfs_get_sb_final(struct super_block *sb, struct vfsmount *mnt) ...@@ -328,7 +328,7 @@ static int logfs_get_sb_final(struct super_block *sb, struct vfsmount *mnt)
sb->s_root = d_alloc_root(rootdir); sb->s_root = d_alloc_root(rootdir);
if (!sb->s_root) if (!sb->s_root)
goto fail; goto fail2;
super->s_erase_page = alloc_pages(GFP_KERNEL, 0); super->s_erase_page = alloc_pages(GFP_KERNEL, 0);
if (!super->s_erase_page) if (!super->s_erase_page)
...@@ -572,8 +572,7 @@ int logfs_get_sb_device(struct file_system_type *type, int flags, ...@@ -572,8 +572,7 @@ int logfs_get_sb_device(struct file_system_type *type, int flags,
return 0; return 0;
err1: err1:
up_write(&sb->s_umount); deactivate_locked_super(sb);
deactivate_super(sb);
return err; return err;
err0: err0:
kfree(super); kfree(super);
......
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