1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
/*
* readwrite.c
*
* Copyright (c) 2007-2008 Joern Engel <joern@logfs.org>
*
* License: GPL version 2
*/
#include <asm/types.h>
#include <errno.h>
#include "btree.h"
#include "kerncompat.h"
#include "logfs_abi.h"
#include "logfs.h"
static unsigned long __get_bits(u64 val, int skip, int no)
{
u64 ret = val;
ret >>= skip * no;
ret <<= 64 - no;
ret >>= 64 - no;
return ret;
}
static unsigned long get_bits(struct super_block *sb, u64 bix, u8 level)
{
return __get_bits(bix, level, sb->blocksize_bits - 3);
}
static inline int child_no(struct super_block *sb, u64 bix)
{
return bix & ((sb->blocksize / sizeof(__be64)) - 1);
}
struct inode *find_or_create_inode(struct super_block *sb, u64 ino)
{
struct inode *inode;
int err;
inode = btree_lookup64(&sb->ino_tree, ino);
if (!inode) {
inode = zalloc(sizeof(*inode) + sb->blocksize);
if (!inode)
return NULL;
err = btree_insert64(&sb->ino_tree, ino, inode);
if (err)
return NULL;
}
return inode;
}
static __be64 *find_or_create_block(struct super_block *sb, struct inode *inode,
u64 bix, u8 level)
{
__be64 *block;
struct btree_head64 *tree = &inode->block_tree[level];
int err;
block = btree_lookup64(tree, bix);
if (!block) {
block = zalloc(sb->blocksize);
if (!block)
return NULL;
err = btree_insert64(tree, bix, block);
if (err)
return NULL;
}
return block;
}
static int write_direct(struct super_block *sb, struct inode *inode, u64 ino,
u64 bix, u8 type, void *buf)
{
s64 ofs;
ofs = logfs_segment_write(sb, buf, type, ino, bix, 0);
if (ofs < 0)
return ofs;
inode->di.di_data[bix] = cpu_to_be64(ofs);
return 0;
}
static u64 bixmask(struct super_block *sb, u8 level)
{
if (level == 0)
return 0;
return (1 << ((sb->blocksize_bits - 3) * level)) - 1;
}
static int write_loop(struct super_block *sb, struct inode *inode, u64 ino,
u64 bix, u8 level, u8 type, void *buf)
{
u64 parent_bix;
__be64 *iblock;
s64 ofs;
parent_bix = bix | bixmask(sb, level + 1);
iblock = find_or_create_block(sb, inode, parent_bix, level + 1);
if (!iblock)
return -ENOMEM;
ofs = logfs_segment_write(sb, buf, type, ino, bix, level);
if (ofs < 0)
return ofs;
iblock[get_bits(sb, bix, level)] = cpu_to_be64(ofs);
return 0;
}
static inline u64 maxbix(u8 height)
{
return 1ULL << (LOGFS_BLOCK_BITS * height);
}
static void grow_inode(struct inode *inode, u64 bix, u8 level)
{
if (level != 0)
return;
while (bix > maxbix(inode->di.di_height))
inode->di.di_height++;
}
int logfs_file_write(struct super_block *sb, u64 ino, u64 bix, u8 level,
u8 type, void *buf)
{
struct inode *inode;
inode = find_or_create_inode(sb, ino);
if (!inode)
return -ENOMEM;
if (level == 0 && bix < I0_BLOCKS)
return write_direct(sb, inode, ino, bix, type, buf);
grow_inode(inode, bix, level);
return write_loop(sb, inode, ino, bix, level, type, buf);
}
int logfs_file_flush(struct super_block *sb, u64 ino)
{
struct btree_head64 *tree;
struct inode *inode;
__be64 *iblock;
s64 ofs;
u64 bix;
u8 level;
int err;
inode = find_or_create_inode(sb, ino);
BUG_ON(!inode);
if (inode->di.di_height == 0)
return 0;
for (level = 1; level < inode->di.di_height; level++) {
tree = &inode->block_tree[level];
for (;;) {
bix = btree_last64(tree);
iblock = btree_remove64(tree, bix);
if (!iblock)
break;
err = logfs_file_write(sb, ino, bix, level, OBJ_BLOCK,
iblock);
if (err)
return err;
free(iblock);
}
}
BUG_ON(level != inode->di.di_height);
tree = &inode->block_tree[level];
bix = btree_last64(tree);
iblock = btree_remove64(tree, bix);
BUG_ON(!iblock);
ofs = logfs_segment_write(sb, iblock, OBJ_BLOCK, ino, bix, level);
if (ofs < 0)
return ofs;
inode->di.di_data[INDIRECT_INDEX] = cpu_to_be64(ofs);
return 0;
}
|