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
This commit is contained in:
commit
4660d3d240
@ -80,6 +80,7 @@ static void writeseg_end_io(struct bio *bio, int err)
|
||||
prefetchw(&bvec->bv_page->flags);
|
||||
|
||||
end_page_writeback(page);
|
||||
page_cache_release(page);
|
||||
} while (bvec >= bio->bi_io_vec);
|
||||
bio_put(bio);
|
||||
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,
|
||||
unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
|
||||
int i;
|
||||
|
||||
if (max_pages > BIO_MAX_PAGES)
|
||||
max_pages = BIO_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++) {
|
||||
if (i >= max_pages) {
|
||||
@ -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);
|
||||
int i;
|
||||
|
||||
if (max_pages > BIO_MAX_PAGES)
|
||||
max_pages = BIO_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++) {
|
||||
if (i >= max_pages) {
|
||||
|
@ -303,12 +303,12 @@ static int __logfs_readdir(struct file *file, void *buf, filldir_t filldir)
|
||||
(filler_t *)logfs_readpage, NULL);
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
dd = kmap_atomic(page, KM_USER0);
|
||||
dd = kmap(page);
|
||||
BUG_ON(dd->namelen == 0);
|
||||
|
||||
full = filldir(buf, (char *)dd->name, be16_to_cpu(dd->namelen),
|
||||
pos, be64_to_cpu(dd->ino), dd->type);
|
||||
kunmap_atomic(dd, KM_USER0);
|
||||
kunmap(page);
|
||||
page_cache_release(page);
|
||||
if (full)
|
||||
break;
|
||||
|
@ -800,6 +800,7 @@ void do_logfs_journal_wl_pass(struct super_block *sb)
|
||||
{
|
||||
struct logfs_super *super = logfs_super(sb);
|
||||
struct logfs_area *area = super->s_journal_area;
|
||||
struct btree_head32 *head = &super->s_reserved_segments;
|
||||
u32 segno, ec;
|
||||
int i, err;
|
||||
|
||||
@ -807,6 +808,7 @@ void do_logfs_journal_wl_pass(struct super_block *sb)
|
||||
/* Drop old segments */
|
||||
journal_for_each(i)
|
||||
if (super->s_journal_seg[i]) {
|
||||
btree_remove32(head, super->s_journal_seg[i]);
|
||||
logfs_set_segment_unreserved(sb,
|
||||
super->s_journal_seg[i],
|
||||
super->s_journal_ec[i]);
|
||||
@ -819,8 +821,13 @@ void do_logfs_journal_wl_pass(struct super_block *sb)
|
||||
super->s_journal_seg[i] = segno;
|
||||
super->s_journal_ec[i] = ec;
|
||||
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 */
|
||||
freeseg(sb, area->a_segno);
|
||||
area->a_segno = super->s_journal_seg[0];
|
||||
area->a_is_open = 0;
|
||||
area->a_used_bytes = 0;
|
||||
|
@ -587,6 +587,7 @@ void move_page_to_btree(struct page *page);
|
||||
int logfs_init_mapping(struct super_block *sb);
|
||||
void logfs_sync_area(struct logfs_area *area);
|
||||
void logfs_sync_segments(struct super_block *sb);
|
||||
void freeseg(struct super_block *sb, u32 segno);
|
||||
|
||||
/* area handling */
|
||||
int logfs_init_areas(struct super_block *sb);
|
||||
|
@ -1594,7 +1594,6 @@ int logfs_delete(struct inode *inode, pgoff_t index,
|
||||
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,
|
||||
gc_level_t gc_level, long flags)
|
||||
{
|
||||
@ -1611,6 +1610,18 @@ int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs,
|
||||
if (level != 0)
|
||||
alloc_indirect_block(inode, page, 0);
|
||||
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);
|
||||
return err;
|
||||
|
@ -93,49 +93,57 @@ void __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len,
|
||||
} while (len);
|
||||
}
|
||||
|
||||
/*
|
||||
* bdev_writeseg will write full pages. Memset the tail to prevent data leaks.
|
||||
*/
|
||||
static void pad_wbuf(struct logfs_area *area, int final)
|
||||
static void pad_partial_page(struct logfs_area *area)
|
||||
{
|
||||
struct super_block *sb = area->a_sb;
|
||||
struct logfs_super *super = logfs_super(sb);
|
||||
struct page *page;
|
||||
u64 ofs = dev_ofs(sb, area->a_segno, area->a_used_bytes);
|
||||
pgoff_t index = ofs >> PAGE_SHIFT;
|
||||
long offset = ofs & (PAGE_SIZE-1);
|
||||
u32 len = PAGE_SIZE - offset;
|
||||
|
||||
if (len == PAGE_SIZE) {
|
||||
/* The math in this function can surely use some love */
|
||||
len = 0;
|
||||
}
|
||||
if (len) {
|
||||
BUG_ON(area->a_used_bytes >= super->s_segsize);
|
||||
|
||||
page = get_mapping_page(area->a_sb, index, 0);
|
||||
if (len % PAGE_SIZE) {
|
||||
page = get_mapping_page(sb, index, 0);
|
||||
BUG_ON(!page); /* FIXME: reserve a pool */
|
||||
memset(page_address(page) + offset, 0xff, len);
|
||||
SetPagePrivate(page);
|
||||
page_cache_release(page);
|
||||
}
|
||||
}
|
||||
|
||||
if (!final)
|
||||
return;
|
||||
static void pad_full_pages(struct logfs_area *area)
|
||||
{
|
||||
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;
|
||||
for ( ; area->a_used_bytes < super->s_segsize;
|
||||
area->a_used_bytes += PAGE_SIZE) {
|
||||
/* Memset another page */
|
||||
index++;
|
||||
page = get_mapping_page(area->a_sb, index, 0);
|
||||
while (no_indizes) {
|
||||
page = get_mapping_page(sb, index, 0);
|
||||
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);
|
||||
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,
|
||||
* 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)
|
||||
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 address_space *mapping = super->s_mapping_inode->i_mapping;
|
||||
|
@ -277,7 +277,7 @@ static int logfs_recover_sb(struct super_block *sb)
|
||||
}
|
||||
if (valid0 && valid1 && ds_cmp(ds0, ds1)) {
|
||||
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
|
||||
* check them before?!? */
|
||||
@ -289,6 +289,10 @@ static int logfs_make_writeable(struct super_block *sb)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = logfs_open_segfile(sb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Repair any broken superblock copies */
|
||||
err = logfs_recover_sb(sb);
|
||||
if (err)
|
||||
@ -299,10 +303,6 @@ static int logfs_make_writeable(struct super_block *sb)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = logfs_open_segfile(sb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Do one GC pass before any data gets dirtied */
|
||||
logfs_gc_pass(sb);
|
||||
|
||||
@ -328,7 +328,7 @@ static int logfs_get_sb_final(struct super_block *sb, struct vfsmount *mnt)
|
||||
|
||||
sb->s_root = d_alloc_root(rootdir);
|
||||
if (!sb->s_root)
|
||||
goto fail;
|
||||
goto fail2;
|
||||
|
||||
super->s_erase_page = alloc_pages(GFP_KERNEL, 0);
|
||||
if (!super->s_erase_page)
|
||||
@ -572,8 +572,7 @@ int logfs_get_sb_device(struct file_system_type *type, int flags,
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
up_write(&sb->s_umount);
|
||||
deactivate_super(sb);
|
||||
deactivate_locked_super(sb);
|
||||
return err;
|
||||
err0:
|
||||
kfree(super);
|
||||
|
Loading…
Reference in New Issue
Block a user