#include "chfs.h"
void
chfs_calc_trigger_levels(struct chfs_mount *chmp)
{
uint32_t size;
chmp->chm_resv_blocks_deletion = 2;
size = chmp->chm_ebh->flash_size / 50;
size += chmp->chm_ebh->peb_nr * 100;
size += chmp->chm_ebh->eb_size - 1;
chmp->chm_resv_blocks_write =
chmp->chm_resv_blocks_deletion + (size / chmp->chm_ebh->eb_size);
chmp->chm_resv_blocks_gctrigger = chmp->chm_resv_blocks_write + 1;
chmp->chm_resv_blocks_gcmerge = chmp->chm_resv_blocks_deletion + 1;
chmp->chm_vdirty_blocks_gctrigger = chmp->chm_resv_blocks_gctrigger * 10;
chmp->chm_nospc_dirty =
chmp->chm_ebh->eb_size + (chmp->chm_ebh->flash_size / 100);
}
void
chfs_build_set_vnodecache_nlink(struct chfs_mount *chmp,
struct chfs_vnode_cache *vc)
{
struct chfs_dirent *fd, *tmpfd;
TAILQ_FOREACH_SAFE(fd, &vc->scan_dirents, fds, tmpfd) {
struct chfs_vnode_cache *child_vc;
if (!fd->vno)
continue;
mutex_enter(&chmp->chm_lock_vnocache);
child_vc = chfs_vnode_cache_get(chmp, fd->vno);
mutex_exit(&chmp->chm_lock_vnocache);
if (!child_vc) {
chfs_mark_node_obsolete(chmp, fd->nref);
TAILQ_REMOVE(&vc->scan_dirents, fd, fds);
continue;
}
if (fd->type == CHT_DIR) {
if (child_vc->nlink < 1)
child_vc->nlink = 1;
if (child_vc->pvno) {
chfs_err("found a hard link: child dir: %s"
", (vno: %llu) of dir vno: %llu\n",
fd->name, (unsigned long long)fd->vno,
(unsigned long long)vc->vno);
} else {
child_vc->pvno = vc->vno;
}
}
child_vc->nlink++;
vc->nlink++;
}
}
void
chfs_build_remove_unlinked_vnode(struct chfs_mount *chmp,
struct chfs_vnode_cache *vc,
struct chfs_dirent_list *unlinked)
{
struct chfs_node_ref *nref;
struct chfs_dirent *fd, *tmpfd;
dbg("START\n");
dbg("vno: %llu\n", (unsigned long long)vc->vno);
KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
nref = vc->dnode;
while (nref != (struct chfs_node_ref *)vc) {
struct chfs_node_ref *next = nref->nref_next;
dbg("mark dnode\n");
chfs_mark_node_obsolete(chmp, nref);
nref = next;
}
vc->dnode = (struct chfs_node_ref *)vc;
nref = vc->dirents;
while (nref != (struct chfs_node_ref *)vc) {
struct chfs_node_ref *next = nref->nref_next;
dbg("mark dirent\n");
chfs_mark_node_obsolete(chmp, nref);
nref = next;
}
vc->dirents = (struct chfs_node_ref *)vc;
if (!TAILQ_EMPTY(&vc->scan_dirents)) {
TAILQ_FOREACH_SAFE(fd, &vc->scan_dirents, fds, tmpfd) {
struct chfs_vnode_cache *child_vc;
dbg("dirent dump:\n");
dbg(" ->vno: %llu\n", (unsigned long long)fd->vno);
dbg(" ->version: %llu\n", (unsigned long long)fd->version);
dbg(" ->nhash: 0x%x\n", fd->nhash);
dbg(" ->nsize: %d\n", fd->nsize);
dbg(" ->name: %s\n", fd->name);
dbg(" ->type: %d\n", fd->type);
TAILQ_REMOVE(&vc->scan_dirents, fd, fds);
if (!fd->vno) {
chfs_free_dirent(fd);
continue;
}
mutex_enter(&chmp->chm_lock_vnocache);
child_vc = chfs_vnode_cache_get(chmp, fd->vno);
mutex_exit(&chmp->chm_lock_vnocache);
if (!child_vc) {
chfs_free_dirent(fd);
continue;
}
child_vc->nlink--;
if (!child_vc->nlink) {
TAILQ_INSERT_TAIL(unlinked, fd, fds);
} else {
chfs_free_dirent(fd);
}
}
} else {
dbg("there are no scan dirents\n");
}
nref = vc->v;
while ((struct chfs_vnode_cache *)nref != vc) {
chfs_mark_node_obsolete(chmp, nref);
nref = nref->nref_next;
}
vc->v = (struct chfs_node_ref *)vc;
mutex_enter(&chmp->chm_lock_vnocache);
if (vc->vno != CHFS_ROOTINO)
vc->state = VNO_STATE_UNCHECKED;
mutex_exit(&chmp->chm_lock_vnocache);
dbg("END\n");
}
int
chfs_build_filesystem(struct chfs_mount *chmp)
{
int i,err = 0;
struct chfs_vnode_cache *vc;
struct chfs_dirent *fd, *tmpfd;
struct chfs_node_ref **nref;
struct chfs_dirent_list unlinked;
struct chfs_vnode_cache *notregvc;
TAILQ_INIT(&unlinked);
mutex_enter(&chmp->chm_lock_mountfields);
chmp->chm_flags |= CHFS_MP_FLAG_SCANNING;
for (i = 0; i < chmp->chm_ebh->peb_nr; i++) {
chmp->chm_blocks[i].lnr = i;
chmp->chm_blocks[i].free_size = chmp->chm_ebh->eb_size;
if (chmp->chm_ebh->lmap[i] < 0) {
TAILQ_INSERT_TAIL(&chmp->chm_free_queue,
&chmp->chm_blocks[i], queue);
chmp->chm_nr_free_blocks++;
continue;
}
err = chfs_scan_eraseblock(chmp, &chmp->chm_blocks[i]);
switch (err) {
case CHFS_BLK_STATE_FREE:
chmp->chm_nr_free_blocks++;
TAILQ_INSERT_TAIL(&chmp->chm_free_queue,
&chmp->chm_blocks[i], queue);
break;
case CHFS_BLK_STATE_CLEAN:
TAILQ_INSERT_TAIL(&chmp->chm_clean_queue,
&chmp->chm_blocks[i], queue);
break;
case CHFS_BLK_STATE_PARTDIRTY:
if (chmp->chm_blocks[i].free_size > chmp->chm_wbuf_pagesize &&
(!chmp->chm_nextblock ||
chmp->chm_blocks[i].free_size >
chmp->chm_nextblock->free_size)) {
if (chmp->chm_nextblock) {
err = chfs_close_eraseblock(chmp,
chmp->chm_nextblock);
if (err) {
mutex_exit(&chmp->chm_lock_mountfields);
return err;
}
}
chmp->chm_nextblock = &chmp->chm_blocks[i];
} else {
err = chfs_close_eraseblock(chmp,
&chmp->chm_blocks[i]);
if (err) {
mutex_exit(&chmp->chm_lock_mountfields);
return err;
}
}
break;
case CHFS_BLK_STATE_ALLDIRTY:
TAILQ_INSERT_TAIL(&chmp->chm_erase_pending_queue,
&chmp->chm_blocks[i], queue);
chmp->chm_nr_erasable_blocks++;
break;
default:
break;
}
}
chmp->chm_flags &= ~CHFS_MP_FLAG_SCANNING;
chmp->chm_flags |= CHFS_MP_FLAG_BUILDING;
for (i = 0; i < VNODECACHE_SIZE; i++) {
vc = chmp->chm_vnocache_hash[i];
while (vc) {
dbg("vc->vno: %llu\n", (unsigned long long)vc->vno);
if (!TAILQ_EMPTY(&vc->scan_dirents))
chfs_build_set_vnodecache_nlink(chmp, vc);
vc = vc->next;
}
}
for (i = 0; i < VNODECACHE_SIZE; i++) {
vc = chmp->chm_vnocache_hash[i];
while (vc) {
if (vc->nlink) {
vc = vc->next;
continue;
}
chfs_build_remove_unlinked_vnode(chmp,
vc, &unlinked);
vc = vc->next;
}
}
TAILQ_FOREACH_SAFE(fd, &unlinked, fds, tmpfd) {
TAILQ_REMOVE(&unlinked, fd, fds);
mutex_enter(&chmp->chm_lock_vnocache);
vc = chfs_vnode_cache_get(chmp, fd->vno);
mutex_exit(&chmp->chm_lock_vnocache);
if (vc) {
chfs_build_remove_unlinked_vnode(chmp,
vc, &unlinked);
}
chfs_free_dirent(fd);
}
chmp->chm_flags &= ~CHFS_MP_FLAG_BUILDING;
for (i = 0; i < VNODECACHE_SIZE; i++) {
vc = chmp->chm_vnocache_hash[i];
while (vc) {
TAILQ_FOREACH_SAFE(fd, &vc->scan_dirents, fds, tmpfd) {
TAILQ_REMOVE(&vc->scan_dirents, fd, fds);
if (fd->vno == 0) {
nref = &fd->nref;
*nref = fd->nref->nref_next;
} else if (fd->type == CHT_DIR) {
mutex_enter(&chmp->chm_lock_vnocache);
notregvc = chfs_vnode_cache_get(chmp, fd->vno);
notregvc->state = VNO_STATE_PRESENT;
mutex_exit(&chmp->chm_lock_vnocache);
}
chfs_free_dirent(fd);
}
KASSERT(TAILQ_EMPTY(&vc->scan_dirents));
vc = vc->next;
}
}
if (chmp->chm_nextblock) {
dbg("free_size: %d\n", chmp->chm_nextblock->free_size);
chmp->chm_wbuf_ofs = chmp->chm_ebh->eb_size -
chmp->chm_nextblock->free_size;
} else {
chmp->chm_wbuf_ofs = 0xffffffff;
}
mutex_exit(&chmp->chm_lock_mountfields);
return 0;
}