#include "hammer.h"
static int
hammer_und_rb_compare(hammer_undo_t node1, hammer_undo_t node2)
{
if (node1->offset < node2->offset)
return(-1);
if (node1->offset > node2->offset)
return(1);
return(0);
}
RB_GENERATE2(hammer_und_rb_tree, hammer_undo, rb_node,
hammer_und_rb_compare, hammer_off_t, offset);
hammer_off_t
hammer_undo_lookup(hammer_mount_t hmp, hammer_off_t zone3_off, int *errorp)
{
hammer_volume_t root_volume;
hammer_blockmap_t undomap __debugvar;
hammer_off_t result_offset;
KKASSERT(hammer_is_zone_undo(zone3_off));
root_volume = hammer_get_root_volume(hmp, errorp);
if (*errorp)
return(0);
undomap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
KKASSERT(hammer_is_zone_undo(undomap->alloc_offset));
KKASSERT(zone3_off < undomap->alloc_offset);
result_offset = hammer_xlate_to_undo(root_volume->ondisk, zone3_off);
hammer_rel_volume(root_volume, 0);
return(result_offset);
}
int
hammer_generate_undo(hammer_transaction_t trans,
hammer_off_t zone_off, void *base, int len)
{
hammer_mount_t hmp;
hammer_volume_t root_volume;
hammer_blockmap_t undomap;
hammer_buffer_t buffer = NULL;
hammer_fifo_undo_t undo;
hammer_fifo_tail_t tail;
hammer_off_t next_offset;
int error;
int bytes;
int n;
hmp = trans->hmp;
if ((hmp->flags & HAMMER_MOUNT_REDO_SYNC) == 0 &&
hmp->version >= HAMMER_VOL_VERSION_FOUR) {
hammer_generate_redo_sync(trans);
}
if (hammer_enter_undo_history(hmp, zone_off, len) == EALREADY)
return(0);
root_volume = trans->rootvol;
undomap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
hammer_modify_volume_noundo(NULL, root_volume);
hammer_lock_ex(&hmp->undo_lock);
if (hammer_undo_space(trans) < len + HAMMER_BUFSIZE*3)
hpanic("insufficient UNDO/REDO FIFO space for undo!");
while (len) {
if (undomap->next_offset == undomap->alloc_offset)
undomap->next_offset = HAMMER_ENCODE_UNDO(0);
next_offset = undomap->next_offset;
if ((next_offset & HAMMER_BUFMASK) == 0) {
undo = hammer_bnew(hmp, next_offset, &error, &buffer);
hammer_format_undo(hmp, undo,
hmp->undo_seqno ^ 0x40000000);
} else {
undo = hammer_bread(hmp, next_offset, &error, &buffer);
}
if (error)
break;
hammer_modify_buffer_noundo(NULL, buffer);
bytes = HAMMER_UNDO_ALIGN -
((int)next_offset & HAMMER_UNDO_MASK);
n = bytes -
(int)sizeof(struct hammer_fifo_undo) -
(int)sizeof(struct hammer_fifo_tail);
if (n <= 0) {
KKASSERT(bytes >= sizeof(struct hammer_fifo_tail));
KKASSERT(((int)next_offset & HAMMER_UNDO_MASK) != 0);
tail = (void *)((char *)undo + bytes - sizeof(*tail));
if ((void *)undo != (void *)tail) {
tail->tail_signature = HAMMER_TAIL_SIGNATURE;
tail->tail_type = HAMMER_HEAD_TYPE_PAD;
tail->tail_size = bytes;
}
undo->head.hdr_signature = HAMMER_HEAD_SIGNATURE;
undo->head.hdr_type = HAMMER_HEAD_TYPE_PAD;
undo->head.hdr_size = bytes;
undomap->next_offset += bytes;
hammer_modify_buffer_done(buffer);
hammer_stats_undo += bytes;
continue;
}
if (n > len) {
n = len;
bytes = HAMMER_HEAD_DOALIGN(n) +
(int)sizeof(struct hammer_fifo_undo) +
(int)sizeof(struct hammer_fifo_tail);
}
if (hammer_debug_general & 0x0080) {
hdkprintf("undo %016jx %d %d\n",
(intmax_t)next_offset, bytes, n);
}
undo->head.hdr_signature = HAMMER_HEAD_SIGNATURE;
undo->head.hdr_type = HAMMER_HEAD_TYPE_UNDO;
undo->head.hdr_size = bytes;
undo->head.hdr_seq = hmp->undo_seqno++;
undo->head.hdr_crc = 0;
undo->undo_offset = zone_off;
undo->undo_data_bytes = n;
bcopy(base, undo + 1, n);
tail = (void *)((char *)undo + bytes - sizeof(*tail));
tail->tail_signature = HAMMER_TAIL_SIGNATURE;
tail->tail_type = HAMMER_HEAD_TYPE_UNDO;
tail->tail_size = bytes;
KKASSERT(bytes >= sizeof(undo->head));
hammer_crc_set_fifo_head(hmp->version, &undo->head, bytes);
undomap->next_offset += bytes;
hammer_stats_undo += bytes;
bytes = HAMMER_UNDO_ALIGN -
((int)undomap->next_offset & HAMMER_UNDO_MASK);
if (bytes != HAMMER_UNDO_ALIGN) {
KKASSERT(bytes >= sizeof(struct hammer_fifo_tail));
undo = (void *)(tail + 1);
tail = (void *)((char *)undo + bytes - sizeof(*tail));
if ((void *)undo != (void *)tail) {
tail->tail_signature = HAMMER_TAIL_SIGNATURE;
tail->tail_type = HAMMER_HEAD_TYPE_PAD;
tail->tail_size = bytes;
}
undo->head.hdr_signature = HAMMER_HEAD_SIGNATURE;
undo->head.hdr_type = HAMMER_HEAD_TYPE_PAD;
undo->head.hdr_size = bytes;
}
hammer_modify_buffer_done(buffer);
len -= n;
base = (char *)base + n;
zone_off += n;
}
hammer_modify_volume_done(root_volume);
hammer_unlock(&hmp->undo_lock);
if (buffer)
hammer_rel_buffer(buffer, 0);
return(error);
}
void
hammer_format_undo(hammer_mount_t hmp, void *base, uint32_t seqno)
{
hammer_fifo_head_t head;
hammer_fifo_tail_t tail;
int i;
int bytes = HAMMER_UNDO_ALIGN;
bzero(base, HAMMER_BUFSIZE);
for (i = 0; i < HAMMER_BUFSIZE; i += bytes) {
head = (void *)((char *)base + i);
tail = (void *)((char *)head + bytes - sizeof(*tail));
head->hdr_signature = HAMMER_HEAD_SIGNATURE;
head->hdr_type = HAMMER_HEAD_TYPE_DUMMY;
head->hdr_size = bytes;
head->hdr_seq = seqno++;
head->hdr_crc = 0;
tail->tail_signature = HAMMER_TAIL_SIGNATURE;
tail->tail_type = HAMMER_HEAD_TYPE_DUMMY;
tail->tail_size = bytes;
hammer_crc_set_fifo_head(hmp->version, head, bytes);
}
}
int
hammer_upgrade_undo_4(hammer_transaction_t trans)
{
hammer_mount_t hmp;
hammer_volume_t root_volume;
hammer_blockmap_t undomap;
hammer_buffer_t buffer = NULL;
hammer_fifo_head_t head;
hammer_fifo_tail_t tail;
hammer_off_t next_offset;
uint32_t seqno;
int error;
int bytes;
hmp = trans->hmp;
root_volume = trans->rootvol;
hammer_lock_ex(&hmp->undo_lock);
hammer_modify_volume_noundo(NULL, root_volume);
next_offset = HAMMER_ENCODE_UNDO(0);
undomap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
undomap->next_offset = next_offset;
undomap->first_offset = next_offset;
undomap = &root_volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
undomap->next_offset = next_offset;
undomap->first_offset = next_offset;
seqno = 0;
bytes = HAMMER_UNDO_ALIGN;
while (next_offset != undomap->alloc_offset) {
head = hammer_bnew(hmp, next_offset, &error, &buffer);
if (error)
break;
hammer_modify_buffer_noundo(NULL, buffer);
tail = (void *)((char *)head + bytes - sizeof(*tail));
head->hdr_signature = HAMMER_HEAD_SIGNATURE;
head->hdr_type = HAMMER_HEAD_TYPE_DUMMY;
head->hdr_size = bytes;
head->hdr_seq = seqno;
head->hdr_crc = 0;
tail = (void *)((char *)head + bytes - sizeof(*tail));
tail->tail_signature = HAMMER_TAIL_SIGNATURE;
tail->tail_type = HAMMER_HEAD_TYPE_DUMMY;
tail->tail_size = bytes;
hammer_crc_set_fifo_head(hmp->version, head, bytes);
hammer_modify_buffer_done(buffer);
hammer_stats_undo += bytes;
next_offset += HAMMER_UNDO_ALIGN;
++seqno;
}
hmp->undo_seqno = seqno;
hmkprintf(hmp, "version upgrade seqno start %08x\n", seqno);
hammer_modify_volume_done(root_volume);
hammer_unlock(&hmp->undo_lock);
if (buffer)
hammer_rel_buffer(buffer, 0);
return (error);
}
int
hammer_enter_undo_history(hammer_mount_t hmp, hammer_off_t offset, int bytes)
{
hammer_undo_t node;
hammer_undo_t onode __debugvar;
node = RB_LOOKUP(hammer_und_rb_tree, &hmp->rb_undo_root, offset);
if (node) {
TAILQ_REMOVE(&hmp->undo_lru_list, node, lru_entry);
TAILQ_INSERT_TAIL(&hmp->undo_lru_list, node, lru_entry);
if (bytes <= node->bytes)
return(EALREADY);
node->bytes = bytes;
return(0);
}
if (hmp->undo_alloc != HAMMER_MAX_UNDOS) {
node = &hmp->undos[hmp->undo_alloc++];
} else {
node = TAILQ_FIRST(&hmp->undo_lru_list);
TAILQ_REMOVE(&hmp->undo_lru_list, node, lru_entry);
RB_REMOVE(hammer_und_rb_tree, &hmp->rb_undo_root, node);
}
node->offset = offset;
node->bytes = bytes;
TAILQ_INSERT_TAIL(&hmp->undo_lru_list, node, lru_entry);
onode = RB_INSERT(hammer_und_rb_tree, &hmp->rb_undo_root, node);
KKASSERT(onode == NULL);
return(0);
}
void
hammer_clear_undo_history(hammer_mount_t hmp)
{
RB_INIT(&hmp->rb_undo_root);
TAILQ_INIT(&hmp->undo_lru_list);
hmp->undo_alloc = 0;
}
int64_t
hammer_undo_used(hammer_transaction_t trans)
{
hammer_blockmap_t cundomap;
hammer_blockmap_t dundomap;
int64_t max_bytes __debugvar;
int64_t bytes;
cundomap = &trans->hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
dundomap = &trans->rootvol->ondisk->
vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
if (dundomap->first_offset <= cundomap->next_offset) {
bytes = cundomap->next_offset - dundomap->first_offset;
} else {
bytes = cundomap->alloc_offset - dundomap->first_offset +
HAMMER_OFF_LONG_ENCODE(cundomap->next_offset);
}
max_bytes = HAMMER_OFF_SHORT_ENCODE(cundomap->alloc_offset);
KKASSERT(bytes <= max_bytes);
return(bytes);
}
int64_t
hammer_undo_space(hammer_transaction_t trans)
{
hammer_blockmap_t rootmap;
int64_t max_bytes;
rootmap = &trans->hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
max_bytes = HAMMER_OFF_SHORT_ENCODE(rootmap->alloc_offset);
return(max_bytes - hammer_undo_used(trans));
}
int64_t
hammer_undo_max(hammer_mount_t hmp)
{
hammer_blockmap_t rootmap;
int64_t max_bytes;
rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
max_bytes = HAMMER_OFF_SHORT_ENCODE(rootmap->alloc_offset);
return(max_bytes);
}
int
hammer_undo_reclaim(hammer_io_t io)
{
hammer_blockmap_t undomap;
hammer_off_t next_offset;
undomap = &io->hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
next_offset = undomap->next_offset & ~HAMMER_BUFMASK64;
if (HAMMER_ITOB(io)->zoneX_offset == next_offset)
return(0);
return(1);
}