aboutsummaryrefslogtreecommitdiff
path: root/fs/logfs/readwrite.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/logfs/readwrite.c')
-rw-r--r--fs/logfs/readwrite.c265
1 files changed, 153 insertions, 112 deletions
diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c
index bff40253dfb..48140315f62 100644
--- a/fs/logfs/readwrite.c
+++ b/fs/logfs/readwrite.c
@@ -119,14 +119,14 @@ static void logfs_disk_to_inode(struct logfs_disk_inode *di, struct inode*inode)
inode->i_mode = be16_to_cpu(di->di_mode);
li->li_height = di->di_height;
li->li_flags = be32_to_cpu(di->di_flags);
- inode->i_uid = be32_to_cpu(di->di_uid);
- inode->i_gid = be32_to_cpu(di->di_gid);
+ i_uid_write(inode, be32_to_cpu(di->di_uid));
+ i_gid_write(inode, be32_to_cpu(di->di_gid));
inode->i_size = be64_to_cpu(di->di_size);
logfs_set_blocks(inode, be64_to_cpu(di->di_used_bytes));
inode->i_atime = be64_to_timespec(di->di_atime);
inode->i_ctime = be64_to_timespec(di->di_ctime);
inode->i_mtime = be64_to_timespec(di->di_mtime);
- inode->i_nlink = be32_to_cpu(di->di_refcount);
+ set_nlink(inode, be32_to_cpu(di->di_refcount));
inode->i_generation = be32_to_cpu(di->di_generation);
switch (inode->i_mode & S_IFMT) {
@@ -156,8 +156,8 @@ static void logfs_inode_to_disk(struct inode *inode, struct logfs_disk_inode*di)
di->di_height = li->li_height;
di->di_pad = 0;
di->di_flags = cpu_to_be32(li->li_flags);
- di->di_uid = cpu_to_be32(inode->i_uid);
- di->di_gid = cpu_to_be32(inode->i_gid);
+ di->di_uid = cpu_to_be32(i_uid_read(inode));
+ di->di_gid = cpu_to_be32(i_gid_read(inode));
di->di_size = cpu_to_be64(i_size_read(inode));
di->di_used_bytes = cpu_to_be64(li->li_used_bytes);
di->di_atime = timespec_to_be64(inode->i_atime);
@@ -244,8 +244,7 @@ static void preunlock_page(struct super_block *sb, struct page *page, int lock)
* is waiting for s_write_mutex. We annotate this fact by setting PG_pre_locked
* in addition to PG_locked.
*/
-static void logfs_get_wblocks(struct super_block *sb, struct page *page,
- int lock)
+void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock)
{
struct logfs_super *super = logfs_super(sb);
@@ -260,8 +259,7 @@ static void logfs_get_wblocks(struct super_block *sb, struct page *page,
}
}
-static void logfs_put_wblocks(struct super_block *sb, struct page *page,
- int lock)
+void logfs_put_wblocks(struct super_block *sb, struct page *page, int lock)
{
struct logfs_super *super = logfs_super(sb);
@@ -424,31 +422,12 @@ static void inode_write_block(struct logfs_block *block)
if (inode->i_ino == LOGFS_INO_MASTER)
logfs_write_anchor(inode->i_sb);
else {
- ret = __logfs_write_inode(inode, 0);
+ ret = __logfs_write_inode(inode, NULL, 0);
/* see indirect_write_block comment */
BUG_ON(ret);
}
}
-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
* jumps into bitops.h each time I recompile this file.
@@ -500,7 +479,7 @@ static int inode_write_alias(struct super_block *sb,
val = inode_val0(inode);
break;
case INODE_USED_OFS:
- val = cpu_to_be64(li->li_used_bytes);;
+ val = cpu_to_be64(li->li_used_bytes);
break;
case INODE_SIZE_OFS:
val = cpu_to_be64(i_size_read(inode));
@@ -538,9 +517,9 @@ static int indirect_write_alias(struct super_block *sb,
ino = page->mapping->host->i_ino;
logfs_unpack_index(page->index, &bix, &level);
- child = kmap_atomic(page, KM_USER0);
+ child = kmap_atomic(page);
val = child[pos];
- kunmap_atomic(child, KM_USER0);
+ kunmap_atomic(child);
err = write_one_alias(sb, ino, bix, level, pos, val);
if (err)
return err;
@@ -579,22 +558,25 @@ static void inode_free_block(struct super_block *sb, struct logfs_block *block)
static void indirect_free_block(struct super_block *sb,
struct logfs_block *block)
{
- ClearPagePrivate(block->page);
- block->page->private = 0;
+ struct page *page = block->page;
+
+ if (PagePrivate(page)) {
+ ClearPagePrivate(page);
+ page_cache_release(page);
+ set_page_private(page, 0);
+ }
__free_block(sb, block);
}
static struct logfs_block_ops inode_block_ops = {
.write_block = inode_write_block,
- .block_level = inode_block_level,
.free_block = inode_free_block,
.write_alias = inode_write_alias,
};
struct logfs_block_ops indirect_block_ops = {
.write_block = indirect_write_block,
- .block_level = indirect_block_level,
.free_block = indirect_free_block,
.write_alias = indirect_write_alias,
};
@@ -671,8 +653,11 @@ static void alloc_data_block(struct inode *inode, struct page *page)
logfs_unpack_index(page->index, &bix, &level);
block = __alloc_block(inode->i_sb, inode->i_ino, bix, level);
block->page = page;
+
SetPagePrivate(page);
- page->private = (unsigned long)block;
+ page_cache_get(page);
+ set_page_private(page, (unsigned long) block);
+
block->ops = &indirect_block_ops;
}
@@ -688,9 +673,9 @@ static void alloc_indirect_block(struct inode *inode, struct page *page,
alloc_data_block(inode, page);
block = logfs_block(page);
- array = kmap_atomic(page, KM_USER0);
+ array = kmap_atomic(page);
initialize_block_counters(page, block, array, page_is_empty);
- kunmap_atomic(array, KM_USER0);
+ kunmap_atomic(array);
}
static void block_set_pointer(struct page *page, int index, u64 ptr)
@@ -700,10 +685,10 @@ static void block_set_pointer(struct page *page, int index, u64 ptr)
u64 oldptr;
BUG_ON(!block);
- array = kmap_atomic(page, KM_USER0);
+ array = kmap_atomic(page);
oldptr = be64_to_cpu(array[index]);
array[index] = cpu_to_be64(ptr);
- kunmap_atomic(array, KM_USER0);
+ kunmap_atomic(array);
SetPageUptodate(page);
block->full += !!(ptr & LOGFS_FULLY_POPULATED)
@@ -716,9 +701,9 @@ static u64 block_get_pointer(struct page *page, int index)
__be64 *block;
u64 ptr;
- block = kmap_atomic(page, KM_USER0);
+ block = kmap_atomic(page);
ptr = be64_to_cpu(block[index]);
- kunmap_atomic(block, KM_USER0);
+ kunmap_atomic(block);
return ptr;
}
@@ -865,7 +850,7 @@ static u64 seek_holedata_loop(struct inode *inode, u64 bix, int data)
}
slot = get_bits(bix, SUBLEVEL(level));
- rblock = kmap_atomic(page, KM_USER0);
+ rblock = kmap_atomic(page);
while (slot < LOGFS_BLOCK_FACTOR) {
if (data && (rblock[slot] != 0))
break;
@@ -876,12 +861,12 @@ static u64 seek_holedata_loop(struct inode *inode, u64 bix, int data)
bix &= ~(increment - 1);
}
if (slot >= LOGFS_BLOCK_FACTOR) {
- kunmap_atomic(rblock, KM_USER0);
+ kunmap_atomic(rblock);
logfs_put_read_page(page);
return bix;
}
bofs = be64_to_cpu(rblock[slot]);
- kunmap_atomic(rblock, KM_USER0);
+ kunmap_atomic(rblock);
logfs_put_read_page(page);
if (!bofs) {
BUG_ON(data);
@@ -913,6 +898,8 @@ u64 logfs_seek_hole(struct inode *inode, u64 bix)
return bix;
else if (li->li_data[INDIRECT_INDEX] & LOGFS_FULLY_POPULATED)
bix = maxbix(li->li_height);
+ else if (bix >= maxbix(li->li_height))
+ return bix;
else {
bix = seek_holedata_loop(inode, bix, 0);
if (bix < maxbix(li->li_height))
@@ -1114,17 +1101,25 @@ static int logfs_reserve_bytes(struct inode *inode, int bytes)
int get_page_reserve(struct inode *inode, struct page *page)
{
struct logfs_super *super = logfs_super(inode->i_sb);
+ struct logfs_block *block = logfs_block(page);
int ret;
- if (logfs_block(page) && logfs_block(page)->reserved_bytes)
+ if (block && block->reserved_bytes)
return 0;
logfs_get_wblocks(inode->i_sb, page, WF_LOCK);
- ret = logfs_reserve_bytes(inode, 6 * LOGFS_MAX_OBJECTSIZE);
+ while ((ret = logfs_reserve_bytes(inode, 6 * LOGFS_MAX_OBJECTSIZE)) &&
+ !list_empty(&super->s_writeback_list)) {
+ block = list_entry(super->s_writeback_list.next,
+ struct logfs_block, alias_list);
+ block->ops->write_block(block);
+ }
if (!ret) {
alloc_data_block(inode, page);
- logfs_block(page)->reserved_bytes += 6 * LOGFS_MAX_OBJECTSIZE;
+ block = logfs_block(page);
+ block->reserved_bytes += 6 * LOGFS_MAX_OBJECTSIZE;
super->s_dirty_pages += 6 * LOGFS_MAX_OBJECTSIZE;
+ list_move_tail(&block->alias_list, &super->s_writeback_list);
}
logfs_put_wblocks(inode->i_sb, page, WF_LOCK);
return ret;
@@ -1241,6 +1236,18 @@ static void free_shadow(struct inode *inode, struct logfs_shadow *shadow)
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
* @inode: Inode owning the page
@@ -1288,6 +1295,8 @@ static void fill_shadow_tree(struct inode *inode, struct page *page,
super->s_dirty_used_bytes += shadow->new_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);
}
}
@@ -1567,11 +1576,15 @@ int logfs_write_buf(struct inode *inode, struct page *page, long flags)
static int __logfs_delete(struct inode *inode, struct page *page)
{
long flags = WF_DELETE;
+ int err;
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
if (page->index < I0_BLOCKS)
return logfs_write_direct(inode, page, flags);
+ err = grow_inode(inode, page->index, 0);
+ if (err)
+ return err;
return logfs_write_rec(inode, page, page->index, 0, flags);
}
@@ -1613,14 +1626,14 @@ int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs,
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.
+ * write it immediately.
* 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);
+ err = __logfs_write_inode(inode, page, flags);
}
}
}
@@ -1845,20 +1858,44 @@ static int __logfs_truncate(struct inode *inode, u64 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;
- int err;
+ u64 size = i_size_read(inode);
+ int err = 0;
- logfs_get_wblocks(sb, NULL, 1);
- err = __logfs_truncate(inode, size);
- if (!err)
- err = __logfs_write_inode(inode, 0);
- logfs_put_wblocks(sb, NULL, 1);
+ size = ALIGN(size, TRUNCATE_STEP);
+ while (size > target) {
+ if (size > TRUNCATE_STEP)
+ size -= TRUNCATE_STEP;
+ else
+ size = 0;
+ if (size < target)
+ size = target;
+
+ logfs_get_wblocks(sb, NULL, 1);
+ err = __logfs_truncate(inode, size);
+ if (!err)
+ err = __logfs_write_inode(inode, NULL, 0);
+ logfs_put_wblocks(sb, NULL, 1);
+ }
- if (!err)
- err = vmtruncate(inode, size);
+ if (!err) {
+ err = inode_newsize_ok(inode, target);
+ if (err)
+ goto out;
+
+ truncate_setsize(inode, target);
+ }
+ out:
/* I don't trust error recovery yet. */
WARN_ON(err);
return err;
@@ -1880,8 +1917,11 @@ static void move_page_to_inode(struct inode *inode, struct page *page)
li->li_block = block;
block->page = NULL;
- page->private = 0;
- ClearPagePrivate(page);
+ if (PagePrivate(page)) {
+ ClearPagePrivate(page);
+ page_cache_release(page);
+ set_page_private(page, 0);
+ }
}
static void move_inode_to_page(struct page *page, struct inode *inode)
@@ -1897,8 +1937,12 @@ static void move_inode_to_page(struct page *page, struct inode *inode)
BUG_ON(PagePrivate(page));
block->ops = &indirect_block_ops;
block->page = page;
- page->private = (unsigned long)block;
- SetPagePrivate(page);
+
+ if (!PagePrivate(page)) {
+ SetPagePrivate(page);
+ page_cache_get(page);
+ set_page_private(page, (unsigned long) block);
+ }
block->inode = NULL;
li->li_block = NULL;
@@ -1923,9 +1967,9 @@ int logfs_read_inode(struct inode *inode)
if (IS_ERR(page))
return PTR_ERR(page);
- di = kmap_atomic(page, KM_USER0);
+ di = kmap_atomic(page);
logfs_disk_to_inode(di, inode);
- kunmap_atomic(di, KM_USER0);
+ kunmap_atomic(di);
move_page_to_inode(inode, page);
page_cache_release(page);
return 0;
@@ -1944,38 +1988,13 @@ static struct page *inode_to_page(struct inode *inode)
if (!page)
return NULL;
- di = kmap_atomic(page, KM_USER0);
+ di = kmap_atomic(page);
logfs_inode_to_disk(inode, di);
- kunmap_atomic(di, KM_USER0);
+ kunmap_atomic(di);
move_inode_to_page(page, inode);
return page;
}
-/* Cheaper version of write_inode. All changes are concealed in
- * aliases, which are moved back. No write to the medium happens.
- */
-void logfs_clear_inode(struct inode *inode)
-{
- struct super_block *sb = inode->i_sb;
- struct logfs_inode *li = logfs_inode(inode);
- struct logfs_block *block = li->li_block;
- struct page *page;
-
- /* Only deleted files may be dirty at this point */
- BUG_ON(inode->i_state & I_DIRTY && inode->i_nlink);
- if (!block)
- return;
- if ((logfs_super(sb)->s_flags & LOGFS_SB_FLAG_SHUTDOWN)) {
- block->ops->free_block(inode->i_sb, block);
- return;
- }
-
- BUG_ON(inode->i_ino < LOGFS_RESERVED_INOS);
- page = inode_to_page(inode);
- BUG_ON(!page); /* FIXME: Use emergency page */
- logfs_put_write_page(page);
-}
-
static int do_write_inode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
@@ -1998,6 +2017,9 @@ static int do_write_inode(struct inode *inode)
/* FIXME: transaction is part of logfs_block now. Is that enough? */
err = logfs_write_buf(master_inode, page, 0);
+ if (err)
+ move_page_to_inode(inode, page);
+
logfs_put_write_page(page);
return err;
}
@@ -2025,13 +2047,13 @@ static void logfs_mod_segment_entry(struct super_block *sb, u32 segno,
if (write)
alloc_indirect_block(inode, page, 0);
- se = kmap_atomic(page, KM_USER0);
+ se = kmap_atomic(page);
change_se(se + child_no, arg);
if (write) {
logfs_set_alias(sb, logfs_block(page), child_no);
BUG_ON((int)be32_to_cpu(se[child_no].valid) > super->s_segsize);
}
- kunmap_atomic(se, KM_USER0);
+ kunmap_atomic(se);
logfs_put_write_page(page);
}
@@ -2107,14 +2129,14 @@ void logfs_set_segment_unreserved(struct super_block *sb, u32 segno, u32 ec)
ec_level);
}
-int __logfs_write_inode(struct inode *inode, long flags)
+int __logfs_write_inode(struct inode *inode, struct page *page, long flags)
{
struct super_block *sb = inode->i_sb;
int ret;
- logfs_get_wblocks(sb, NULL, flags & WF_LOCK);
+ logfs_get_wblocks(sb, page, flags & WF_LOCK);
ret = do_write_inode(inode);
- logfs_put_wblocks(sb, NULL, flags & WF_LOCK);
+ logfs_put_wblocks(sb, page, flags & WF_LOCK);
return ret;
}
@@ -2143,18 +2165,39 @@ static int do_delete_inode(struct inode *inode)
* ZOMBIE inodes have already been deleted before and should remain dead,
* if it weren't for valid checking. No need to kill them again here.
*/
-void logfs_delete_inode(struct inode *inode)
+void logfs_evict_inode(struct inode *inode)
{
+ struct super_block *sb = inode->i_sb;
struct logfs_inode *li = logfs_inode(inode);
+ struct logfs_block *block = li->li_block;
+ struct page *page;
- if (!(li->li_flags & LOGFS_IF_ZOMBIE)) {
- li->li_flags |= LOGFS_IF_ZOMBIE;
- if (i_size_read(inode) > 0)
- logfs_truncate(inode, 0);
- do_delete_inode(inode);
+ if (!inode->i_nlink) {
+ if (!(li->li_flags & LOGFS_IF_ZOMBIE)) {
+ li->li_flags |= LOGFS_IF_ZOMBIE;
+ if (i_size_read(inode) > 0)
+ logfs_truncate(inode, 0);
+ do_delete_inode(inode);
+ }
}
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
+
+ /* Cheaper version of write_inode. All changes are concealed in
+ * aliases, which are moved back. No write to the medium happens.
+ */
+ /* Only deleted files may be dirty at this point */
+ BUG_ON(inode->i_state & I_DIRTY && inode->i_nlink);
+ if (!block)
+ return;
+ if ((logfs_super(sb)->s_flags & LOGFS_SB_FLAG_SHUTDOWN)) {
+ block->ops->free_block(inode->i_sb, block);
+ return;
+ }
+
+ page = inode_to_page(inode);
+ BUG_ON(!page); /* FIXME: Use emergency page */
+ logfs_put_write_page(page);
}
void btree_write_block(struct logfs_block *block)
@@ -2207,10 +2250,10 @@ int logfs_inode_write(struct inode *inode, const void *buf, size_t count,
if (!page)
return -ENOMEM;
- pagebuf = kmap_atomic(page, KM_USER0);
+ pagebuf = kmap_atomic(page);
memcpy(pagebuf, buf, count);
flush_dcache_page(page);
- kunmap_atomic(pagebuf, KM_USER0);
+ kunmap_atomic(pagebuf);
if (i_size_read(inode) < pos + LOGFS_BLOCKSIZE)
i_size_write(inode, pos + LOGFS_BLOCKSIZE);
@@ -2238,6 +2281,7 @@ int logfs_init_rw(struct super_block *sb)
int min_fill = 3 * super->s_no_blocks;
INIT_LIST_HEAD(&super->s_object_alias);
+ INIT_LIST_HEAD(&super->s_writeback_list);
mutex_init(&super->s_write_mutex);
super->s_block_pool = mempool_create_kmalloc_pool(min_fill,
sizeof(struct logfs_block));
@@ -2250,9 +2294,6 @@ void logfs_cleanup_rw(struct super_block *sb)
{
struct logfs_super *super = logfs_super(sb);
- destroy_meta_inode(super->s_segfile_inode);
- if (super->s_block_pool)
- mempool_destroy(super->s_block_pool);
- if (super->s_shadow_pool)
- mempool_destroy(super->s_shadow_pool);
+ logfs_mempool_destroy(super->s_block_pool);
+ logfs_mempool_destroy(super->s_shadow_pool);
}