Browse Source

Numorous changes. See below:

- ext4: ext4_trunc_inode now requires inode index instead of an
          existing inode_ref.
  - ext4: the support of removing directory recursively is temporarily
          dropped.
  - ext4: ext4_dir_rm temporarily relies on ext4_fs_truncate instead of
          ext4_trunc_inode to ensure directory inode not having 0
          length in case of power failure.

  - ext4_journal: connect transactions having reference to the same blocks
                  together.
pull/11/head
ngkaho1234 9 years ago
parent
commit
b05eaa268d
  1. 348
      lwext4/ext4.c
  2. 165
      lwext4/ext4_journal.c
  3. 3
      lwext4/ext4_types.h

348
lwext4/ext4.c

@ -705,52 +705,73 @@ static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)
}
static int ext4_trunc_inode(struct ext4_mountpoint *mp,
struct ext4_inode_ref *inode_ref, uint64_t new_size)
uint32_t index, uint64_t new_size)
{
int r = EOK;
struct ext4_fs *const fs = &mp->fs;
uint64_t inode_size = ext4_inode_get_size(&fs->sb, inode_ref->inode);
uint32_t index = inode_ref->index;
struct ext4_inode_ref inode_ref;
uint64_t inode_size;
bool has_trans = mp->fs.jbd_journal && mp->fs.curr_trans;
r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
if (r != EOK)
return r;
inode_size = ext4_inode_get_size(&fs->sb, inode_ref.inode);
ext4_fs_put_inode_ref(&inode_ref);
if (has_trans)
ext4_trans_stop(mp);
while (inode_size > new_size + CONFIG_MAX_TRUNCATE_SIZE) {
inode_size -= CONFIG_MAX_TRUNCATE_SIZE;
ext4_trans_start(mp);
r = ext4_fs_truncate_inode(inode_ref, inode_size);
r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
if (r != EOK) {
ext4_trans_abort(mp);
break;
}
r = ext4_fs_truncate_inode(&inode_ref, inode_size);
if (r != EOK)
return r;
ext4_fs_put_inode_ref(&inode_ref);
else
r = ext4_fs_put_inode_ref(&inode_ref);
r = ext4_fs_put_inode_ref(inode_ref);
if (r != EOK)
if (r != EOK) {
ext4_trans_abort(mp);
else
goto Finish;
} else
ext4_trans_stop(mp);
r = ext4_fs_get_inode_ref(fs, index, inode_ref);
if (r != EOK)
return r;
}
if (inode_size > new_size) {
inode_size = new_size;
ext4_trans_start(mp);
r = ext4_fs_truncate_inode(inode_ref, inode_size);
r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
if (r != EOK) {
ext4_trans_abort(mp);
goto Finish;
}
r = ext4_fs_truncate_inode(&inode_ref, inode_size);
if (r != EOK)
return r;
ext4_fs_put_inode_ref(&inode_ref);
else
r = ext4_fs_put_inode_ref(&inode_ref);
r = ext4_fs_put_inode_ref(inode_ref);
if (r != EOK)
ext4_trans_abort(mp);
else
ext4_trans_stop(mp);
r = ext4_fs_get_inode_ref(fs, index, inode_ref);
if (r != EOK)
return r;
}
Finish:
if (has_trans)
ext4_trans_start(mp);
return r;
}
@ -796,6 +817,9 @@ static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
if (parent_inode)
*parent_inode = ref.index;
if (flags & O_CREAT)
ext4_trans_start(mp);
len = ext4_path_check(path, &is_goal);
while (1) {
@ -905,7 +929,7 @@ static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
if (is_goal) {
if ((f->flags & O_TRUNC) && (imode == EXT4_INODE_MODE_FILE)) {
r = ext4_trunc_inode(mp, &ref, 0);
r = ext4_trunc_inode(mp, ref.index, 0);
if (r != EOK) {
ext4_fs_put_inode_ref(&ref);
return r;
@ -923,6 +947,14 @@ static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
}
r = ext4_fs_put_inode_ref(&ref);
if (flags & O_CREAT) {
if (r == EOK)
ext4_trans_stop(mp);
else
ext4_trans_abort(mp);
}
return r;
}
@ -1270,6 +1302,29 @@ int ext4_fremove(const char *path)
EXT4_MP_UNLOCK(mp);
return r;
}
/* We do not allow opening files here. */
if (ext4_inode_type(&mp->fs.sb, child.inode) ==
EXT4_INODE_MODE_DIRECTORY) {
ext4_fs_put_inode_ref(&parent);
ext4_fs_put_inode_ref(&child);
ext4_trans_abort(mp);
EXT4_MP_UNLOCK(mp);
return r;
}
/*Link count will be zero, the inode should be freed. */
if (ext4_inode_get_links_cnt(child.inode) == 1) {
ext4_block_cache_write_back(mp->fs.bdev, 1);
r = ext4_trunc_inode(mp, child.index, 0);
if (r != EOK) {
ext4_fs_put_inode_ref(&parent);
ext4_fs_put_inode_ref(&child);
ext4_trans_abort(mp);
EXT4_MP_UNLOCK(mp);
return r;
}
ext4_block_cache_write_back(mp->fs.bdev, 0);
}
/*Set path*/
path += name_off;
@ -1284,14 +1339,6 @@ int ext4_fremove(const char *path)
/*Link count is zero, the inode should be freed. */
if (!ext4_inode_get_links_cnt(child.inode)) {
ext4_inode_set_del_time(child.inode, -1L);
/*Turncate*/
ext4_block_cache_write_back(mp->fs.bdev, 1);
/*Truncate may be IO heavy. Do it writeback cache mode.*/
r = ext4_trunc_inode(mp, &child, 0);
ext4_block_cache_write_back(mp->fs.bdev, 0);
if (r != EOK)
goto Finish;
r = ext4_fs_free_inode(&child);
if (r != EOK)
@ -1360,17 +1407,11 @@ int ext4_fopen(ext4_file *f, const char *path, const char *flags)
return ENOENT;
EXT4_MP_LOCK(mp);
ext4_trans_start(mp);
ext4_block_cache_write_back(mp->fs.bdev, 1);
r = ext4_generic_open(f, path, flags, true, 0, 0);
ext4_block_cache_write_back(mp->fs.bdev, 0);
if (r != EOK)
ext4_trans_abort(mp);
else
ext4_trans_stop(mp);
EXT4_MP_UNLOCK(mp);
return r;
}
@ -1387,17 +1428,11 @@ int ext4_fopen2(ext4_file *f, const char *path, int flags)
filetype = EXT4_DE_REG_FILE;
EXT4_MP_LOCK(mp);
ext4_trans_start(mp);
ext4_block_cache_write_back(mp->fs.bdev, 1);
r = ext4_generic_open2(f, path, flags, filetype, NULL, NULL);
ext4_block_cache_write_back(mp->fs.bdev, 0);
if (r != EOK)
ext4_trans_abort(mp);
else
ext4_trans_stop(mp);
EXT4_MP_UNLOCK(mp);
return r;
}
@ -1438,7 +1473,7 @@ static int ext4_ftruncate_no_lock(ext4_file *f, uint64_t size)
if (r != EOK)
goto Finish;
r = ext4_trunc_inode(f->mp, &ref, size);
r = ext4_trunc_inode(f->mp, ref.index, size);
if (r != EOK)
goto Finish;
@ -2463,26 +2498,19 @@ int ext4_dir_rm(const char *path)
ext4_file f;
struct ext4_mountpoint *mp = ext4_get_mount(path);
struct ext4_inode_ref act;
struct ext4_inode_ref child;
struct ext4_dir_iter it;
struct ext4_inode_ref act, parent;
uint32_t name_off;
uint32_t inode_up;
uint32_t inode_current;
uint32_t depth = 1;
uint32_t inode_up, inode_current;
bool has_children;
bool has_children = false;
bool is_goal;
bool dir_end;
if (!mp)
return ENOENT;
EXT4_MP_LOCK(mp);
struct ext4_fs *const fs = &mp->fs;
/*Check if exist.*/
r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
if (r != EOK) {
@ -2498,188 +2526,74 @@ int ext4_dir_rm(const char *path)
ext4_block_cache_write_back(mp->fs.bdev, 1);
do {
uint64_t act_curr_pos = 0;
has_children = false;
dir_end = false;
while (r == EOK && !has_children && !dir_end) {
/*Load directory node.*/
r = ext4_fs_get_inode_ref(fs, inode_current, &act);
if (r != EOK) {
break;
}
/*Initialize iterator.*/
r = ext4_dir_iterator_init(&it, &act, act_curr_pos);
if (r != EOK) {
ext4_fs_put_inode_ref(&act);
break;
}
if (!it.curr) {
dir_end = true;
goto End;
}
ext4_trans_start(mp);
/*Get up directory inode when ".." entry*/
if ((it.curr->name_len == 2) &&
ext4_is_dots(it.curr->name, it.curr->name_len)) {
inode_up = ext4_dir_en_get_inode(it.curr);
}
/*If directory or file entry, but not "." ".." entry*/
if (!ext4_is_dots(it.curr->name, it.curr->name_len)) {
/*Get child inode reference do unlink
* directory/file.*/
uint32_t cinode;
cinode = ext4_dir_en_get_inode(it.curr);
r = ext4_fs_get_inode_ref(fs, cinode, &child);
if (r != EOK)
goto End;
/*If directory with no leaf children*/
r = ext4_has_children(&has_children, &child);
if (r != EOK) {
ext4_fs_put_inode_ref(&child);
goto End;
}
if (has_children) {
/*Has directory children. Go into this
* directory.*/
inode_up = inode_current;
inode_current = cinode;
depth++;
ext4_fs_put_inode_ref(&child);
goto End;
}
/* Truncate */
r = ext4_trunc_inode(mp, &child, 0);
if (r != EOK) {
ext4_fs_put_inode_ref(&child);
goto End;
}
/*No children in child directory or file. Just
* unlink.*/
r = ext4_unlink(f.mp, &act, &child,
(char *)it.curr->name,
it.curr->name_len);
if (r != EOK) {
ext4_fs_put_inode_ref(&child);
goto End;
}
ext4_inode_set_del_time(child.inode, -1L);
ext4_inode_set_links_cnt(child.inode, 0);
child.dirty = true;
r = ext4_fs_free_inode(&child);
if (r != EOK) {
ext4_fs_put_inode_ref(&child);
goto End;
}
r = ext4_fs_put_inode_ref(&child);
if (r != EOK)
goto End;
}
r = ext4_dir_iterator_next(&it);
if (r != EOK)
goto End;
act_curr_pos = it.curr_off;
End:
ext4_dir_iterator_fini(&it);
if (r == EOK)
r = ext4_fs_put_inode_ref(&act);
else
ext4_fs_put_inode_ref(&act);
if (r != EOK)
ext4_trans_abort(mp);
else
ext4_trans_stop(mp);
}
if (dir_end) {
/*Directory iterator reached last entry*/
depth--;
if (depth)
inode_current = inode_up;
}
/*Load parent.*/
r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
&parent);
if (r != EOK)
goto Finish;
r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current,
&act);
if (r != EOK) {
ext4_fs_put_inode_ref(&parent);
goto Finish;
}
r = ext4_has_children(&has_children, &act);
if (r != EOK) {
ext4_fs_put_inode_ref(&parent);
ext4_fs_put_inode_ref(&act);
goto Finish;
}
if (has_children) {
r = ENOTEMPTY;
ext4_fs_put_inode_ref(&parent);
ext4_fs_put_inode_ref(&act);
goto Finish;
}
} while (depth);
ext4_trans_start(mp);
/*Last unlink*/
if (r == EOK && !depth) {
/*Load parent.*/
struct ext4_inode_ref parent;
r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
&parent);
if (r != EOK)
goto Finish;
r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current,
&act);
if (ext4_inode_get_links_cnt(act.inode) == 2) {
/*Truncate*/
r = ext4_fs_truncate_inode(&act, 0);
if (r != EOK) {
ext4_fs_put_inode_ref(&parent);
ext4_fs_put_inode_ref(&act);
goto Finish;
}
}
ext4_trans_start(mp);
/* Unlink from root of current directory*/
r = ext4_unlink(f.mp, &parent, &act,
(char *)path, len);
if (r != EOK) {
ext4_fs_put_inode_ref(&parent);
ext4_fs_put_inode_ref(&act);
goto Finish;
}
if (ext4_inode_get_links_cnt(act.inode) == 2) {
ext4_inode_set_del_time(act.inode, -1L);
ext4_inode_set_links_cnt(act.inode, 0);
act.dirty = true;
/* In this place all directories should be
* unlinked.
* Last unlink from root of current directory*/
r = ext4_unlink(f.mp, &parent, &act,
(char *)path, len);
r = ext4_fs_free_inode(&act);
if (r != EOK) {
ext4_fs_put_inode_ref(&parent);
ext4_fs_put_inode_ref(&act);
goto Finish;
}
}
if (ext4_inode_get_links_cnt(act.inode) == 2) {
ext4_inode_set_del_time(act.inode, -1L);
ext4_inode_set_links_cnt(act.inode, 0);
act.dirty = true;
/*Turncate*/
r = ext4_trunc_inode(mp, &act, 0);
if (r != EOK) {
ext4_fs_put_inode_ref(&parent);
ext4_fs_put_inode_ref(&act);
goto Finish;
}
r = ext4_fs_free_inode(&act);
if (r != EOK) {
ext4_fs_put_inode_ref(&parent);
ext4_fs_put_inode_ref(&act);
goto Finish;
}
}
r = ext4_fs_put_inode_ref(&parent);
if (r != EOK)
goto Finish;
r = ext4_fs_put_inode_ref(&parent);
if (r != EOK)
goto Finish;
r = ext4_fs_put_inode_ref(&act);
Finish:
if (r != EOK)
ext4_trans_abort(mp);
else
ext4_trans_stop(mp);
}
r = ext4_fs_put_inode_ref(&act);
Finish:
if (r != EOK)
ext4_trans_abort(mp);
else
ext4_trans_stop(mp);
ext4_block_cache_write_back(mp->fs.bdev, 0);
EXT4_MP_UNLOCK(mp);

