#include "hammer.h"
static int hammer_pfs_autodetect(struct hammer_ioc_pseudofs_rw *pfs,
hammer_inode_t ip);
static int hammer_pfs_rollback(hammer_transaction_t trans,
hammer_pseudofs_inmem_t pfsm,
hammer_tid_t trunc_tid);
static int hammer_pfs_delete_at_cursor(hammer_cursor_t cursor,
hammer_tid_t trunc_tid);
int
hammer_ioc_get_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_pseudofs_rw *pfs)
{
hammer_pseudofs_inmem_t pfsm;
uint32_t localization;
int error;
if ((error = hammer_pfs_autodetect(pfs, ip)) != 0)
return(error);
localization = pfs_to_lo(pfs->pfs_id);
pfs->bytes = sizeof(struct hammer_pseudofs_data);
pfs->version = HAMMER_IOC_PSEUDOFS_VERSION;
pfsm = hammer_load_pseudofs(trans, localization, &error);
if (error) {
hammer_rel_pseudofs(trans->hmp, pfsm);
return(error);
}
if (hammer_is_pfs_master(&pfsm->pfsd))
pfsm->pfsd.sync_end_tid = trans->hmp->flush_tid1;
if (pfs->ondisk)
error = copyout(&pfsm->pfsd, pfs->ondisk, sizeof(pfsm->pfsd));
hammer_rel_pseudofs(trans->hmp, pfsm);
return(error);
}
int
hammer_ioc_set_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
struct ucred *cred, struct hammer_ioc_pseudofs_rw *pfs)
{
hammer_pseudofs_inmem_t pfsm;
uint32_t localization;
int error;
if ((error = hammer_pfs_autodetect(pfs, ip)) != 0)
return(error);
localization = pfs_to_lo(pfs->pfs_id);
if (pfs->version != HAMMER_IOC_PSEUDOFS_VERSION)
error = EINVAL;
if (error == 0 && pfs->ondisk) {
pfsm = hammer_load_pseudofs(trans, localization, &error);
error = copyin(pfs->ondisk, &pfsm->pfsd, sizeof(pfsm->pfsd));
if (error == 0 && hammer_is_pfs_master(&pfsm->pfsd)) {
error = hammer_mkroot_pseudofs(trans, cred, pfsm, ip);
}
if (error == 0)
error = hammer_save_pseudofs(trans, pfsm);
wakeup(&pfsm->pfsd.sync_end_tid);
hammer_rel_pseudofs(trans->hmp, pfsm);
}
return(error);
}
int
hammer_ioc_upgrade_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_pseudofs_rw *pfs)
{
hammer_pseudofs_inmem_t pfsm;
uint32_t localization;
int error;
if ((error = hammer_pfs_autodetect(pfs, ip)) != 0)
return(error);
localization = pfs_to_lo(pfs->pfs_id);
if ((error = hammer_unload_pseudofs(trans, localization)) != 0)
return(error);
pfsm = hammer_load_pseudofs(trans, localization, &error);
if (error == 0) {
if (hammer_is_pfs_slave(&pfsm->pfsd)) {
error = hammer_pfs_rollback(trans, pfsm,
pfsm->pfsd.sync_end_tid + 1);
if (error == 0) {
pfsm->pfsd.mirror_flags &= ~HAMMER_PFSD_SLAVE;
error = hammer_save_pseudofs(trans, pfsm);
}
}
}
hammer_rel_pseudofs(trans->hmp, pfsm);
if (error == EINTR) {
pfs->head.flags |= HAMMER_IOC_HEAD_INTR;
error = 0;
}
return (error);
}
int
hammer_ioc_downgrade_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_pseudofs_rw *pfs)
{
hammer_mount_t hmp = trans->hmp;
hammer_pseudofs_inmem_t pfsm;
uint32_t localization;
int error;
if ((error = hammer_pfs_autodetect(pfs, ip)) != 0)
return(error);
localization = pfs_to_lo(pfs->pfs_id);
if ((error = hammer_unload_pseudofs(trans, localization)) != 0)
return(error);
pfsm = hammer_load_pseudofs(trans, localization, &error);
if (error == 0) {
if (hammer_is_pfs_master(&pfsm->pfsd)) {
pfsm->pfsd.mirror_flags |= HAMMER_PFSD_SLAVE;
if (pfsm->pfsd.sync_end_tid < hmp->flush_tid1)
pfsm->pfsd.sync_end_tid = hmp->flush_tid1;
error = hammer_save_pseudofs(trans, pfsm);
}
}
hammer_rel_pseudofs(trans->hmp, pfsm);
return (error);
}
int
hammer_ioc_destroy_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_pseudofs_rw *pfs)
{
hammer_pseudofs_inmem_t pfsm;
uint32_t localization;
int error;
if ((error = hammer_pfs_autodetect(pfs, ip)) != 0)
return(error);
localization = pfs_to_lo(pfs->pfs_id);
if ((error = hammer_unload_pseudofs(trans, localization)) != 0)
return(error);
pfsm = hammer_load_pseudofs(trans, localization, &error);
if (error == 0) {
error = hammer_pfs_rollback(trans, pfsm, 0);
if (error == 0) {
pfsm->pfsd.mirror_flags |= HAMMER_PFSD_DELETED;
error = hammer_save_pseudofs(trans, pfsm);
}
}
hammer_rel_pseudofs(trans->hmp, pfsm);
if (error == EINTR) {
pfs->head.flags |= HAMMER_IOC_HEAD_INTR;
error = 0;
}
return(error);
}
int
hammer_ioc_wait_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_pseudofs_rw *pfs)
{
hammer_pseudofs_inmem_t pfsm;
struct hammer_pseudofs_data pfsd;
uint32_t localization;
hammer_tid_t tid;
void *waitp;
int error;
if ((error = hammer_pfs_autodetect(pfs, ip)) != 0)
return(error);
localization = pfs_to_lo(pfs->pfs_id);
if ((error = copyin(pfs->ondisk, &pfsd, sizeof(pfsd))) != 0)
return(error);
pfsm = hammer_load_pseudofs(trans, localization, &error);
if (error == 0) {
if (hammer_is_pfs_slave(&pfsm->pfsd)) {
tid = pfsm->pfsd.sync_end_tid;
waitp = &pfsm->pfsd.sync_end_tid;
} else {
tid = trans->hmp->flush_tid1;
waitp = &trans->hmp->flush_tid1;
}
if (tid <= pfsd.sync_end_tid)
tsleep(waitp, PCATCH, "hmrmwt", 0);
}
hammer_rel_pseudofs(trans->hmp, pfsm);
if (error == EINTR) {
pfs->head.flags |= HAMMER_IOC_HEAD_INTR;
error = 0;
}
return(error);
}
int
hammer_ioc_scan_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_pseudofs_rw *pfs)
{
struct hammer_cursor cursor;
hammer_inode_t dip;
uint32_t localization;
int error;
if ((error = hammer_pfs_autodetect(pfs, ip)) != 0)
return(error);
localization = pfs_to_lo(pfs->pfs_id);
pfs->bytes = sizeof(struct hammer_pseudofs_data);
pfs->version = HAMMER_IOC_PSEUDOFS_VERSION;
dip = hammer_get_inode(trans, NULL, HAMMER_OBJID_ROOT, HAMMER_MAX_TID,
HAMMER_DEF_LOCALIZATION, 0, &error);
error = hammer_init_cursor(trans, &cursor,
(dip ? &dip->cache[1] : NULL), dip);
if (error)
goto fail;
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;
error = hammer_ip_lookup(&cursor);
if (error == 0) {
error = hammer_ip_resolve_data(&cursor);
if (error == 0) {
if (pfs->ondisk)
copyout(cursor.data, pfs->ondisk, cursor.leaf->data_len);
localization = cursor.leaf->base.key;
pfs->pfs_id = lo_to_pfs(localization);
}
}
hammer_done_cursor(&cursor);
fail:
if (dip)
hammer_rel_inode(dip, 0);
return(error);
}
static
int
hammer_pfs_autodetect(struct hammer_ioc_pseudofs_rw *pfs, hammer_inode_t ip)
{
int error = 0;
if (pfs->pfs_id == -1)
pfs->pfs_id = lo_to_pfs(ip->obj_localization);
if (pfs->pfs_id < 0 || pfs->pfs_id >= HAMMER_MAX_PFS)
error = EINVAL;
if (pfs->bytes < sizeof(struct hammer_pseudofs_data))
error = EINVAL;
return(error);
}
static
int
hammer_pfs_rollback(hammer_transaction_t trans,
hammer_pseudofs_inmem_t pfsm,
hammer_tid_t trunc_tid)
{
struct hammer_cmirror cmirror;
struct hammer_cursor cursor;
struct hammer_base_elm key_cur;
int error;
int seq;
bzero(&cmirror, sizeof(cmirror));
bzero(&key_cur, sizeof(key_cur));
key_cur.localization = HAMMER_MIN_LOCALIZATION | pfsm->localization;
key_cur.obj_id = HAMMER_MIN_OBJID;
key_cur.key = HAMMER_MIN_KEY;
key_cur.create_tid = 1;
key_cur.rec_type = HAMMER_MIN_RECTYPE;
seq = trans->hmp->flusher.done;
retry:
error = hammer_init_cursor(trans, &cursor, NULL, NULL);
if (error) {
hammer_done_cursor(&cursor);
goto failed;
}
cursor.key_beg = key_cur;
cursor.key_end.localization = HAMMER_MAX_LOCALIZATION |
pfsm->localization;
cursor.key_end.obj_id = HAMMER_MAX_OBJID;
cursor.key_end.key = HAMMER_MAX_KEY;
cursor.key_end.create_tid = HAMMER_MAX_TID;
cursor.key_end.rec_type = HAMMER_MAX_RECTYPE;
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
cursor.flags |= HAMMER_CURSOR_BACKEND;
cursor.flags |= HAMMER_CURSOR_MIRROR_FILTERED;
cursor.cmirror = &cmirror;
cmirror.mirror_tid = trunc_tid;
error = hammer_btree_first(&cursor);
while (error == 0) {
if (error == 0) {
error = hammer_signal_check(trans->hmp);
if (error)
break;
}
cursor.flags |= HAMMER_CURSOR_ATEDISK;
if (cursor.node->ondisk->type == HAMMER_BTREE_TYPE_LEAF) {
key_cur = cursor.node->ondisk->elms[cursor.index].base;
error = hammer_pfs_delete_at_cursor(&cursor, trunc_tid);
}
while (hammer_flusher_meta_halflimit(trans->hmp) ||
hammer_flusher_undo_exhausted(trans, 2)) {
hammer_unlock_cursor(&cursor);
hammer_flusher_wait(trans->hmp, seq);
hammer_lock_cursor(&cursor);
seq = hammer_flusher_async_one(trans->hmp);
}
if (error == 0)
error = hammer_btree_iterate(&cursor);
}
if (error == ENOENT)
error = 0;
hammer_done_cursor(&cursor);
if (error == EDEADLK)
goto retry;
failed:
return(error);
}
static
int
hammer_pfs_delete_at_cursor(hammer_cursor_t cursor, hammer_tid_t trunc_tid)
{
hammer_btree_leaf_elm_t elm;
int error;
elm = &cursor->node->ondisk->elms[cursor->index].leaf;
if (elm->base.create_tid < trunc_tid &&
elm->base.delete_tid < trunc_tid) {
return(0);
}
if (elm->base.create_tid >= trunc_tid) {
error = hammer_delete_at_cursor(
cursor, HAMMER_DELETE_DESTROY,
cursor->trans->tid, cursor->trans->time32,
1, NULL);
} else if (elm->base.delete_tid >= trunc_tid) {
error = hammer_delete_at_cursor(
cursor, HAMMER_DELETE_ADJUST,
0, 0,
1, NULL);
} else {
error = 0;
}
return(error);
}