/*
* fs/logfs/journal.c - journal handling code
*
* As should be obvious for Linux kernel code, license is GPLv2
*
* Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
*/
#include "logfs.h"
#include <linux/slab.h>
static void logfs_calc_free(struct super_block *sb)
{
struct logfs_super *super = logfs_super(sb);
u64 reserve, no_segs = super->s_no_segs;
s64 free;
int i;
/* superblock segments */
no_segs -= 2;
super->s_no_journal_segs = 0;
/* journal */
journal_for_each(i)
if (super->s_journal_seg[i]) {
no_segs--;
super->s_no_journal_segs++;
}
/* open segments plus one extra per level for GC */
no_segs -= 2 * super->s_total_levels;
free = no_segs * (super->s_segsize - LOGFS_SEGMENT_RESERVE);
free -= super->s_used_bytes;
/* just a bit extra */
free -= super->s_total_levels * 4096;
/* Bad blocks are 'paid' for with speed reserve - the filesystem
* simply gets slower as bad blocks accumulate. Until the bad blocks
* exceed the speed reserve - then the filesystem gets smaller.
*/
reserve = super->s_bad_segments + super->s_bad_seg_reserve;
reserve *= super->s_segsize - LOGFS_SEGMENT_RESERVE;
reserve = max(reserve, super->s_speed_reserve);
free -= reserve;
if (free < 0)
free = 0;
super->s_free_bytes = free;
}
static void reserve_sb_and_journal(struct super_block *sb)
{
struct logfs_super *super = logfs_super(sb);
struct btree_head32 *head = &super->s_reserved_segments;
int i, err;
err = btree_insert32(head, seg_no(sb, super->s_sb_ofs[0]), (void *)1,
GFP_KERNEL);
BUG_ON(err);
err = btree_insert32(head, seg_no(sb, super->s_sb_ofs[1]), (void *)1,
GFP_KERNEL);
BUG_ON(err);
journal_for_each(i) {
if (!super->s_journal_seg[i])
continue;
err = btree_insert32(head, super->s_journal_seg[i], (void *)1,
GFP_KERNEL);
BUG_ON(err);
}
}
static void read_dynsb(struct super_block *sb,
struct logfs_je_dynsb *dynsb)
{
struct logfs_super *super = logfs_super(sb);
super->s_gec = be64_to_cpu(dynsb->ds_gec);
super->s_sweeper = be64_to_cpu(dynsb->ds_sweeper);
super->s_victim_ino = be64_to_cpu(dynsb->ds_victim_ino);
super->s_rename_dir = be64_to_cpu(dynsb->ds_rename_dir);
super->s_rename_pos = be64_to_cpu(dynsb->ds_rename_pos);
super->s_used_bytes = be64_to_cpu(dynsb->ds_used_bytes);
super->s_generation = be32_to_cpu(dynsb->ds_generation);
}
static void read_anchor(struct super_block *sb,
struct logfs_je_anchor *da)
{
struct logfs_super *super = logfs_super(sb);
struct inode *inode = super->s_master_inode;
struct logfs_inode *li = logfs_inode(inode);
int i;
super->s_last_ino = be64_to_cpu(da->da_last_ino);
li->li_flags = 0;
li->li_height = da->da_height;
i_size_write(inode, be64_to_cpu(da->da_size));
li->li_used_bytes = be64_to_cpu(da->da_used_bytes);
for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
li->li_data[i] = be64_to_cpu(da->da_data[i]);
}
static void read_erasecount(struct super_block *sb,
struct logfs_je_journal_ec *ec)
{
struct logfs_super *super = logfs_super(sb);
int i;
journal_for_each(i)
super->s_journal_ec[i] = be32_to_cpu(ec->ec[i]);
}
static int read_area(struct super_block *sb, struct logfs_je_area *a)
{
struct logfs_super *super = logfs_super(sb);
struct logfs_area *area = super->s_area[a->gc_level];
u64 ofs;
u32 writemask = ~(super->s_writesize - 1);
if (a->gc_level >= LOGFS_NO_AREAS)
return -EIO;
if (a->vim != VIM_DEFAULT)
return -EIO; /* TODO: close area and continue */
area->a_used_bytes = be32_to_cpu(a->used_bytes);
area->a_written_bytes = area->a_used_bytes & writemask;
area->a_segno = be32_to_cpu(a->segno);
if (area->a_segno)
area->a_is_open = 1;
ofs = dev_ofs(sb, area->a_segno, area->a_written_bytes);
if (super->s_writesize