165
lwext4/ext4_journal.c

@ -1273,8 +1273,9 @@ static void jbd_journal_flush_trans(struct jbd_trans *trans)
struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
tmp) {
struct ext4_block block = jbd_buf->block;
ext4_block_flush_buf(fs->bdev, block.buf);
struct ext4_buf *buf = jbd_buf->block_rec->buf;
if (buf)
ext4_block_flush_buf(fs->bdev, buf);
}
}
@ -1458,6 +1459,18 @@ jbd_trans_block_rec_lookup(struct jbd_journal *journal,
&tmp);
}
static void
jbd_trans_change_ownership(struct jbd_block_rec *block_rec,
struct jbd_trans *new_trans,
struct ext4_buf *new_buf)
{
LIST_REMOVE(block_rec, tbrec_node);
/* Now this block record belongs to this transaction. */
LIST_INSERT_HEAD(&new_trans->tbrec_list, block_rec, tbrec_node);
block_rec->trans = new_trans;
block_rec->buf = new_buf;
}
static inline struct jbd_block_rec *
jbd_trans_insert_block_rec(struct jbd_trans *trans,
ext4_fsblk_t lba,
@ -1466,12 +1479,7 @@ jbd_trans_insert_block_rec(struct jbd_trans *trans,
struct jbd_block_rec *block_rec;
block_rec = jbd_trans_block_rec_lookup(trans->journal, lba);
if (block_rec) {
LIST_REMOVE(block_rec, tbrec_node);
/* Data should be flushed to disk already. */
ext4_assert(!block_rec->buf);
/* Now this block record belongs to this transaction. */
LIST_INSERT_HEAD(&trans->tbrec_list, block_rec, tbrec_node);
block_rec->trans = trans;
jbd_trans_change_ownership(block_rec, trans, buf);
return block_rec;
}
block_rec = calloc(1, sizeof(struct jbd_block_rec));
@ -1481,11 +1489,68 @@ jbd_trans_insert_block_rec(struct jbd_trans *trans,
block_rec->lba = lba;
block_rec->buf = buf;
block_rec->trans = trans;
TAILQ_INIT(&block_rec->dirty_buf_queue);
LIST_INSERT_HEAD(&trans->tbrec_list, block_rec, tbrec_node);
RB_INSERT(jbd_block, &trans->journal->block_rec_root, block_rec);
return block_rec;
}
static void
jbd_trans_finish_callback(struct jbd_journal *journal,
const struct jbd_trans *trans,
struct jbd_block_rec *block_rec,
bool abort)
{
struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
if (block_rec->trans != trans)
return;
if (!abort) {
struct jbd_buf *jbd_buf, *tmp;
TAILQ_FOREACH_SAFE(jbd_buf,
&block_rec->dirty_buf_queue,
dirty_buf_node,
tmp) {
/* All we need is a fake ext4_buf. */
struct ext4_buf buf;
jbd_trans_end_write(fs->bdev->bc,
&buf,
EOK,
jbd_buf);
}
} else {
struct jbd_buf *jbd_buf;
struct ext4_block jbd_block = EXT4_BLOCK_ZERO(),
block = EXT4_BLOCK_ZERO();
jbd_buf = TAILQ_LAST(&block_rec->dirty_buf_queue,
jbd_buf_dirty);
if (jbd_buf) {
ext4_assert(ext4_block_get(fs->bdev,
&jbd_block,
jbd_buf->jbd_lba) == EOK);
ext4_assert(ext4_block_get_noread(fs->bdev,
&block,
block_rec->lba) == EOK);
memcpy(block.data, jbd_block.data,
journal->block_size);
jbd_trans_change_ownership(block_rec,
jbd_buf->trans, block.buf);
block.buf->end_write = jbd_trans_end_write;
block.buf->end_write_arg = jbd_buf;
ext4_bcache_set_flag(jbd_block.buf, BC_TMP);
ext4_bcache_set_dirty(block.buf);
ext4_block_set(fs->bdev, &jbd_block);
ext4_block_set(fs->bdev, &block);
return;
}
}
}
static inline void
jbd_trans_remove_block_rec(struct jbd_journal *journal,
struct jbd_block_rec *block_rec,
@ -1511,35 +1576,41 @@ int jbd_trans_set_block_dirty(struct jbd_trans *trans,
{
struct jbd_buf *buf;
if (!ext4_bcache_test_flag(block->buf, BC_DIRTY) &&
block->buf->end_write != jbd_trans_end_write) {
struct jbd_block_rec *block_rec;
buf = calloc(1, sizeof(struct jbd_buf));
if (!buf)
return ENOMEM;
struct jbd_block_rec *block_rec;
if (block->buf->end_write == jbd_trans_end_write) {
buf = block->buf->end_write_arg;
if (buf && buf->trans == trans)
return EOK;
}
buf = calloc(1, sizeof(struct jbd_buf));
if (!buf)
return ENOMEM;
if ((block_rec = jbd_trans_insert_block_rec(trans,
if ((block_rec = jbd_trans_insert_block_rec(trans,
block->lb_id,
block->buf)) == NULL) {
free(buf);
return ENOMEM;
}
free(buf);
return ENOMEM;
}
buf->block_rec = block_rec;
buf->trans = trans;
buf->block = *block;
ext4_bcache_inc_ref(block->buf);
TAILQ_INSERT_TAIL(&block_rec->dirty_buf_queue,
buf,
dirty_buf_node);
/* If the content reach the disk, notify us
* so that we may do a checkpoint. */
block->buf->end_write = jbd_trans_end_write;
block->buf->end_write_arg = buf;
buf->block_rec = block_rec;
buf->trans = trans;
buf->block = *block;
ext4_bcache_inc_ref(block->buf);
trans->data_cnt++;
TAILQ_INSERT_HEAD(&trans->buf_queue, buf, buf_node);
/* If the content reach the disk, notify us
* so that we may do a checkpoint. */
block->buf->end_write = jbd_trans_end_write;
block->buf->end_write_arg = buf;
ext4_bcache_set_dirty(block->buf);
}
trans->data_cnt++;
TAILQ_INSERT_HEAD(&trans->buf_queue, buf, buf_node);
ext4_bcache_set_dirty(block->buf);
return EOK;
}
@ -1606,6 +1677,7 @@ void jbd_journal_free_trans(struct jbd_journal *journal,
struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
tmp) {
block_rec = jbd_buf->block_rec;
if (abort) {
jbd_buf->block.buf->end_write = NULL;
jbd_buf->block.buf->end_write_arg = NULL;
@ -1613,6 +1685,13 @@ void jbd_journal_free_trans(struct jbd_journal *journal,
ext4_block_set(fs->bdev, &jbd_buf->block);
}
TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
jbd_buf,
dirty_buf_node);
jbd_trans_finish_callback(journal,
trans,
block_rec,
abort);
TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
free(jbd_buf);
}
@ -1691,6 +1770,15 @@ static int jbd_journal_prepare(struct jbd_journal *journal,
if (ext4_bcache_test_flag(jbd_buf->block.buf,
BC_DIRTY))
break;
TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
jbd_buf,
dirty_buf_node);
jbd_trans_finish_callback(journal,
trans,
jbd_buf->block_rec,
false);
/* The buffer has not been modified, just release
* that jbd_buf. */
@ -1710,6 +1798,15 @@ static int jbd_journal_prepare(struct jbd_journal *journal,
bool uuid_exist = false;
if (!ext4_bcache_test_flag(jbd_buf->block.buf,
BC_DIRTY)) {
TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
jbd_buf,
dirty_buf_node);
jbd_trans_finish_callback(journal,
trans,
jbd_buf->block_rec,
false);
/* The buffer has not been modified, just release
* that jbd_buf. */
jbd_trans_remove_block_rec(journal,
@ -1791,6 +1888,7 @@ again:
memcpy(data_block.data, jbd_buf->block.data,
journal->block_size);
jbd_buf->jbd_lba = data_block.lb_id;
rc = jbd_block_set(journal->jbd_fs, &data_block);
if (rc != EOK)
@ -1943,6 +2041,13 @@ static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
trans->error = res;
TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
jbd_buf,
dirty_buf_node);
jbd_trans_finish_callback(journal,
trans,
jbd_buf->block_rec,
false);
jbd_buf->block_rec->buf = NULL;
free(jbd_buf);

3
lwext4/ext4_types.h

@ -1098,10 +1098,12 @@ struct jbd_fs {
};
struct jbd_buf {
uint64_t jbd_lba;
struct ext4_block block;
struct jbd_trans *trans;
struct jbd_block_rec *block_rec;
TAILQ_ENTRY(jbd_buf) buf_node;
TAILQ_ENTRY(jbd_buf) dirty_buf_node;
};
struct jbd_revoke_rec {
@ -1115,6 +1117,7 @@ struct jbd_block_rec {
struct jbd_trans *trans;
RB_ENTRY(jbd_block_rec) block_rec_node;
LIST_ENTRY(jbd_block_rec) tbrec_node;
TAILQ_HEAD(jbd_buf_dirty, jbd_buf) dirty_buf_queue;
};
struct jbd_trans {

Loading…
Cancel
Save