#include "hammer.h"
static int hammer_mirror_check(hammer_cursor_t cursor,
struct hammer_ioc_mrecord_rec *mrec);
static int hammer_mirror_update(hammer_cursor_t cursor,
struct hammer_ioc_mrecord_rec *mrec);
static int hammer_ioc_mirror_write_rec(hammer_cursor_t cursor,
struct hammer_ioc_mrecord_rec *mrec,
struct hammer_ioc_mirror_rw *mirror,
uint32_t localization,
char *uptr);
static int hammer_ioc_mirror_write_pass(hammer_cursor_t cursor,
struct hammer_ioc_mrecord_rec *mrec,
struct hammer_ioc_mirror_rw *mirror,
uint32_t localization);
static int hammer_ioc_mirror_write_skip(hammer_cursor_t cursor,
struct hammer_ioc_mrecord_skip *mrec,
struct hammer_ioc_mirror_rw *mirror,
uint32_t localization);
static int hammer_mirror_delete_to(hammer_cursor_t cursor,
struct hammer_ioc_mirror_rw *mirror);
static int hammer_mirror_nomirror(hammer_base_elm_t base);
int
hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_mirror_rw *mirror)
{
struct hammer_cmirror cmirror;
struct hammer_cursor cursor;
union hammer_ioc_mrecord_any mrec;
hammer_btree_leaf_elm_t elm;
char *uptr;
int error;
int data_len;
int bytes;
int eatdisk;
int mrec_flags;
uint32_t localization;
hammer_crc_t rec_crc;
localization = pfs_to_lo(mirror->pfs_id);
if ((mirror->key_beg.localization | mirror->key_end.localization) &
HAMMER_LOCALIZE_PSEUDOFS_MASK) {
return(EINVAL);
}
if (hammer_btree_cmp(&mirror->key_beg, &mirror->key_end) > 0)
return(EINVAL);
mirror->key_cur = mirror->key_beg;
mirror->key_cur.localization &= HAMMER_LOCALIZE_MASK;
mirror->key_cur.localization |= localization;
bzero(&mrec, sizeof(mrec));
bzero(&cmirror, sizeof(cmirror));
trans->flags |= HAMMER_TRANSF_CRCDOM;
retry:
error = hammer_init_cursor(trans, &cursor, NULL, NULL);
if (error) {
hammer_done_cursor(&cursor);
goto failed;
}
cursor.key_beg = mirror->key_cur;
cursor.key_end = mirror->key_end;
cursor.key_end.localization &= HAMMER_LOCALIZE_MASK;
cursor.key_end.localization |= localization;
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
cursor.flags |= HAMMER_CURSOR_BACKEND;
cursor.flags |= HAMMER_CURSOR_MIRROR_FILTERED;
cursor.cmirror = &cmirror;
cmirror.mirror_tid = mirror->tid_beg;
error = hammer_btree_first(&cursor);
while (error == 0) {
if (error == 0) {
error = hammer_signal_check(trans->hmp);
if (error)
break;
}
uptr = (char *)mirror->ubuf + mirror->count;
if (cursor.node->ondisk->type == HAMMER_BTREE_TYPE_INTERNAL) {
mirror->key_cur = cmirror.skip_beg;
bytes = sizeof(mrec.skip);
if (mirror->count + HAMMER_HEAD_DOALIGN(bytes) >
mirror->size) {
break;
}
mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
mrec.head.type = HAMMER_MREC_TYPE_SKIP;
mrec.head.rec_size = bytes;
mrec.skip.skip_beg = cmirror.skip_beg;
mrec.skip.skip_end = cmirror.skip_end;
hammer_crc_set_mrec_head(&mrec.head, bytes);
error = copyout(&mrec, uptr, bytes);
eatdisk = 0;
goto didwrite;
}
KKASSERT(cursor.node->ondisk->type == HAMMER_BTREE_TYPE_LEAF);
elm = &cursor.node->ondisk->elms[cursor.index].leaf;
mirror->key_cur = elm->base;
if (elm->base.create_tid > mirror->tid_end) {
error = 0;
bytes = 0;
eatdisk = 1;
goto didwrite;
}
if (elm->base.create_tid < mirror->tid_beg) {
bytes = sizeof(mrec.rec);
if (mirror->count + HAMMER_HEAD_DOALIGN(bytes) >
mirror->size) {
break;
}
mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
mrec.head.type = HAMMER_MREC_TYPE_PASS;
mrec.head.rec_size = bytes;
mrec.rec.leaf = *elm;
hammer_crc_set_mrec_head(&mrec.head, bytes);
error = copyout(&mrec, uptr, bytes);
eatdisk = 1;
goto didwrite;
}
mrec_flags = 0;
data_len = (elm->data_offset) ? elm->data_len : 0;
if (data_len &&
(mirror->head.flags & HAMMER_IOC_MIRROR_NODATA)) {
data_len = 0;
mrec_flags |= HAMMER_MRECF_NODATA;
}
if (data_len) {
error = hammer_btree_extract_data(&cursor);
if (error) {
if (error != EDOM)
break;
mrec_flags |= HAMMER_MRECF_CRC_ERROR |
HAMMER_MRECF_DATA_CRC_BAD;
}
}
bytes = sizeof(mrec.rec) + data_len;
if (mirror->count + HAMMER_HEAD_DOALIGN(bytes) > mirror->size)
break;
mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
mrec.head.type = HAMMER_MREC_TYPE_REC | mrec_flags;
mrec.head.rec_size = bytes;
mrec.rec.leaf = *elm;
if (elm->base.delete_tid > mirror->tid_end)
mrec.rec.leaf.base.delete_tid = 0;
rec_crc = hammer_crc_get_mrec_head(&mrec.head, sizeof(mrec.rec));
if (data_len)
rec_crc = crc32_ext(cursor.data, data_len, rec_crc);
mrec.head.rec_crc = rec_crc;
error = copyout(&mrec, uptr, sizeof(mrec.rec));
if (data_len && error == 0) {
error = copyout(cursor.data, uptr + sizeof(mrec.rec),
data_len);
}
eatdisk = 1;
didwrite:
if (error == 0) {
mirror->count += HAMMER_HEAD_DOALIGN(bytes);
if (eatdisk)
cursor.flags |= HAMMER_CURSOR_ATEDISK;
else
cursor.flags &= ~HAMMER_CURSOR_ATEDISK;
error = hammer_btree_iterate(&cursor);
}
}
if (error == ENOENT) {
mirror->key_cur = mirror->key_end;
error = 0;
}
hammer_done_cursor(&cursor);
if (error == EDEADLK)
goto retry;
if (error == EINTR) {
mirror->head.flags |= HAMMER_IOC_HEAD_INTR;
error = 0;
}
failed:
mirror->key_cur.localization &= HAMMER_LOCALIZE_MASK;
return(error);
}
int
hammer_ioc_mirror_write(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_mirror_rw *mirror)
{
union hammer_ioc_mrecord_any mrec;
struct hammer_cursor cursor;
uint32_t localization;
int checkspace_count = 0;
int error;
int bytes;
char *uptr;
int seq;
localization = pfs_to_lo(mirror->pfs_id);
seq = trans->hmp->flusher.done;
if (mirror->size < 0 || mirror->size > 0x70000000)
return(EINVAL);
mirror->key_beg.localization &= HAMMER_LOCALIZE_MASK;
mirror->key_beg.localization |= localization;
mirror->key_end.localization &= HAMMER_LOCALIZE_MASK;
mirror->key_end.localization |= localization;
mirror->key_cur.localization &= HAMMER_LOCALIZE_MASK;
mirror->key_cur.localization |= localization;
error = hammer_init_cursor(trans, &cursor, NULL, NULL);
cursor.key_beg = mirror->key_cur;
cursor.key_end = mirror->key_end;
cursor.flags |= HAMMER_CURSOR_BACKEND;
error = hammer_btree_first(&cursor);
if (error == 0)
cursor.flags |= HAMMER_CURSOR_ATEDISK;
if (error == ENOENT)
error = 0;
while (error == 0 &&
mirror->count + sizeof(mrec.head) <= mirror->size) {
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 (hammer_checkspace(trans->hmp, HAMMER_CHKSPC_MIRROR)) {
if (++checkspace_count == 10) {
error = ENOSPC;
break;
}
hammer_unlock_cursor(&cursor);
hammer_flusher_wait(trans->hmp, seq);
hammer_lock_cursor(&cursor);
seq = hammer_flusher_async(trans->hmp, NULL);
}
if ((bytes = mirror->size - mirror->count) > sizeof(mrec))
bytes = sizeof(mrec);
uptr = (char *)mirror->ubuf + mirror->count;
error = copyin(uptr, &mrec, bytes);
if (error)
break;
if (mrec.head.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
error = EINVAL;
break;
}
if (mrec.head.rec_size < sizeof(mrec.head) ||
mrec.head.rec_size > sizeof(mrec) + HAMMER_XBUFSIZE ||
mirror->count + mrec.head.rec_size > mirror->size) {
error = EINVAL;
break;
}
switch(mrec.head.type & HAMMER_MRECF_TYPE_MASK) {
case HAMMER_MREC_TYPE_SKIP:
if (mrec.head.rec_size != sizeof(mrec.skip))
error = EINVAL;
if (error == 0)
error = hammer_ioc_mirror_write_skip(&cursor, &mrec.skip, mirror, localization);
break;
case HAMMER_MREC_TYPE_REC:
if (mrec.head.rec_size < sizeof(mrec.rec))
error = EINVAL;
if (error == 0)
error = hammer_ioc_mirror_write_rec(&cursor, &mrec.rec, mirror, localization, uptr + sizeof(mrec.rec));
break;
case HAMMER_MREC_TYPE_REC_NODATA:
case HAMMER_MREC_TYPE_REC_BADCRC:
if (mrec.head.rec_size < sizeof(mrec.rec))
error = EINVAL;
break;
case HAMMER_MREC_TYPE_PASS:
if (mrec.head.rec_size != sizeof(mrec.rec))
error = EINVAL;
if (error == 0)
error = hammer_ioc_mirror_write_pass(&cursor, &mrec.rec, mirror, localization);
break;
default:
error = EINVAL;
break;
}
if (error == EDEADLK) {
while (error == EDEADLK) {
hammer_sync_lock_sh(trans);
hammer_recover_cursor(&cursor);
error = hammer_cursor_upgrade(&cursor);
hammer_sync_unlock(trans);
}
} else {
if (error == EALREADY)
error = 0;
if (error == 0) {
mirror->count +=
HAMMER_HEAD_DOALIGN(mrec.head.rec_size);
}
}
}
hammer_done_cursor(&cursor);
if (error) {
mirror->head.flags |= HAMMER_IOC_HEAD_ERROR;
mirror->head.error = error;
}
return(0);
}
static int
hammer_ioc_mirror_write_skip(hammer_cursor_t cursor,
struct hammer_ioc_mrecord_skip *mrec,
struct hammer_ioc_mirror_rw *mirror,
uint32_t localization)
{
int error;
mrec->skip_beg.localization &= HAMMER_LOCALIZE_MASK;
mrec->skip_beg.localization |= localization;
mrec->skip_end.localization &= HAMMER_LOCALIZE_MASK;
mrec->skip_end.localization |= localization;
cursor->key_end = mrec->skip_beg;
cursor->flags &= ~HAMMER_CURSOR_END_INCLUSIVE;
cursor->flags |= HAMMER_CURSOR_BACKEND;
error = hammer_mirror_delete_to(cursor, mirror);
if (error == 0) {
mirror->key_cur = mrec->skip_end;
cursor->key_beg = mrec->skip_end;
error = hammer_btree_lookup(cursor);
cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
if (error == ENOENT)
error = 0;
}
return(error);
}
static int
hammer_ioc_mirror_write_rec(hammer_cursor_t cursor,
struct hammer_ioc_mrecord_rec *mrec,
struct hammer_ioc_mirror_rw *mirror,
uint32_t localization,
char *uptr)
{
int error;
if (mrec->leaf.data_len < 0 ||
mrec->leaf.data_len > HAMMER_XBUFSIZE ||
mrec->leaf.data_len + sizeof(*mrec) > mrec->head.rec_size) {
return(EINVAL);
}
mrec->leaf.base.localization &= HAMMER_LOCALIZE_MASK;
mrec->leaf.base.localization |= localization;
cursor->key_end = mrec->leaf.base;
cursor->flags &= ~HAMMER_CURSOR_END_INCLUSIVE;
cursor->flags |= HAMMER_CURSOR_BACKEND;
error = hammer_mirror_delete_to(cursor, mirror);
if (error == 0 && hammer_mirror_nomirror(&mrec->leaf.base))
return(0);
if (error == 0) {
cursor->key_beg = mrec->leaf.base;
cursor->flags |= HAMMER_CURSOR_BACKEND;
cursor->flags &= ~HAMMER_CURSOR_INSERT;
error = hammer_btree_lookup(cursor);
}
if (error == 0 && hammer_mirror_check(cursor, mrec)) {
error = hammer_mirror_update(cursor, mrec);
} else if (error == ENOENT) {
if (mrec->leaf.base.create_tid >= mirror->tid_beg) {
error = hammer_create_at_cursor(
cursor, &mrec->leaf,
uptr, HAMMER_CREATE_MODE_UMIRROR);
} else {
error = 0;
}
}
if (error == 0 || error == EALREADY)
mirror->key_cur = mrec->leaf.base;
return(error);
}
static
int
hammer_ioc_mirror_write_pass(hammer_cursor_t cursor,
struct hammer_ioc_mrecord_rec *mrec,
struct hammer_ioc_mirror_rw *mirror,
uint32_t localization)
{
int error;
mrec->leaf.base.localization &= HAMMER_LOCALIZE_MASK;
mrec->leaf.base.localization |= localization;
cursor->key_end = mrec->leaf.base;
cursor->flags &= ~HAMMER_CURSOR_END_INCLUSIVE;
cursor->flags |= HAMMER_CURSOR_BACKEND;
error = hammer_mirror_delete_to(cursor, mirror);
if (hammer_mirror_nomirror(&mrec->leaf.base))
return(0);
if (error == 0) {
mirror->key_cur = mrec->leaf.base;
cursor->key_beg = mrec->leaf.base;
cursor->flags |= HAMMER_CURSOR_BACKEND;
cursor->flags &= ~HAMMER_CURSOR_INSERT;
error = hammer_btree_lookup(cursor);
if (error == 0) {
if (hammer_mirror_check(cursor, mrec))
error = hammer_mirror_update(cursor, mrec);
cursor->flags |= HAMMER_CURSOR_ATEDISK;
} else {
cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
}
if (error == ENOENT)
error = 0;
}
return(error);
}
static
int
hammer_mirror_delete_to(hammer_cursor_t cursor,
struct hammer_ioc_mirror_rw *mirror)
{
hammer_btree_leaf_elm_t elm;
int error;
error = hammer_btree_iterate(cursor);
while (error == 0) {
elm = &cursor->node->ondisk->elms[cursor->index].leaf;
KKASSERT(elm->base.btype == HAMMER_BTREE_TYPE_RECORD);
cursor->flags |= HAMMER_CURSOR_ATEDISK;
if (hammer_mirror_nomirror(&elm->base)) {
error = hammer_btree_iterate(cursor);
continue;
}
if (elm->base.delete_tid == 0 &&
elm->base.create_tid < mirror->tid_end) {
error = hammer_delete_at_cursor(cursor,
HAMMER_DELETE_ADJUST,
mirror->tid_end,
time_second,
1, NULL);
}
if (error == 0)
error = hammer_btree_iterate(cursor);
}
if (error == ENOENT)
error = 0;
return(error);
}
static
int
hammer_mirror_check(hammer_cursor_t cursor, struct hammer_ioc_mrecord_rec *mrec)
{
hammer_btree_leaf_elm_t leaf = cursor->leaf;
if (leaf->base.delete_tid != mrec->leaf.base.delete_tid) {
if (mrec->leaf.base.delete_tid != 0)
return(1);
}
return(0);
}
static
int
hammer_mirror_nomirror(hammer_base_elm_t base)
{
if (base->rec_type == HAMMER_RECTYPE_CONFIG)
return(1);
return(0);
}
static
int
hammer_mirror_update(hammer_cursor_t cursor,
struct hammer_ioc_mrecord_rec *mrec)
{
int error;
if (mrec->leaf.base.delete_tid == 0)
return(0);
error = hammer_delete_at_cursor(cursor, HAMMER_DELETE_ADJUST,
mrec->leaf.base.delete_tid,
mrec->leaf.delete_ts,
1, NULL);
cursor->flags |= HAMMER_CURSOR_ATEDISK;
return(error);
}