#include <vm/vm_page2.h>
#include "hammer.h"
static int hammer_unload_inode(hammer_inode_t ip);
static void hammer_free_inode(hammer_inode_t ip);
static void hammer_flush_inode_core(hammer_inode_t ip,
hammer_flush_group_t flg, int flags);
static int hammer_setup_child_callback(hammer_record_t rec, void *data);
#if 0
static int hammer_syncgrp_child_callback(hammer_record_t rec, void *data);
#endif
static int hammer_setup_parent_inodes(hammer_inode_t ip, int depth,
hammer_flush_group_t flg);
static int hammer_setup_parent_inodes_helper(hammer_record_t record,
int depth, hammer_flush_group_t flg);
static void hammer_inode_wakereclaims(hammer_inode_t ip);
static struct hammer_inostats *hammer_inode_inostats(hammer_mount_t hmp,
pid_t pid);
static hammer_inode_t __hammer_find_inode(hammer_transaction_t trans,
int64_t obj_id, hammer_tid_t asof,
uint32_t localization);
struct krate hammer_gen_krate = { 1 };
int
hammer_ino_rb_compare(hammer_inode_t ip1, hammer_inode_t ip2)
{
if (ip1->obj_localization < ip2->obj_localization)
return(-1);
if (ip1->obj_localization > ip2->obj_localization)
return(1);
if (ip1->obj_id < ip2->obj_id)
return(-1);
if (ip1->obj_id > ip2->obj_id)
return(1);
if (ip1->obj_asof < ip2->obj_asof)
return(-1);
if (ip1->obj_asof > ip2->obj_asof)
return(1);
return(0);
}
int
hammer_redo_rb_compare(hammer_inode_t ip1, hammer_inode_t ip2)
{
if (ip1->redo_fifo_start < ip2->redo_fifo_start)
return(-1);
if (ip1->redo_fifo_start > ip2->redo_fifo_start)
return(1);
return(0);
}
static int
hammer_inode_info_cmp(hammer_inode_info_t info, hammer_inode_t ip)
{
if (info->obj_localization < ip->obj_localization)
return(-1);
if (info->obj_localization > ip->obj_localization)
return(1);
if (info->obj_id < ip->obj_id)
return(-1);
if (info->obj_id > ip->obj_id)
return(1);
if (info->obj_asof < ip->obj_asof)
return(-1);
if (info->obj_asof > ip->obj_asof)
return(1);
return(0);
}
static int
hammer_inode_info_cmp_all_history(hammer_inode_t ip, void *data)
{
hammer_inode_info_t info = data;
if (ip->obj_localization > info->obj_localization)
return(1);
if (ip->obj_localization < info->obj_localization)
return(-1);
if (ip->obj_id > info->obj_id)
return(1);
if (ip->obj_id < info->obj_id)
return(-1);
return(0);
}
static int
hammer_inode_pfs_cmp(hammer_inode_t ip, void *data)
{
uint32_t localization = *(uint32_t *)data;
if (ip->obj_localization > localization)
return(1);
if (ip->obj_localization < localization)
return(-1);
return(0);
}
static int
hammer_pfs_rb_compare(hammer_pseudofs_inmem_t p1, hammer_pseudofs_inmem_t p2)
{
if (p1->localization < p2->localization)
return(-1);
if (p1->localization > p2->localization)
return(1);
return(0);
}
RB_GENERATE(hammer_ino_rb_tree, hammer_inode, rb_node, hammer_ino_rb_compare);
RB_GENERATE_XLOOKUP(hammer_ino_rb_tree, INFO, hammer_inode, rb_node,
hammer_inode_info_cmp, hammer_inode_info_t);
RB_GENERATE2(hammer_pfs_rb_tree, hammer_pseudofs_inmem, rb_node,
hammer_pfs_rb_compare, uint32_t, localization);
int
hammer_vop_inactive(struct vop_inactive_args *ap)
{
hammer_inode_t ip = VTOI(ap->a_vp);
hammer_mount_t hmp;
if (ip == NULL) {
vrecycle(ap->a_vp);
return(0);
}
if (ip->ino_data.nlinks == 0) {
hmp = ip->hmp;
lwkt_gettoken(&hmp->fs_token);
hammer_inode_unloadable_check(ip, 0);
if (ip->flags & HAMMER_INODE_MODMASK)
hammer_flush_inode(ip, 0);
lwkt_reltoken(&hmp->fs_token);
vrecycle(ap->a_vp);
}
return(0);
}
int
hammer_vop_reclaim(struct vop_reclaim_args *ap)
{
hammer_inode_t ip;
hammer_mount_t hmp;
struct vnode *vp;
vp = ap->a_vp;
if ((ip = vp->v_data) != NULL) {
hmp = ip->hmp;
lwkt_gettoken(&hmp->fs_token);
hammer_lock_ex(&ip->lock);
vp->v_data = NULL;
ip->vp = NULL;
if ((ip->flags & HAMMER_INODE_RECLAIM) == 0) {
++hammer_count_reclaims;
++hmp->count_reclaims;
ip->flags |= HAMMER_INODE_RECLAIM;
}
hammer_unlock(&ip->lock);
vclrisdirty(vp);
hammer_rel_inode(ip, 1);
lwkt_reltoken(&hmp->fs_token);
}
return(0);
}
void
hammer_inode_dirty(hammer_inode_t ip)
{
struct vnode *vp;
if ((ip->flags & HAMMER_INODE_MODMASK) &&
(vp = ip->vp) != NULL &&
(vp->v_flag & (VRECLAIMED | VISDIRTY)) == 0) {
vsetisdirty(vp);
}
}
int
hammer_get_vnode(hammer_inode_t ip, struct vnode **vpp)
{
hammer_mount_t hmp;
struct vnode *vp;
int error = 0;
uint8_t obj_type;
hmp = ip->hmp;
for (;;) {
if ((vp = ip->vp) == NULL) {
error = getnewvnode(VT_HAMMER, hmp->mp, vpp, 0, 0);
if (error)
break;
hammer_lock_ex(&ip->lock);
if (ip->vp != NULL) {
hammer_unlock(&ip->lock);
vp = *vpp;
vp->v_type = VBAD;
vx_put(vp);
continue;
}
hammer_ref(&ip->lock);
vp = *vpp;
ip->vp = vp;
obj_type = ip->ino_data.obj_type;
vp->v_type = hammer_get_vnode_type(obj_type);
hammer_inode_wakereclaims(ip);
switch(ip->ino_data.obj_type) {
case HAMMER_OBJTYPE_CDEV:
case HAMMER_OBJTYPE_BDEV:
vp->v_ops = &hmp->mp->mnt_vn_spec_ops;
addaliasu(vp, ip->ino_data.rmajor,
ip->ino_data.rminor);
break;
case HAMMER_OBJTYPE_FIFO:
vp->v_ops = &hmp->mp->mnt_vn_fifo_ops;
break;
case HAMMER_OBJTYPE_REGFILE:
break;
default:
break;
}
if (ip->obj_id == HAMMER_OBJID_ROOT) {
if (ip->obj_asof == hmp->asof) {
if (ip->obj_localization ==
HAMMER_DEF_LOCALIZATION)
vsetflags(vp, VROOT);
else
vsetflags(vp, VPFSROOT);
} else {
vsetflags(vp, VPFSROOT);
}
}
vp->v_data = (void *)ip;
hammer_unlock(&ip->lock);
if (vp->v_type == VREG) {
vinitvmio(vp, ip->ino_data.size,
hammer_blocksize(ip->ino_data.size),
hammer_blockoff(ip->ino_data.size));
}
vx_downgrade(vp);
break;
}
hammer_lock_ex(&ip->lock);
if ((vp = ip->vp) == NULL) {
hammer_unlock(&ip->lock);
continue;
}
vhold(vp);
hammer_unlock(&ip->lock);
if (vget(vp, LK_EXCLUSIVE) == 0) {
if (vp == ip->vp) {
vdrop(vp);
break;
}
vput(vp);
}
vdrop(vp);
}
*vpp = vp;
return(error);
}
void
hammer_scan_inode_snapshots(hammer_mount_t hmp, hammer_inode_info_t iinfo,
int (*callback)(hammer_inode_t ip, void *data),
void *data)
{
hammer_ino_rb_tree_RB_SCAN(&hmp->rb_inos_root,
hammer_inode_info_cmp_all_history,
callback, iinfo);
}
hammer_inode_t
hammer_get_inode(hammer_transaction_t trans, hammer_inode_t dip,
int64_t obj_id, hammer_tid_t asof, uint32_t localization,
int flags, int *errorp)
{
hammer_mount_t hmp = trans->hmp;
struct hammer_node_cache *cachep;
struct hammer_cursor cursor;
hammer_inode_t ip;
loop:
*errorp = 0;
ip = __hammer_find_inode(trans, obj_id, asof, localization);
if (ip) {
if (ip->flags & HAMMER_INODE_DUMMY) {
*errorp = ENOENT;
return(NULL);
}
hammer_ref(&ip->lock);
return(ip);
}
ip = kmalloc(sizeof(*ip), hmp->m_inodes, M_WAITOK|M_ZERO);
++hammer_count_inodes;
++hmp->count_inodes;
ip->obj_id = obj_id;
ip->obj_asof = asof;
ip->obj_localization = localization;
ip->hmp = hmp;
ip->flags = flags & HAMMER_INODE_RO;
ip->cache[0].ip = ip;
ip->cache[1].ip = ip;
ip->cache[2].ip = ip;
ip->cache[3].ip = ip;
if (hmp->ronly)
ip->flags |= HAMMER_INODE_RO;
ip->sync_trunc_off = ip->trunc_off = ip->save_trunc_off =
HAMMER_MAX_KEY;
RB_INIT(&ip->rec_tree);
TAILQ_INIT(&ip->target_list);
hammer_ref(&ip->lock);
retry:
cachep = NULL;
if (dip) {
if (dip->cache[2].node)
cachep = &dip->cache[2];
else
cachep = &dip->cache[0];
}
hammer_init_cursor(trans, &cursor, cachep, NULL);
cursor.key_beg.localization = localization | HAMMER_LOCALIZE_INODE;
cursor.key_beg.obj_id = ip->obj_id;
cursor.key_beg.key = 0;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
cursor.key_beg.obj_type = 0;
cursor.asof = asof;
cursor.flags = HAMMER_CURSOR_GET_DATA | HAMMER_CURSOR_ASOF;
*errorp = hammer_btree_lookup(&cursor);
if (*errorp == EDEADLK) {
hammer_done_cursor(&cursor);
goto retry;
}
if (*errorp == 0) {
ip->ino_leaf = cursor.node->ondisk->elms[cursor.index].leaf;
ip->ino_data = cursor.data->inode;
hammer_cache_node(&ip->cache[0], cursor.node);
if (dip) {
if (dip->cache[3].node) {
hammer_cache_node(&ip->cache[1],
dip->cache[3].node);
}
hammer_cache_node(&dip->cache[2], cursor.node);
}
ip->save_trunc_off = ip->ino_data.size;
if (dip && dip->obj_localization == ip->obj_localization) {
ip->pfsm = dip->pfsm;
hammer_ref(&ip->pfsm->lock);
} else {
ip->pfsm = hammer_load_pseudofs(trans,
ip->obj_localization,
errorp);
*errorp = 0;
}
}
if (*errorp == 0) {
if (RB_INSERT(hammer_ino_rb_tree, &hmp->rb_inos_root, ip)) {
hammer_free_inode(ip);
hammer_done_cursor(&cursor);
goto loop;
}
ip->flags |= HAMMER_INODE_ONDISK;
} else {
if (ip->flags & HAMMER_INODE_RSV_INODES) {
ip->flags &= ~HAMMER_INODE_RSV_INODES;
--hmp->rsv_inodes;
}
hammer_free_inode(ip);
ip = NULL;
}
hammer_done_cursor(&cursor);
return (ip);
}
hammer_inode_t
hammer_get_dummy_inode(hammer_transaction_t trans, hammer_inode_t dip,
int64_t obj_id, hammer_tid_t asof, uint32_t localization,
int flags, int *errorp)
{
hammer_mount_t hmp = trans->hmp;
hammer_inode_t ip;
loop:
*errorp = 0;
ip = __hammer_find_inode(trans, obj_id, asof, localization);
if (ip) {
if ((ip->flags & HAMMER_INODE_DUMMY) == 0) {
*errorp = ENOENT;
return(NULL);
}
hammer_ref(&ip->lock);
return(ip);
}
ip = kmalloc(sizeof(*ip), hmp->m_inodes, M_WAITOK|M_ZERO);
++hammer_count_inodes;
++hmp->count_inodes;
ip->obj_id = obj_id;
ip->obj_asof = asof;
ip->obj_localization = localization;
ip->hmp = hmp;
ip->flags = flags | HAMMER_INODE_RO | HAMMER_INODE_DUMMY;
ip->cache[0].ip = ip;
ip->cache[1].ip = ip;
ip->cache[2].ip = ip;
ip->cache[3].ip = ip;
ip->sync_trunc_off = ip->trunc_off = ip->save_trunc_off =
HAMMER_MAX_KEY;
RB_INIT(&ip->rec_tree);
TAILQ_INIT(&ip->target_list);
hammer_ref(&ip->lock);
ip->save_trunc_off = ip->ino_data.size;
ip->ino_data.obj_type = HAMMER_OBJTYPE_FIFO;
if (dip && dip->obj_localization == ip->obj_localization) {
ip->pfsm = dip->pfsm;
hammer_ref(&ip->pfsm->lock);
} else {
ip->pfsm = hammer_load_pseudofs(trans, ip->obj_localization,
errorp);
*errorp = 0;
}
if (*errorp == 0) {
if (RB_INSERT(hammer_ino_rb_tree, &hmp->rb_inos_root, ip)) {
hammer_free_inode(ip);
goto loop;
}
} else {
if (ip->flags & HAMMER_INODE_RSV_INODES) {
ip->flags &= ~HAMMER_INODE_RSV_INODES;
--hmp->rsv_inodes;
}
hammer_free_inode(ip);
ip = NULL;
}
trans->flags |= HAMMER_TRANSF_NEWINODE;
return (ip);
}
hammer_inode_t
hammer_find_inode(hammer_transaction_t trans, int64_t obj_id,
hammer_tid_t asof, uint32_t localization)
{
hammer_inode_t ip;
ip = __hammer_find_inode(trans, obj_id, asof, localization);
if (ip) {
if (ip->flags & HAMMER_INODE_DUMMY)
ip = NULL;
else
hammer_ref(&ip->lock);
}
return(ip);
}
static hammer_inode_t
__hammer_find_inode(hammer_transaction_t trans, int64_t obj_id,
hammer_tid_t asof, uint32_t localization)
{
hammer_mount_t hmp = trans->hmp;
struct hammer_inode_info iinfo;
hammer_inode_t ip;
iinfo.obj_id = obj_id;
iinfo.obj_asof = asof;
iinfo.obj_localization = localization;
ip = hammer_ino_rb_tree_RB_LOOKUP_INFO(&hmp->rb_inos_root, &iinfo);
return(ip);
}
int
hammer_create_inode(hammer_transaction_t trans, struct vattr *vap,
struct ucred *cred,
hammer_inode_t dip, const char *name, int namelen,
hammer_pseudofs_inmem_t pfsm, hammer_inode_t *ipp)
{
hammer_mount_t hmp;
hammer_inode_t ip;
uid_t xuid;
int error;
int64_t namekey;
uint32_t dummy;
hmp = trans->hmp;
if (dip && dip->ino_data.nlinks == 0) {
*ipp = NULL;
return (EINVAL);
}
ip = kmalloc(sizeof(*ip), hmp->m_inodes, M_WAITOK|M_ZERO);
++hammer_count_inodes;
++hmp->count_inodes;
trans->flags |= HAMMER_TRANSF_NEWINODE;
if (pfsm) {
KKASSERT(pfsm->localization != HAMMER_DEF_LOCALIZATION);
ip->obj_id = HAMMER_OBJID_ROOT;
ip->obj_localization = pfsm->localization;
} else {
KKASSERT(dip != NULL);
namekey = hammer_direntry_namekey(dip, name, namelen, &dummy);
ip->obj_id = hammer_alloc_objid(hmp, dip, namekey);
ip->obj_localization = dip->obj_localization;
}
KKASSERT(ip->obj_id != 0);
ip->obj_asof = hmp->asof;
ip->hmp = hmp;
ip->flush_state = HAMMER_FST_IDLE;
ip->flags = HAMMER_INODE_DDIRTY |
HAMMER_INODE_ATIME | HAMMER_INODE_MTIME;
ip->cache[0].ip = ip;
ip->cache[1].ip = ip;
ip->cache[2].ip = ip;
ip->cache[3].ip = ip;
ip->trunc_off = HAMMER_MAX_KEY;
RB_INIT(&ip->rec_tree);
TAILQ_INIT(&ip->target_list);
ip->ino_data.atime = trans->time;
ip->ino_data.mtime = trans->time;
ip->ino_data.size = 0;
ip->ino_data.nlinks = 0;
if (dip) {
ip->ino_data.uflags = dip->ino_data.uflags &
(SF_NOHISTORY|UF_NOHISTORY|UF_NODUMP);
}
ip->ino_leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
ip->ino_leaf.base.localization = ip->obj_localization |
HAMMER_LOCALIZE_INODE;
ip->ino_leaf.base.obj_id = ip->obj_id;
ip->ino_leaf.base.key = 0;
ip->ino_leaf.base.create_tid = 0;
ip->ino_leaf.base.delete_tid = 0;
ip->ino_leaf.base.rec_type = HAMMER_RECTYPE_INODE;
ip->ino_leaf.base.obj_type = hammer_get_obj_type(vap->va_type);
ip->ino_data.obj_type = ip->ino_leaf.base.obj_type;
ip->ino_data.version = HAMMER_INODE_DATA_VERSION;
ip->ino_data.mode = vap->va_mode;
ip->ino_data.ctime = trans->time;
if (trans->hmp->version >= HAMMER_VOL_VERSION_TWO) {
if (ip->ino_leaf.base.obj_type == HAMMER_OBJTYPE_DIRECTORY) {
ip->ino_data.cap_flags |=
HAMMER_INODE_CAP_DIR_LOCAL_INO;
}
}
if (trans->hmp->version >= HAMMER_VOL_VERSION_SIX) {
if (ip->ino_leaf.base.obj_type == HAMMER_OBJTYPE_DIRECTORY) {
ip->ino_data.cap_flags |=
HAMMER_INODE_CAP_DIRHASH_ALG1;
}
}
if (dip)
ip->ino_data.parent_obj_id = dip->ino_leaf.base.obj_id;
switch(ip->ino_leaf.base.obj_type) {
case HAMMER_OBJTYPE_CDEV:
case HAMMER_OBJTYPE_BDEV:
ip->ino_data.rmajor = vap->va_rmajor;
ip->ino_data.rminor = vap->va_rminor;
break;
default:
break;
}
if (dip) {
xuid = hammer_to_unix_xid(&dip->ino_data.uid);
xuid = vop_helper_create_uid(hmp->mp, dip->ino_data.mode,
xuid, cred, &vap->va_mode);
} else {
xuid = 0;
}
ip->ino_data.mode = vap->va_mode;
if (vap->va_vaflags & VA_UID_UUID_VALID)
ip->ino_data.uid = vap->va_uid_uuid;
else if (vap->va_uid != (uid_t)VNOVAL)
hammer_guid_to_uuid(&ip->ino_data.uid, vap->va_uid);
else
hammer_guid_to_uuid(&ip->ino_data.uid, xuid);
if (vap->va_vaflags & VA_GID_UUID_VALID)
ip->ino_data.gid = vap->va_gid_uuid;
else if (vap->va_gid != (gid_t)VNOVAL)
hammer_guid_to_uuid(&ip->ino_data.gid, vap->va_gid);
else if (dip)
ip->ino_data.gid = dip->ino_data.gid;
hammer_ref(&ip->lock);
if (pfsm) {
ip->pfsm = pfsm;
hammer_ref(&pfsm->lock);
error = 0;
} else if (dip->obj_localization == ip->obj_localization) {
ip->pfsm = dip->pfsm;
hammer_ref(&ip->pfsm->lock);
error = 0;
} else {
ip->pfsm = hammer_load_pseudofs(trans,
ip->obj_localization,
&error);
error = 0;
}
if (error) {
hammer_free_inode(ip);
ip = NULL;
} else if (RB_INSERT(hammer_ino_rb_tree, &hmp->rb_inos_root, ip)) {
hpanic("duplicate obj_id %jx", (intmax_t)ip->obj_id);
hammer_free_inode(ip);
}
*ipp = ip;
return(error);
}
static void
hammer_free_inode(hammer_inode_t ip)
{
hammer_mount_t hmp;
hmp = ip->hmp;
KKASSERT(hammer_oneref(&ip->lock));
hammer_uncache_node(&ip->cache[0]);
hammer_uncache_node(&ip->cache[1]);
hammer_uncache_node(&ip->cache[2]);
hammer_uncache_node(&ip->cache[3]);
hammer_inode_wakereclaims(ip);
if (ip->objid_cache)
hammer_clear_objid(ip);
--hammer_count_inodes;
--hmp->count_inodes;
if (ip->pfsm) {
hammer_rel_pseudofs(hmp, ip->pfsm);
ip->pfsm = NULL;
}
kfree(ip, hmp->m_inodes);
}
hammer_pseudofs_inmem_t
hammer_load_pseudofs(hammer_transaction_t trans,
uint32_t localization, int *errorp)
{
hammer_mount_t hmp = trans->hmp;
hammer_inode_t ip;
hammer_pseudofs_inmem_t pfsm;
struct hammer_cursor cursor;
int bytes;
retry:
pfsm = RB_LOOKUP(hammer_pfs_rb_tree, &hmp->rb_pfsm_root, localization);
if (pfsm) {
hammer_ref(&pfsm->lock);
*errorp = 0;
return(pfsm);
}
if (localization) {
ip = hammer_get_inode(trans, NULL, HAMMER_OBJID_ROOT,
HAMMER_MAX_TID,
HAMMER_DEF_LOCALIZATION, 0, errorp);
} else {
ip = NULL;
}
pfsm = kmalloc(sizeof(*pfsm), hmp->m_misc, M_WAITOK | M_ZERO);
pfsm->localization = localization;
pfsm->pfsd.unique_uuid = trans->rootvol->ondisk->vol_fsid;
pfsm->pfsd.shared_uuid = pfsm->pfsd.unique_uuid;
hammer_init_cursor(trans, &cursor, (ip ? &ip->cache[1] : NULL), ip);
cursor.key_beg.localization = HAMMER_DEF_LOCALIZATION |
HAMMER_LOCALIZE_MISC;
cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_PFS;
cursor.key_beg.obj_type = 0;
cursor.key_beg.key = localization;
cursor.asof = HAMMER_MAX_TID;
cursor.flags |= HAMMER_CURSOR_ASOF;
if (ip)
*errorp = hammer_ip_lookup(&cursor);
else
*errorp = hammer_btree_lookup(&cursor);
if (*errorp == 0) {
*errorp = hammer_ip_resolve_data(&cursor);
if (*errorp == 0) {
if (hammer_is_pfs_deleted(&cursor.data->pfsd)) {
*errorp = ENOENT;
} else {
bytes = cursor.leaf->data_len;
if (bytes > sizeof(pfsm->pfsd))
bytes = sizeof(pfsm->pfsd);
bcopy(cursor.data, &pfsm->pfsd, bytes);
}
}
}
hammer_done_cursor(&cursor);
pfsm->fsid_udev = hammer_fsid_to_udev(&pfsm->pfsd.shared_uuid);
hammer_ref(&pfsm->lock);
if (ip)
hammer_rel_inode(ip, 0);
if (RB_INSERT(hammer_pfs_rb_tree, &hmp->rb_pfsm_root, pfsm)) {
kfree(pfsm, hmp->m_misc);
goto retry;
}
return(pfsm);
}
int
hammer_save_pseudofs(hammer_transaction_t trans, hammer_pseudofs_inmem_t pfsm)
{
struct hammer_cursor cursor;
hammer_record_t record;
hammer_inode_t ip;
int error;
ip = hammer_get_inode(trans, NULL, HAMMER_OBJID_ROOT, HAMMER_MAX_TID,
HAMMER_DEF_LOCALIZATION, 0, &error);
retry:
pfsm->fsid_udev = hammer_fsid_to_udev(&pfsm->pfsd.shared_uuid);
hammer_init_cursor(trans, &cursor, &ip->cache[1], ip);
cursor.key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_PFS;
cursor.key_beg.obj_type = 0;
cursor.key_beg.key = pfsm->localization;
cursor.asof = HAMMER_MAX_TID;
cursor.flags |= HAMMER_CURSOR_ASOF;
error = hammer_ip_lookup(&cursor);
if (error == 0 && hammer_cursor_inmem(&cursor)) {
record = cursor.iprec;
if (record->flags & HAMMER_RECF_INTERLOCK_BE) {
KKASSERT(cursor.deadlk_rec == NULL);
hammer_ref(&record->lock);
cursor.deadlk_rec = record;
error = EDEADLK;
} else {
record->flags |= HAMMER_RECF_DELETED_FE;
error = 0;
}
}
if (error == 0 || error == ENOENT) {
record = hammer_alloc_mem_record(ip, sizeof(pfsm->pfsd));
record->type = HAMMER_MEM_RECORD_GENERAL;
record->leaf.base.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
record->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
record->leaf.base.key = pfsm->localization;
record->leaf.data_len = sizeof(pfsm->pfsd);
bcopy(&pfsm->pfsd, record->data, sizeof(pfsm->pfsd));
error = hammer_ip_add_record(trans, record);
}
hammer_done_cursor(&cursor);
if (error == EDEADLK)
goto retry;
hammer_rel_inode(ip, 0);
return(error);
}
int
hammer_mkroot_pseudofs(hammer_transaction_t trans, struct ucred *cred,
hammer_pseudofs_inmem_t pfsm, hammer_inode_t dip)
{
hammer_inode_t ip;
struct vattr vap;
int error;
ip = hammer_get_inode(trans, NULL, HAMMER_OBJID_ROOT, HAMMER_MAX_TID,
pfsm->localization, 0, &error);
if (ip == NULL) {
if (lo_to_pfs(dip->obj_localization) != HAMMER_ROOT_PFSID) {
hmkprintf(trans->hmp,
"Warning: creating a PFS from non-root PFS "
"is not allowed\n");
return(EINVAL);
}
vattr_null(&vap);
vap.va_mode = 0755;
vap.va_type = VDIR;
error = hammer_create_inode(trans, &vap, cred,
NULL, NULL, 0,
pfsm, &ip);
if (error == 0) {
++ip->ino_data.nlinks;
hammer_modify_inode(trans, ip, HAMMER_INODE_DDIRTY);
}
}
if (ip)
hammer_rel_inode(ip, 0);
return(error);
}
static
int
hammer_unload_pseudofs_callback(hammer_inode_t ip, void *data)
{
int res;
hammer_ref(&ip->lock);
if (ip->vp && (ip->vp->v_flag & VPFSROOT)) {
res = 0;
} else {
if (hammer_isactive(&ip->lock) == 2 && ip->vp)
vclean_unlocked(ip->vp);
if (hammer_isactive(&ip->lock) == 1 && ip->vp == NULL)
res = 0;
else
res = -1;
}
hammer_rel_inode(ip, 0);
return(res);
}
int
hammer_unload_pseudofs(hammer_transaction_t trans, uint32_t localization)
{
int res;
int try;
for (try = res = 0; try < 4; ++try) {
res = hammer_ino_rb_tree_RB_SCAN(&trans->hmp->rb_inos_root,
hammer_inode_pfs_cmp,
hammer_unload_pseudofs_callback,
&localization);
if (res == 0 && try > 1)
break;
hammer_flusher_sync(trans->hmp);
}
if (res != 0)
res = ENOTEMPTY;
return(res);
}
void
hammer_rel_pseudofs(hammer_mount_t hmp, hammer_pseudofs_inmem_t pfsm)
{
hammer_rel(&pfsm->lock);
if (hammer_norefs(&pfsm->lock)) {
RB_REMOVE(hammer_pfs_rb_tree, &hmp->rb_pfsm_root, pfsm);
kfree(pfsm, hmp->m_misc);
}
}
static int
hammer_update_inode(hammer_cursor_t cursor, hammer_inode_t ip)
{
hammer_transaction_t trans = cursor->trans;
hammer_record_t record;
int error;
int redirty;
retry:
error = 0;
if ((ip->flags & (HAMMER_INODE_ONDISK|HAMMER_INODE_DELONDISK)) ==
HAMMER_INODE_ONDISK) {
hammer_normalize_cursor(cursor);
cursor->key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_INODE;
cursor->key_beg.obj_id = ip->obj_id;
cursor->key_beg.key = 0;
cursor->key_beg.create_tid = 0;
cursor->key_beg.delete_tid = 0;
cursor->key_beg.rec_type = HAMMER_RECTYPE_INODE;
cursor->key_beg.obj_type = 0;
cursor->asof = ip->obj_asof;
cursor->flags &= ~HAMMER_CURSOR_INITMASK;
cursor->flags |= HAMMER_CURSOR_ASOF;
cursor->flags |= HAMMER_CURSOR_BACKEND;
error = hammer_btree_lookup(cursor);
if (hammer_debug_inode)
hdkprintf("IPDEL %p %08x %d\n", ip, ip->flags, error);
if (error == 0) {
error = hammer_ip_delete_record(cursor, ip, trans->tid);
if (hammer_debug_inode)
hdkprintf("error %d\n", error);
if (error == 0) {
ip->flags |= HAMMER_INODE_DELONDISK;
}
if (cursor->node)
hammer_cache_node(&ip->cache[0], cursor->node);
}
if (error == EDEADLK) {
hammer_done_cursor(cursor);
error = hammer_init_cursor(trans, cursor,
&ip->cache[0], ip);
if (hammer_debug_inode)
hdkprintf("IPDED %p %d\n", ip, error);
if (error == 0)
goto retry;
}
}
if (error == 0 && (ip->flags & HAMMER_INODE_DELETED) == 0) {
record = hammer_alloc_mem_record(ip, 0);
record->type = HAMMER_MEM_RECORD_INODE;
record->flush_state = HAMMER_FST_FLUSH;
record->leaf = ip->sync_ino_leaf;
record->leaf.base.create_tid = trans->tid;
record->leaf.data_len = sizeof(ip->sync_ino_data);
record->leaf.create_ts = trans->time32;
record->data = (void *)&ip->sync_ino_data;
record->flags |= HAMMER_RECF_INTERLOCK_BE;
if ((ip->flags & HAMMER_INODE_WOULDBLOCK) &&
ip->sync_ino_data.size != ip->ino_data.size) {
redirty = 1;
ip->sync_ino_data.size = ip->ino_data.size;
} else {
redirty = 0;
}
for (;;) {
error = hammer_ip_sync_record_cursor(cursor, record);
if (hammer_debug_inode)
hdkprintf("GENREC %p rec %08x %d\n",
ip, record->flags, error);
if (error != EDEADLK)
break;
hammer_done_cursor(cursor);
error = hammer_init_cursor(trans, cursor,
&ip->cache[0], ip);
if (hammer_debug_inode)
hdkprintf("GENREC reinit %d\n", error);
if (error)
break;
}
record->flags |= HAMMER_RECF_COMMITTED;
record->flags &= ~HAMMER_RECF_INTERLOCK_BE;
record->flush_state = HAMMER_FST_IDLE;
++ip->rec_generation;
hammer_rel_mem_record(record);
if (error == 0) {
if (hammer_debug_inode)
hdkprintf("CLEANDELOND %p %08x\n", ip, ip->flags);
ip->sync_flags &= ~(HAMMER_INODE_DDIRTY |
HAMMER_INODE_SDIRTY |
HAMMER_INODE_ATIME |
HAMMER_INODE_MTIME);
ip->flags &= ~HAMMER_INODE_DELONDISK;
if (redirty)
ip->sync_flags |= HAMMER_INODE_DDIRTY;
hammer_sync_lock_sh(trans);
if ((ip->flags & HAMMER_INODE_ONDISK) == 0) {
hammer_modify_volume_field(trans,
trans->rootvol,
vol0_stat_inodes);
++ip->hmp->rootvol->ondisk->vol0_stat_inodes;
hammer_modify_volume_done(trans->rootvol);
ip->flags |= HAMMER_INODE_ONDISK;
if (hammer_debug_inode)
hdkprintf("NOWONDISK %p\n", ip);
}
hammer_sync_unlock(trans);
}
}
if (error == 0 && (ip->flags & HAMMER_INODE_DELETED)) {
ip->sync_flags &= ~(HAMMER_INODE_DDIRTY |
HAMMER_INODE_SDIRTY |
HAMMER_INODE_ATIME |
HAMMER_INODE_MTIME);
}
return(error);
}
static int
hammer_update_itimes(hammer_cursor_t cursor, hammer_inode_t ip)
{
hammer_transaction_t trans = cursor->trans;
int error;
retry:
if ((ip->flags & (HAMMER_INODE_ONDISK|HAMMER_INODE_DELONDISK)) !=
HAMMER_INODE_ONDISK) {
return(0);
}
hammer_normalize_cursor(cursor);
cursor->key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_INODE;
cursor->key_beg.obj_id = ip->obj_id;
cursor->key_beg.key = 0;
cursor->key_beg.create_tid = 0;
cursor->key_beg.delete_tid = 0;
cursor->key_beg.rec_type = HAMMER_RECTYPE_INODE;
cursor->key_beg.obj_type = 0;
cursor->asof = ip->obj_asof;
cursor->flags &= ~HAMMER_CURSOR_INITMASK;
cursor->flags |= HAMMER_CURSOR_ASOF;
cursor->flags |= HAMMER_CURSOR_GET_DATA;
cursor->flags |= HAMMER_CURSOR_BACKEND;
error = hammer_btree_lookup(cursor);
if (error == 0) {
hammer_cache_node(&ip->cache[0], cursor->node);
if (ip->sync_flags & HAMMER_INODE_MTIME) {
hammer_sync_lock_sh(trans);
hammer_modify_buffer(trans, cursor->data_buffer,
&cursor->data->inode.mtime,
sizeof(cursor->data->inode.atime) +
sizeof(cursor->data->inode.mtime));
cursor->data->inode.atime = ip->sync_ino_data.atime;
cursor->data->inode.mtime = ip->sync_ino_data.mtime;
hammer_modify_buffer_done(cursor->data_buffer);
hammer_sync_unlock(trans);
} else if (ip->sync_flags & HAMMER_INODE_ATIME) {
hammer_sync_lock_sh(trans);
hammer_modify_buffer_noundo(trans, cursor->data_buffer);
cursor->data->inode.atime = ip->sync_ino_data.atime;
hammer_modify_buffer_done(cursor->data_buffer);
hammer_sync_unlock(trans);
}
ip->sync_flags &= ~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME);
}
if (error == EDEADLK) {
hammer_done_cursor(cursor);
error = hammer_init_cursor(trans, cursor, &ip->cache[0], ip);
if (error == 0)
goto retry;
}
return(error);
}
void
hammer_rel_inode(hammer_inode_t ip, int flush)
{
for (;;) {
if (hammer_oneref(&ip->lock)) {
KKASSERT(ip->vp == NULL);
hammer_inode_unloadable_check(ip, 0);
if (ip->flags & HAMMER_INODE_MODMASK) {
hammer_flush_inode(ip, 0);
} else if (hammer_oneref(&ip->lock)) {
hammer_unload_inode(ip);
break;
}
} else {
if (flush)
hammer_flush_inode(ip, 0);
KKASSERT(hammer_isactive(&ip->lock) >= 1);
if (hammer_isactive(&ip->lock) > 1) {
hammer_rel(&ip->lock);
break;
}
}
}
}
static int
hammer_unload_inode(hammer_inode_t ip)
{
hammer_mount_t hmp = ip->hmp;
KASSERT(hammer_oneref(&ip->lock),
("hammer_unload_inode: %d refs", hammer_isactive(&ip->lock)));
KKASSERT(ip->vp == NULL);
KKASSERT(ip->flush_state == HAMMER_FST_IDLE);
KKASSERT(ip->cursor_ip_refs == 0);
KKASSERT(hammer_notlocked(&ip->lock));
KKASSERT((ip->flags & HAMMER_INODE_MODMASK) == 0);
KKASSERT(RB_EMPTY(&ip->rec_tree));
KKASSERT(TAILQ_EMPTY(&ip->target_list));
if (ip->flags & HAMMER_INODE_RDIRTY) {
RB_REMOVE(hammer_redo_rb_tree, &hmp->rb_redo_root, ip);
ip->flags &= ~HAMMER_INODE_RDIRTY;
}
RB_REMOVE(hammer_ino_rb_tree, &hmp->rb_inos_root, ip);
hammer_free_inode(ip);
return(0);
}
int
hammer_destroy_inode_callback(hammer_inode_t ip, void *data __unused)
{
hammer_record_t rec;
while ((rec = TAILQ_FIRST(&ip->target_list)) != NULL) {
TAILQ_REMOVE(&ip->target_list, rec, target_entry);
rec->target_ip = NULL;
if (rec->flush_state == HAMMER_FST_SETUP)
rec->flush_state = HAMMER_FST_IDLE;
}
while ((rec = RB_ROOT(&ip->rec_tree)) != NULL) {
if (rec->flush_state == HAMMER_FST_FLUSH)
--rec->flush_group->refs;
else
hammer_ref(&rec->lock);
KKASSERT(hammer_oneref(&rec->lock));
rec->flush_state = HAMMER_FST_IDLE;
rec->flush_group = NULL;
rec->flags |= HAMMER_RECF_DELETED_FE;
rec->flags |= HAMMER_RECF_DELETED_BE;
++ip->rec_generation;
hammer_rel_mem_record(rec);
}
ip->flags &= ~HAMMER_INODE_MODMASK;
ip->sync_flags &= ~HAMMER_INODE_MODMASK;
KKASSERT(ip->vp == NULL);
switch(ip->flush_state) {
case HAMMER_FST_FLUSH:
RB_REMOVE(hammer_fls_rb_tree, &ip->flush_group->flush_tree, ip);
--ip->flush_group->refs;
ip->flush_group = NULL;
case HAMMER_FST_SETUP:
hammer_rel(&ip->lock);
ip->flush_state = HAMMER_FST_IDLE;
case HAMMER_FST_IDLE:
break;
}
if (ip->vp) {
hdkprintf("Unexpected vnode association ip %p vp %p\n",
ip, ip->vp);
ip->vp->v_data = NULL;
ip->vp = NULL;
} else {
hammer_ref(&ip->lock);
}
hammer_unload_inode(ip);
return(0);
}
int
hammer_reload_inode(hammer_inode_t ip, void *arg __unused)
{
hammer_mount_t hmp = ip->hmp;
if (hmp->ronly || hmp->asof != HAMMER_MAX_TID)
ip->flags |= HAMMER_INODE_RO;
else
ip->flags &= ~HAMMER_INODE_RO;
return(0);
}
void
hammer_modify_inode(hammer_transaction_t trans, hammer_inode_t ip, int flags)
{
KKASSERT(ip->hmp->ronly != 1 ||
(flags & (HAMMER_INODE_DDIRTY | HAMMER_INODE_XDIRTY |
HAMMER_INODE_SDIRTY |
HAMMER_INODE_BUFS | HAMMER_INODE_DELETED |
HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) == 0);
if ((ip->flags & HAMMER_INODE_RSV_INODES) == 0) {
ip->flags |= HAMMER_INODE_RSV_INODES;
++ip->hmp->rsv_inodes;
}
if (trans &&
(ip->flags & HAMMER_INODE_MODMASK) == 0 &&
(flags & HAMMER_INODE_MODMASK)) {
trans->flags |= HAMMER_TRANSF_NEWINODE;
}
if (flags & HAMMER_INODE_MODMASK)
hammer_inode_dirty(ip);
ip->flags |= flags;
}
int
hammer_update_atime_quick(hammer_inode_t ip)
{
struct timespec ts;
int res = -1;
if ((ip->flags & HAMMER_INODE_RO) ||
(ip->hmp->mp->mnt_flag & MNT_NOATIME)) {
res = 0;
} else if (ip->flags & HAMMER_INODE_ATIME) {
vfs_timestamp(&ts);
hammer_lock_ex(&ip->lock);
if (ip->flags & HAMMER_INODE_ATIME) {
ip->ino_data.atime =
(unsigned long)ts.tv_sec * 1000000ULL +
ts.tv_nsec / 1000;
res = 0;
}
hammer_unlock(&ip->lock);
}
return res;
}
void
hammer_flush_inode(hammer_inode_t ip, int flags)
{
hammer_mount_t hmp;
hammer_flush_group_t flg;
int good;
hmp = ip->hmp;
while ((flg = hmp->fill_flush_group) != NULL) {
KKASSERT(flg->running == 0);
if (flg->total_count + flg->refs <= ip->hmp->undo_rec_limit &&
flg->total_count <= hammer_autoflush) {
break;
}
hmp->fill_flush_group = TAILQ_NEXT(flg, flush_entry);
hammer_flusher_async(ip->hmp, flg);
}
if (flg == NULL) {
flg = kmalloc(sizeof(*flg), hmp->m_misc, M_WAITOK|M_ZERO);
flg->seq = hmp->flusher.next++;
if (hmp->next_flush_group == NULL)
hmp->next_flush_group = flg;
if (hmp->fill_flush_group == NULL)
hmp->fill_flush_group = flg;
RB_INIT(&flg->flush_tree);
TAILQ_INSERT_TAIL(&hmp->flush_group_list, flg, flush_entry);
}
if ((ip->flags & HAMMER_INODE_MODMASK) == 0) {
if (ip->flush_state == HAMMER_FST_SETUP &&
TAILQ_EMPTY(&ip->target_list)) {
ip->flush_state = HAMMER_FST_IDLE;
hammer_rel_inode(ip, 0);
}
if (ip->flush_state == HAMMER_FST_IDLE)
return;
}
switch(ip->flush_state) {
case HAMMER_FST_IDLE:
hammer_flush_inode_core(ip, flg, flags);
break;
case HAMMER_FST_SETUP:
good = hammer_setup_parent_inodes(ip, 0, flg);
if (good >= 0) {
hammer_flush_inode_core(ip, flg, flags);
} else {
ip->flags |= HAMMER_INODE_CONN_DOWN |
HAMMER_INODE_REFLUSH;
if (flags & HAMMER_FLUSH_SIGNAL) {
ip->flags |= HAMMER_INODE_RESIGNAL;
hammer_flusher_async(ip->hmp, flg);
}
}
break;
case HAMMER_FST_FLUSH:
if ((ip->flags & HAMMER_INODE_REFLUSH) == 0)
ip->flags |= HAMMER_INODE_REFLUSH;
if (flags & HAMMER_FLUSH_SIGNAL) {
ip->flags |= HAMMER_INODE_RESIGNAL;
hammer_flusher_async(ip->hmp, flg);
}
break;
}
}
static int
hammer_setup_parent_inodes(hammer_inode_t ip, int depth,
hammer_flush_group_t flg)
{
hammer_record_t depend;
int good;
int r;
if (depth == 20 && TAILQ_FIRST(&ip->target_list)) {
if (hammer_debug_general & 0x10000)
hkrateprintf(&hammer_gen_krate,
"Warning: depth limit reached on "
"setup recursion, inode %p %016jx\n",
ip, (intmax_t)ip->obj_id);
return(-2);
}
good = 0;
TAILQ_FOREACH(depend, &ip->target_list, target_entry) {
r = hammer_setup_parent_inodes_helper(depend, depth, flg);
KKASSERT(depend->target_ip == ip);
if (r < 0 && good == 0)
good = -1;
if (r > 0)
good = 1;
if (r == -2)
break;
}
return(good);
}
static int
hammer_setup_parent_inodes_helper(hammer_record_t record, int depth,
hammer_flush_group_t flg)
{
hammer_inode_t pip;
int good;
KKASSERT(record->flush_state != HAMMER_FST_IDLE);
pip = record->ip;
if (record->flush_state == HAMMER_FST_FLUSH) {
if (record->flush_group != flg) {
pip->flags |= HAMMER_INODE_REFLUSH;
record->target_ip->flags |= HAMMER_INODE_CONN_DOWN;
return(-1);
}
if (record->type == HAMMER_MEM_RECORD_ADD)
return(1);
return(0);
}
KKASSERT(record->flush_state == HAMMER_FST_SETUP);
good = hammer_setup_parent_inodes(pip, depth + 1, flg);
if (good < 0) {
pip->flags |= HAMMER_INODE_REFLUSH;
record->target_ip->flags |= HAMMER_INODE_CONN_DOWN;
return(good);
}
if (pip->flush_state != HAMMER_FST_FLUSH)
hammer_flush_inode_core(pip, flg, HAMMER_FLUSH_RECURSION);
KKASSERT(pip->flush_state == HAMMER_FST_FLUSH);
if (record->flush_state == HAMMER_FST_FLUSH) {
if (record->type == HAMMER_MEM_RECORD_ADD)
return(1);
return(0);
}
KKASSERT(record->flush_state == HAMMER_FST_SETUP);
#if 0
if (record->type == HAMMER_MEM_RECORD_DEL &&
(record->target_ip->flags & (HAMMER_INODE_DELETED|HAMMER_INODE_DELONDISK)) == 0) {
record->target_ip->flags |= HAMMER_INODE_REFLUSH;
return(-1);
} else
#endif
if (pip->flush_group == flg) {
record->flush_state = HAMMER_FST_FLUSH;
record->flush_group = flg;
++record->flush_group->refs;
hammer_ref(&record->lock);
if (record->type == HAMMER_MEM_RECORD_ADD)
return(1);
return(0);
} else {
pip->flags |= HAMMER_INODE_REFLUSH;
record->target_ip->flags |= HAMMER_INODE_CONN_DOWN;
return(-1);
}
}
static void
hammer_flush_inode_core(hammer_inode_t ip, hammer_flush_group_t flg, int flags)
{
hammer_mount_t hmp = ip->hmp;
int go_count;
KKASSERT(ip->flush_state != HAMMER_FST_FLUSH);
if (ip->flush_state == HAMMER_FST_IDLE)
hammer_ref(&ip->lock);
ip->flush_state = HAMMER_FST_FLUSH;
ip->flush_group = flg;
++hmp->flusher.group_lock;
++hmp->count_iqueued;
++hammer_count_iqueued;
++flg->total_count;
hammer_redo_fifo_start_flush(ip);
#if 0
KKASSERT((ip->flags & HAMMER_INODE_VHELD) == 0);
if (ip->vp && (ip->vp->v_flag & VINACTIVE) == 0) {
ip->flags |= HAMMER_INODE_VHELD;
vref(ip->vp);
}
#endif
KKASSERT((ip->flags & HAMMER_INODE_WOULDBLOCK) == 0);
if (flags & HAMMER_FLUSH_RECURSION) {
go_count = 1;
#if 0
} else if (ip->flags & HAMMER_INODE_WOULDBLOCK) {
#if 0
go_count = RB_SCAN(hammer_rec_rb_tree, &ip->rec_tree, NULL,
hammer_syncgrp_child_callback, NULL);
#endif
go_count = 1;
#endif
} else {
go_count = RB_SCAN(hammer_rec_rb_tree, &ip->rec_tree, NULL,
hammer_setup_child_callback, NULL);
}
if (go_count == 0) {
if ((ip->flags & HAMMER_INODE_MODMASK_NOXDIRTY) == 0) {
--hmp->count_iqueued;
--hammer_count_iqueued;
--flg->total_count;
ip->flush_state = HAMMER_FST_SETUP;
ip->flush_group = NULL;
if (flags & HAMMER_FLUSH_SIGNAL) {
ip->flags |= HAMMER_INODE_REFLUSH |
HAMMER_INODE_RESIGNAL;
} else {
ip->flags |= HAMMER_INODE_REFLUSH;
}
#if 0
if (ip->flags & HAMMER_INODE_VHELD) {
ip->flags &= ~HAMMER_INODE_VHELD;
vrele(ip->vp);
}
#endif
ip->flags |= HAMMER_INODE_REFLUSH;
if (--hmp->flusher.group_lock == 0)
wakeup(&hmp->flusher.group_lock);
return;
}
}
if (ip->flags & HAMMER_INODE_TRUNCATED) {
KKASSERT((ip->sync_flags & HAMMER_INODE_TRUNCATED) == 0);
ip->sync_trunc_off = ip->trunc_off;
ip->trunc_off = HAMMER_MAX_KEY;
ip->flags &= ~HAMMER_INODE_TRUNCATED;
ip->sync_flags |= HAMMER_INODE_TRUNCATED;
if (ip->save_trunc_off > ip->sync_trunc_off)
ip->save_trunc_off = ip->sync_trunc_off;
}
ip->sync_flags |= (ip->flags & HAMMER_INODE_MODMASK &
~HAMMER_INODE_TRUNCATED);
ip->sync_ino_leaf = ip->ino_leaf;
ip->sync_ino_data = ip->ino_data;
ip->flags &= ~HAMMER_INODE_MODMASK | HAMMER_INODE_TRUNCATED;
KKASSERT(flg->running == 0);
RB_INSERT(hammer_fls_rb_tree, &flg->flush_tree, ip);
if (--hmp->flusher.group_lock == 0)
wakeup(&hmp->flusher.group_lock);
if (flg->total_count >= hammer_autoflush ||
flg->total_count >= hammer_limit_reclaims / 4) {
if (hmp->fill_flush_group == flg)
hmp->fill_flush_group = TAILQ_NEXT(flg, flush_entry);
hammer_flusher_async(hmp, flg);
}
}
static int
hammer_setup_child_callback(hammer_record_t rec, void *data)
{
hammer_flush_group_t flg;
hammer_inode_t target_ip;
hammer_inode_t ip;
int r;
if (rec->flags & (HAMMER_RECF_DELETED_FE | HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED)) {
if (rec->flush_state == HAMMER_FST_FLUSH) {
KKASSERT(rec->flush_group == rec->ip->flush_group);
r = 1;
} else {
r = 0;
}
return(r);
}
ip = rec->ip;
flg = ip->flush_group;
r = 0;
switch(rec->flush_state) {
case HAMMER_FST_IDLE:
KKASSERT(rec->target_ip == NULL);
rec->flush_state = HAMMER_FST_FLUSH;
rec->flush_group = flg;
++flg->refs;
hammer_ref(&rec->lock);
r = 1;
break;
case HAMMER_FST_SETUP:
target_ip = rec->target_ip;
KKASSERT(target_ip != NULL);
KKASSERT(target_ip->flush_state != HAMMER_FST_IDLE);
if (target_ip->flush_state == HAMMER_FST_FLUSH) {
if (rec->type == HAMMER_MEM_RECORD_ADD)
ip->flags |= HAMMER_INODE_REFLUSH;
else
target_ip->flags |= HAMMER_INODE_REFLUSH;
break;
}
#if 0
if ((target_ip->flags & HAMMER_INODE_RECLAIM) == 0 &&
(target_ip->flags & HAMMER_INODE_CONN_DOWN) == 0) {
} else
#endif
if (flg->total_count + flg->refs >
ip->hmp->undo_rec_limit) {
ip->flags |= HAMMER_INODE_RESIGNAL |
HAMMER_INODE_REFLUSH;
r = -1;
} else if (rec->type == HAMMER_MEM_RECORD_ADD) {
rec->flush_state = HAMMER_FST_FLUSH;
rec->flush_group = flg;
++flg->refs;
hammer_ref(&rec->lock);
hammer_flush_inode_core(target_ip, flg,
HAMMER_FLUSH_RECURSION);
r = 1;
} else {
rec->flush_state = HAMMER_FST_FLUSH;
rec->flush_group = flg;
++flg->refs;
hammer_ref(&rec->lock);
hammer_flush_inode_core(target_ip, flg,
HAMMER_FLUSH_RECURSION);
r = 1;
}
break;
case HAMMER_FST_FLUSH:
KKASSERT(
rec->ip->ino_data.obj_type == HAMMER_OBJTYPE_DIRECTORY ||
rec->flush_group == flg);
r = 1;
break;
}
return(r);
}
#if 0
static int
hammer_syncgrp_child_callback(hammer_record_t rec, void *data)
{
hammer_inode_t ip = rec->ip;
switch(rec->flush_state) {
case HAMMER_FST_FLUSH:
KKASSERT(rec->flush_group == ip->flush_group);
break;
default:
break;
}
return(0);
}
#endif
void
hammer_wait_inode(hammer_inode_t ip)
{
while (ip->flush_state == HAMMER_FST_FLUSH ||
(ip->flush_state == HAMMER_FST_SETUP &&
(ip->flags & HAMMER_INODE_RESIGNAL))) {
if (ip->hmp->flags & HAMMER_MOUNT_CRITICAL_ERROR)
break;
if (ip->flush_state == HAMMER_FST_FLUSH) {
KKASSERT(ip->flush_group);
if (ip->flush_group->closed == 0) {
if (hammer_debug_inode) {
hkprintf("debug: forcing "
"async flush ip %016jx\n",
(intmax_t)ip->obj_id);
}
hammer_flusher_async(ip->hmp, ip->flush_group);
continue;
}
}
ip->flags |= HAMMER_INODE_FLUSHW;
tsleep(&ip->flags, 0, "hmrwin", 0);
}
#if 0
if (ip->flush_state == HAMMER_FST_SETUP)
hammer_flush_inode(ip, HAMMER_FLUSH_SIGNAL);
#endif
}
void
hammer_sync_inode_done(hammer_inode_t ip, int error)
{
hammer_mount_t hmp;
int dorel;
KKASSERT(ip->flush_state == HAMMER_FST_FLUSH);
hmp = ip->hmp;
if (ip->sync_flags & HAMMER_INODE_MODMASK)
ip->flags |= HAMMER_INODE_REFLUSH;
ip->error = error;
ip->flags |= ip->sync_flags & ~HAMMER_INODE_TRUNCATED;
ip->sync_flags &= HAMMER_INODE_TRUNCATED;
if (ip->ino_data.nlinks != ip->sync_ino_data.nlinks)
ip->flags |= HAMMER_INODE_DDIRTY;
if (ip->vp && RB_ROOT(&ip->vp->v_rbdirty_tree)) {
ip->flags |= HAMMER_INODE_BUFS;
}
hammer_redo_fifo_end_flush(ip);
KKASSERT((RB_EMPTY(&ip->rec_tree) &&
(ip->flags & HAMMER_INODE_XDIRTY) == 0) ||
(!RB_EMPTY(&ip->rec_tree) &&
(ip->flags & HAMMER_INODE_XDIRTY) != 0));
if (ip->flags & HAMMER_INODE_MODMASK) {
if (ip->vp == NULL)
ip->flags |= HAMMER_INODE_REFLUSH;
} else {
ip->flags &= ~HAMMER_INODE_REFLUSH;
}
if (ip->flags & HAMMER_INODE_MODMASK) {
hammer_lock_ex(&ip->lock);
hammer_inode_dirty(ip);
hammer_unlock(&ip->lock);
}
if (ip->flags & HAMMER_INODE_WOULDBLOCK) {
ip->flags &= ~HAMMER_INODE_WOULDBLOCK;
dorel = 0;
} else {
RB_REMOVE(hammer_fls_rb_tree, &ip->flush_group->flush_tree, ip);
ip->flush_group = NULL;
#if 0
if (ip->flags & HAMMER_INODE_VHELD) {
ip->flags &= ~HAMMER_INODE_VHELD;
vrele(ip->vp);
}
#endif
--hmp->count_iqueued;
--hammer_count_iqueued;
if (TAILQ_EMPTY(&ip->target_list) && RB_EMPTY(&ip->rec_tree)) {
ip->flush_state = HAMMER_FST_IDLE;
dorel = 1;
} else {
ip->flush_state = HAMMER_FST_SETUP;
dorel = 0;
}
if (ip->flags & HAMMER_INODE_FLUSHW) {
ip->flags &= ~HAMMER_INODE_FLUSHW;
wakeup(&ip->flags);
}
if (ip->flags & HAMMER_INODE_REFLUSH) {
ip->flags &= ~HAMMER_INODE_REFLUSH;
if (ip->flags & HAMMER_INODE_RESIGNAL) {
ip->flags &= ~HAMMER_INODE_RESIGNAL;
hammer_flush_inode(ip, HAMMER_FLUSH_SIGNAL);
} else {
hammer_flush_inode(ip, 0);
}
}
}
if (TAILQ_EMPTY(&ip->target_list))
ip->flags &= ~HAMMER_INODE_CONN_DOWN;
if ((ip->flags & HAMMER_INODE_MODMASK) == 0 &&
(ip->flags & HAMMER_INODE_RSV_INODES)) {
ip->flags &= ~HAMMER_INODE_RSV_INODES;
--hmp->rsv_inodes;
}
ip->flags &= ~HAMMER_INODE_SLAVEFLUSH;
if (dorel)
hammer_rel_inode(ip, 0);
}
static int
hammer_sync_record_callback(hammer_record_t record, void *data)
{
hammer_cursor_t cursor = data;
hammer_transaction_t trans = cursor->trans;
hammer_mount_t hmp = trans->hmp;
int error;
++hammer_stats_record_iterations;
if (record->flush_state != HAMMER_FST_FLUSH)
return(0);
if (record->flush_group != record->ip->flush_group) {
hdkprintf("rec %p ip %p bad flush group %p %p\n",
record,
record->ip,
record->flush_group,
record->ip->flush_group);
if (hammer_debug_critical)
Debugger("blah2");
return(0);
}
KKASSERT(record->flush_group == record->ip->flush_group);
KKASSERT((record->flags & HAMMER_RECF_INTERLOCK_BE) == 0);
record->flags |= HAMMER_RECF_INTERLOCK_BE;
if (record->flags & (HAMMER_RECF_DELETED_BE | HAMMER_RECF_COMMITTED)) {
error = 0;
goto done;
}
if (record->ip->sync_flags & HAMMER_INODE_DELETING) {
switch(record->type) {
case HAMMER_MEM_RECORD_DATA:
case HAMMER_MEM_RECORD_GENERAL:
record->flags |= HAMMER_RECF_DELETED_BE;
++record->ip->rec_generation;
error = 0;
goto done;
case HAMMER_MEM_RECORD_ADD:
hpanic("illegal add during inode deletion record %p",
record);
break;
case HAMMER_MEM_RECORD_INODE:
hpanic("attempt to sync inode record %p?", record);
break;
case HAMMER_MEM_RECORD_DEL:
break;
}
}
if (record->flags & HAMMER_RECF_DELETED_FE) {
if (record->type == HAMMER_MEM_RECORD_ADD) {
record->flags |= HAMMER_RECF_CONVERT_DELETE;
} else {
KKASSERT(record->type != HAMMER_MEM_RECORD_DEL);
record->flags |= HAMMER_RECF_DELETED_BE;
++record->ip->rec_generation;
error = 0;
goto done;
}
}
if (record->type != HAMMER_MEM_RECORD_DEL) {
record->leaf.base.create_tid = trans->tid;
record->leaf.create_ts = trans->time32;
}
if (record->flags & HAMMER_RECF_REDO) {
KKASSERT(record->type == HAMMER_MEM_RECORD_DATA);
hammer_generate_redo(trans, record->ip,
record->leaf.base.key -
record->leaf.data_len,
HAMMER_REDO_TERM_WRITE,
NULL,
record->leaf.data_len);
}
for (;;) {
error = hammer_ip_sync_record_cursor(cursor, record);
if (error != EDEADLK)
break;
hammer_done_cursor(cursor);
error = hammer_init_cursor(trans, cursor, &record->ip->cache[0],
record->ip);
if (error)
break;
}
record->flags &= ~HAMMER_RECF_CONVERT_DELETE;
if (error)
error = -error;
done:
hammer_flush_record_done(record, error);
if (hammer_flusher_meta_limit(hmp) ||
vm_paging_severe()) {
hammer_unlock_cursor(cursor);
hammer_flusher_finalize(trans, 0);
hammer_lock_cursor(cursor);
}
return(error);
}
int
hammer_sync_inode(hammer_transaction_t trans, hammer_inode_t ip)
{
struct hammer_cursor cursor;
hammer_node_t tmp_node;
hammer_record_t depend;
hammer_record_t next;
int error, tmp_error;
uint64_t nlinks;
if ((ip->sync_flags & HAMMER_INODE_MODMASK) == 0)
return(0);
error = hammer_init_cursor(trans, &cursor, &ip->cache[1], ip);
if (error)
goto done;
nlinks = ip->ino_data.nlinks;
next = TAILQ_FIRST(&ip->target_list);
while ((depend = next) != NULL) {
next = TAILQ_NEXT(depend, target_entry);
if (depend->flush_state == HAMMER_FST_FLUSH &&
depend->flush_group == ip->flush_group) {
if (depend->flags & HAMMER_RECF_DELETED_FE) {
++nlinks;
} else {
TAILQ_REMOVE(&ip->target_list, depend,
target_entry);
depend->target_ip = NULL;
}
} else if ((depend->flags & HAMMER_RECF_DELETED_FE) == 0) {
KKASSERT((depend->flags & HAMMER_RECF_DELETED_BE) == 0);
switch(depend->type) {
case HAMMER_MEM_RECORD_ADD:
--nlinks;
break;
case HAMMER_MEM_RECORD_DEL:
++nlinks;
break;
default:
break;
}
}
}
if (ip->sync_ino_data.nlinks != nlinks) {
KKASSERT((int64_t)nlinks >= 0);
ip->sync_ino_data.nlinks = nlinks;
ip->sync_flags |= HAMMER_INODE_DDIRTY;
}
if (ip->sync_flags & HAMMER_INODE_TRUNCATED) {
off_t trunc_off;
off_t aligned_trunc_off;
int blkmask;
trunc_off = ip->sync_trunc_off;
blkmask = hammer_blocksize(trunc_off) - 1;
aligned_trunc_off = (trunc_off + blkmask) & ~(int64_t)blkmask;
error = hammer_ip_delete_range(&cursor, ip,
aligned_trunc_off,
HAMMER_MAX_KEY, 2);
if (error == EWOULDBLOCK) {
ip->flags |= HAMMER_INODE_WOULDBLOCK;
error = 0;
goto defer_buffer_flush;
}
if (error)
goto done;
if (trans->hmp->version >= HAMMER_VOL_VERSION_FOUR &&
(ip->flags & HAMMER_INODE_RDIRTY)) {
hammer_generate_redo(trans, ip, aligned_trunc_off,
HAMMER_REDO_TERM_TRUNC,
NULL, 0);
}
ip->sync_flags &= ~HAMMER_INODE_TRUNCATED;
} else {
error = 0;
}
if (error == 0) {
tmp_error = RB_SCAN(hammer_rec_rb_tree, &ip->rec_tree, NULL,
hammer_sync_record_callback, &cursor);
if (tmp_error < 0)
tmp_error = -error;
if (tmp_error)
error = tmp_error;
}
hammer_cache_node(&ip->cache[1], cursor.node);
if (error == 0) {
tmp_node = hammer_ref_node_safe(trans, &ip->cache[0], &error);
if (tmp_node) {
hammer_cursor_downgrade(&cursor);
hammer_lock_sh(&tmp_node->lock);
if ((tmp_node->flags & HAMMER_NODE_DELETED) == 0)
hammer_cursor_seek(&cursor, tmp_node, 0);
hammer_unlock(&tmp_node->lock);
hammer_rel_node(tmp_node);
}
error = 0;
}
if (error == 0 && ip->sync_ino_data.nlinks == 0 &&
RB_EMPTY(&ip->rec_tree) &&
(ip->sync_flags & HAMMER_INODE_DELETING) &&
(ip->flags & HAMMER_INODE_DELETED) == 0) {
int count1 = 0;
error = hammer_ip_delete_clean(&cursor, ip, &count1);
if (error == 0) {
ip->flags |= HAMMER_INODE_DELETED;
ip->sync_flags &= ~HAMMER_INODE_DELETING;
ip->sync_flags &= ~HAMMER_INODE_TRUNCATED;
KKASSERT(RB_EMPTY(&ip->rec_tree));
ip->ino_leaf.base.delete_tid = trans->tid;
ip->sync_ino_leaf.base.delete_tid = trans->tid;
ip->ino_leaf.delete_ts = trans->time32;
ip->sync_ino_leaf.delete_ts = trans->time32;
hammer_sync_lock_sh(trans);
if (ip->flags & HAMMER_INODE_ONDISK) {
hammer_modify_volume_field(trans,
trans->rootvol,
vol0_stat_inodes);
--ip->hmp->rootvol->ondisk->vol0_stat_inodes;
hammer_modify_volume_done(trans->rootvol);
}
hammer_sync_unlock(trans);
}
}
if (error)
goto done;
ip->sync_flags &= ~HAMMER_INODE_BUFS;
defer_buffer_flush:
switch(ip->flags & (HAMMER_INODE_DELETED | HAMMER_INODE_ONDISK)) {
case HAMMER_INODE_DELETED|HAMMER_INODE_ONDISK:
ip->sync_flags &= ~(HAMMER_INODE_DDIRTY | HAMMER_INODE_XDIRTY |
HAMMER_INODE_SDIRTY |
HAMMER_INODE_ATIME | HAMMER_INODE_MTIME |
HAMMER_INODE_DELETING);
break;
case HAMMER_INODE_DELETED:
ip->sync_flags &= ~(HAMMER_INODE_DDIRTY | HAMMER_INODE_XDIRTY |
HAMMER_INODE_SDIRTY |
HAMMER_INODE_ATIME | HAMMER_INODE_MTIME |
HAMMER_INODE_DELETING);
while (RB_ROOT(&ip->rec_tree)) {
hammer_record_t record = RB_ROOT(&ip->rec_tree);
hammer_ref(&record->lock);
KKASSERT(hammer_oneref(&record->lock));
record->flags |= HAMMER_RECF_DELETED_BE;
++record->ip->rec_generation;
hammer_rel_mem_record(record);
}
break;
case HAMMER_INODE_ONDISK:
break;
default:
ip->ino_leaf.base.create_tid = trans->tid;
ip->ino_leaf.create_ts = trans->time32;
ip->sync_ino_leaf.base.create_tid = trans->tid;
ip->sync_ino_leaf.create_ts = trans->time32;
ip->sync_flags |= HAMMER_INODE_DDIRTY;
break;
}
if (ip->flags & HAMMER_INODE_DELETED) {
error = hammer_update_inode(&cursor, ip);
} else
if (!(ip->sync_flags & (HAMMER_INODE_DDIRTY | HAMMER_INODE_SDIRTY)) &&
(ip->sync_flags & (HAMMER_INODE_ATIME | HAMMER_INODE_MTIME))) {
error = hammer_update_itimes(&cursor, ip);
} else
if (ip->sync_flags & (HAMMER_INODE_DDIRTY | HAMMER_INODE_SDIRTY |
HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) {
error = hammer_update_inode(&cursor, ip);
}
done:
if (ip->flags & HAMMER_INODE_MODMASK)
hammer_inode_dirty(ip);
if (error) {
hammer_critical_error(ip->hmp, ip, error,
"while syncing inode");
}
hammer_done_cursor(&cursor);
return(error);
}
void
hammer_inode_unloadable_check(hammer_inode_t ip, int getvp)
{
struct vnode *vp;
if (ip->ino_data.nlinks == 0 &&
((ip->flags | ip->sync_flags) & (HAMMER_INODE_RO|HAMMER_INODE_DELETING|HAMMER_INODE_DELETED)) == 0) {
ip->flags |= HAMMER_INODE_DELETING;
ip->flags |= HAMMER_INODE_TRUNCATED;
ip->trunc_off = 0;
vp = NULL;
if (getvp) {
if (hammer_get_vnode(ip, &vp) != 0)
return;
}
if (ip->vp)
nvtruncbuf(ip->vp, 0, HAMMER_BUFSIZE, 0, 0);
if (ip->flags & HAMMER_INODE_MODMASK)
hammer_inode_dirty(ip);
if (getvp)
vput(vp);
}
}
void
hammer_test_inode(hammer_inode_t ip)
{
if (ip->flags & HAMMER_INODE_REFLUSH) {
ip->flags &= ~HAMMER_INODE_REFLUSH;
hammer_ref(&ip->lock);
if (ip->flags & HAMMER_INODE_RESIGNAL) {
ip->flags &= ~HAMMER_INODE_RESIGNAL;
hammer_flush_inode(ip, HAMMER_FLUSH_SIGNAL);
} else {
hammer_flush_inode(ip, 0);
}
hammer_rel_inode(ip, 0);
}
}
static void
hammer_inode_wakereclaims(hammer_inode_t ip)
{
struct hammer_reclaim *reclaim;
hammer_mount_t hmp = ip->hmp;
if ((ip->flags & HAMMER_INODE_RECLAIM) == 0)
return;
--hammer_count_reclaims;
--hmp->count_reclaims;
ip->flags &= ~HAMMER_INODE_RECLAIM;
if ((reclaim = TAILQ_FIRST(&hmp->reclaim_list)) != NULL) {
KKASSERT(reclaim->count > 0);
if (--reclaim->count == 0) {
TAILQ_REMOVE(&hmp->reclaim_list, reclaim, entry);
wakeup(reclaim);
}
}
}
void
hammer_inode_waitreclaims(hammer_transaction_t trans)
{
hammer_mount_t hmp = trans->hmp;
struct hammer_reclaim reclaim;
int lower_limit;
if (curthread->td_proc) {
struct hammer_inostats *stats;
stats = hammer_inode_inostats(hmp, curthread->td_proc->p_pid);
++stats->count;
if (stats->count > hammer_limit_reclaims / 2)
stats->count = hammer_limit_reclaims / 2;
lower_limit = hammer_limit_reclaims - stats->count;
if (hammer_debug_general & 0x10000) {
hdkprintf("pid %5d limit %d\n",
(int)curthread->td_proc->p_pid, lower_limit);
}
} else {
lower_limit = hammer_limit_reclaims * 3 / 4;
}
if (hmp->count_reclaims >= lower_limit) {
reclaim.count = 1;
TAILQ_INSERT_TAIL(&hmp->reclaim_list, &reclaim, entry);
tsleep(&reclaim, 0, "hmrrcm", hz);
if (reclaim.count > 0)
TAILQ_REMOVE(&hmp->reclaim_list, &reclaim, entry);
}
}
static
struct hammer_inostats *
hammer_inode_inostats(hammer_mount_t hmp, pid_t pid)
{
struct hammer_inostats *stats;
int delta;
int chain;
static volatile int iterator;
for (chain = 0; chain < 4; ++chain) {
stats = &hmp->inostats[(pid + chain) & HAMMER_INOSTATS_HMASK];
if (stats->pid == pid)
break;
}
if (chain == 4) {
stats = &hmp->inostats[(pid + (iterator++ & 3)) &
HAMMER_INOSTATS_HMASK];
stats->pid = pid;
}
if (stats->count && stats->ltick != ticks) {
delta = ticks - stats->ltick;
stats->ltick = ticks;
if (delta <= 0 || delta > hz * 60)
stats->count = 0;
else
stats->count = stats->count * hz / (hz + delta);
}
if (hammer_debug_general & 0x10000)
hdkprintf("pid %5d stats %d\n", (int)pid, stats->count);
return (stats);
}
#if 0
void
hammer_inode_waithard(hammer_mount_t hmp)
{
if (hmp->flags & HAMMER_MOUNT_FLUSH_RECOVERY) {
if (hmp->count_reclaims < hammer_limit_reclaims / 2 &&
hmp->count_iqueued < hmp->count_inodes / 20) {
hmp->flags &= ~HAMMER_MOUNT_FLUSH_RECOVERY;
return;
}
} else {
if (hmp->count_reclaims < hammer_limit_reclaims ||
hmp->count_iqueued < hmp->count_inodes / 10) {
return;
}
hmp->flags |= HAMMER_MOUNT_FLUSH_RECOVERY;
}
hammer_flusher_wait_next(hmp);
}
#endif