#include <vm/vm_page2.h>
#include "hammer.h"
static int hammer_res_rb_compare(hammer_reserve_t res1, hammer_reserve_t res2);
static void hammer_reserve_setdelay_offset(hammer_mount_t hmp,
hammer_off_t base_offset, int zone,
hammer_blockmap_layer2_t layer2);
static void hammer_reserve_setdelay(hammer_mount_t hmp, hammer_reserve_t resv);
static int hammer_check_volume(hammer_mount_t, hammer_off_t*);
static void hammer_skip_volume(hammer_off_t *offsetp);
static __inline void
hammer_verify_layer1_crc(hammer_mount_t hmp, hammer_blockmap_layer1_t layer1)
{
if (!hammer_crc_test_layer1(hmp->version, layer1)) {
hammer_lock_ex(&hmp->blkmap_lock);
if (!hammer_crc_test_layer1(hmp->version, layer1))
hpanic("CRC FAILED: LAYER1");
hammer_unlock(&hmp->blkmap_lock);
}
}
static __inline void
hammer_verify_layer2_crc(hammer_mount_t hmp, hammer_blockmap_layer2_t layer2)
{
if (!hammer_crc_test_layer2(hmp->version, layer2)) {
hammer_lock_ex(&hmp->blkmap_lock);
if (!hammer_crc_test_layer2(hmp->version, layer2))
hpanic("CRC FAILED: LAYER2");
hammer_unlock(&hmp->blkmap_lock);
}
}
RB_GENERATE2(hammer_res_rb_tree, hammer_reserve, rb_node,
hammer_res_rb_compare, hammer_off_t, zone_offset);
static int
hammer_res_rb_compare(hammer_reserve_t res1, hammer_reserve_t res2)
{
if (res1->zone_offset < res2->zone_offset)
return(-1);
if (res1->zone_offset > res2->zone_offset)
return(1);
return(0);
}
hammer_off_t
hammer_blockmap_alloc(hammer_transaction_t trans, int zone, int bytes,
hammer_off_t hint, int *errorp)
{
hammer_mount_t hmp;
hammer_volume_t root_volume;
hammer_blockmap_t blockmap;
hammer_blockmap_t freemap;
hammer_reserve_t resv;
hammer_blockmap_layer1_t layer1;
hammer_blockmap_layer2_t layer2;
hammer_buffer_t buffer1 = NULL;
hammer_buffer_t buffer2 = NULL;
hammer_buffer_t buffer3 = NULL;
hammer_off_t tmp_offset;
hammer_off_t next_offset;
hammer_off_t result_offset;
hammer_off_t layer1_offset;
hammer_off_t layer2_offset;
hammer_off_t base_off;
int loops = 0;
int offset;
int use_hint;
hmp = trans->hmp;
bytes = HAMMER_DATA_DOALIGN(bytes);
KKASSERT(bytes > 0 && bytes <= HAMMER_XBUFSIZE);
KKASSERT(hammer_is_index_record(zone));
root_volume = trans->rootvol;
*errorp = 0;
blockmap = &hmp->blockmap[zone];
freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
KKASSERT(HAMMER_ZONE_DECODE(blockmap->next_offset) == zone);
if (hint && HAMMER_ZONE_DECODE(hint) == zone) {
next_offset = HAMMER_DATA_DOALIGN_WITH(hammer_off_t, hint);
use_hint = 1;
} else {
next_offset = blockmap->next_offset;
use_hint = 0;
}
again:
if (use_hint && ((next_offset ^ hint) & ~HAMMER_HINTBLOCK_MASK64)) {
next_offset = blockmap->next_offset;
use_hint = 0;
}
if (next_offset == HAMMER_ZONE_ENCODE(zone + 1, 0)) {
if (++loops == 2) {
hmkprintf(hmp, "No space left for zone %d "
"allocation\n", zone);
result_offset = 0;
*errorp = ENOSPC;
goto failed;
}
next_offset = HAMMER_ZONE_ENCODE(zone, 0);
}
tmp_offset = next_offset + bytes - 1;
if (bytes <= HAMMER_BUFSIZE) {
if ((next_offset ^ tmp_offset) & ~HAMMER_BUFMASK64) {
next_offset = tmp_offset & ~HAMMER_BUFMASK64;
goto again;
}
} else {
if ((next_offset ^ tmp_offset) & ~HAMMER_BIGBLOCK_MASK64) {
next_offset = tmp_offset & ~HAMMER_BIGBLOCK_MASK64;
goto again;
}
}
offset = (int)next_offset & HAMMER_BIGBLOCK_MASK;
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(next_offset);
layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer1);
if (*errorp) {
result_offset = 0;
goto failed;
}
hammer_verify_layer1_crc(hmp, layer1);
if (offset == 0 && layer1->blocks_free == 0) {
next_offset = HAMMER_ZONE_LAYER1_NEXT_OFFSET(next_offset);
if (hammer_check_volume(hmp, &next_offset)) {
result_offset = 0;
goto failed;
}
goto again;
}
KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL);
if (HAMMER_VOL_DECODE(layer1->phys_offset) == hmp->volume_to_remove) {
hammer_skip_volume(&next_offset);
goto again;
}
layer2_offset = layer1->phys_offset +
HAMMER_BLOCKMAP_LAYER2_OFFSET(next_offset);
layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer2);
if (*errorp) {
result_offset = 0;
goto failed;
}
hammer_verify_layer2_crc(hmp, layer2);
if (layer2->zone && layer2->zone != zone) {
next_offset += (HAMMER_BIGBLOCK_SIZE - offset);
goto again;
}
if (offset < layer2->append_off) {
next_offset += layer2->append_off - offset;
goto again;
}
#if 0
if ((zone == HAMMER_ZONE_BTREE_INDEX ||
zone == HAMMER_ZONE_META_INDEX) &&
offset >= HAMMER_BIGBLOCK_OVERFILL &&
!((next_offset ^ blockmap->next_offset) & ~HAMMER_BIGBLOCK_MASK64)) {
if (offset >= HAMMER_BIGBLOCK_OVERFILL) {
next_offset += (HAMMER_BIGBLOCK_SIZE - offset);
use_hint = 0;
goto again;
}
}
#endif
hammer_lock_ex(&hmp->blkmap_lock);
if (layer2->zone && layer2->zone != zone) {
hammer_unlock(&hmp->blkmap_lock);
next_offset += (HAMMER_BIGBLOCK_SIZE - offset);
goto again;
}
if (offset < layer2->append_off) {
hammer_unlock(&hmp->blkmap_lock);
next_offset += layer2->append_off - offset;
goto again;
}
base_off = hammer_xlate_to_zone2(next_offset & ~HAMMER_BIGBLOCK_MASK64);
resv = RB_LOOKUP(hammer_res_rb_tree, &hmp->rb_resv_root, base_off);
if (resv) {
if (resv->zone != zone) {
hammer_unlock(&hmp->blkmap_lock);
next_offset = HAMMER_ZONE_LAYER2_NEXT_OFFSET(next_offset);
goto again;
}
if (offset < resv->append_off) {
hammer_unlock(&hmp->blkmap_lock);
next_offset += resv->append_off - offset;
goto again;
}
++resv->refs;
}
if (layer2->zone == 0) {
hammer_modify_buffer(trans, buffer1, layer1, sizeof(*layer1));
--layer1->blocks_free;
hammer_crc_set_layer1(hmp->version, layer1);
hammer_modify_buffer_done(buffer1);
hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2));
layer2->zone = zone;
KKASSERT(layer2->bytes_free == HAMMER_BIGBLOCK_SIZE);
KKASSERT(layer2->append_off == 0);
hammer_modify_volume_field(trans, trans->rootvol,
vol0_stat_freebigblocks);
--root_volume->ondisk->vol0_stat_freebigblocks;
hmp->copy_stat_freebigblocks =
root_volume->ondisk->vol0_stat_freebigblocks;
hammer_modify_volume_done(trans->rootvol);
} else {
hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2));
}
KKASSERT(layer2->zone == zone);
layer2->bytes_free -= bytes;
KKASSERT(layer2->append_off <= offset);
layer2->append_off = offset + bytes;
hammer_crc_set_layer2(hmp->version, layer2);
hammer_modify_buffer_done(buffer2);
KKASSERT(bytes != 0);
if (resv) {
KKASSERT(resv->append_off <= offset);
resv->append_off = offset + bytes;
resv->flags &= ~HAMMER_RESF_LAYER2FREE;
hammer_blockmap_reserve_complete(hmp, resv);
}
if ((next_offset & HAMMER_BUFMASK) == 0) {
hammer_bnew_ext(trans->hmp, next_offset, bytes,
errorp, &buffer3);
if (*errorp) {
result_offset = 0;
goto failed;
}
}
result_offset = next_offset;
if (use_hint == 0) {
hammer_modify_volume_noundo(NULL, root_volume);
blockmap->next_offset = next_offset + bytes;
hammer_modify_volume_done(root_volume);
}
hammer_unlock(&hmp->blkmap_lock);
failed:
if (buffer1)
hammer_rel_buffer(buffer1, 0);
if (buffer2)
hammer_rel_buffer(buffer2, 0);
if (buffer3)
hammer_rel_buffer(buffer3, 0);
return(result_offset);
}
hammer_reserve_t
hammer_blockmap_reserve(hammer_mount_t hmp, int zone, int bytes,
hammer_off_t *zone_offp, int *errorp)
{
hammer_volume_t root_volume;
hammer_blockmap_t blockmap;
hammer_blockmap_t freemap;
hammer_blockmap_layer1_t layer1;
hammer_blockmap_layer2_t layer2;
hammer_buffer_t buffer1 = NULL;
hammer_buffer_t buffer2 = NULL;
hammer_buffer_t buffer3 = NULL;
hammer_off_t tmp_offset;
hammer_off_t next_offset;
hammer_off_t layer1_offset;
hammer_off_t layer2_offset;
hammer_off_t base_off;
hammer_reserve_t resv;
hammer_reserve_t resx = NULL;
int loops = 0;
int offset;
KKASSERT(hammer_is_index_record(zone));
root_volume = hammer_get_root_volume(hmp, errorp);
if (*errorp)
return(NULL);
blockmap = &hmp->blockmap[zone];
freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
KKASSERT(HAMMER_ZONE_DECODE(blockmap->next_offset) == zone);
bytes = HAMMER_DATA_DOALIGN(bytes);
KKASSERT(bytes > 0 && bytes <= HAMMER_XBUFSIZE);
next_offset = blockmap->next_offset;
again:
resv = NULL;
if (next_offset == HAMMER_ZONE_ENCODE(zone + 1, 0)) {
if (++loops == 2) {
hmkprintf(hmp, "No space left for zone %d "
"reservation\n", zone);
*errorp = ENOSPC;
goto failed;
}
next_offset = HAMMER_ZONE_ENCODE(zone, 0);
}
tmp_offset = next_offset + bytes - 1;
if (bytes <= HAMMER_BUFSIZE) {
if ((next_offset ^ tmp_offset) & ~HAMMER_BUFMASK64) {
next_offset = tmp_offset & ~HAMMER_BUFMASK64;
goto again;
}
} else {
if ((next_offset ^ tmp_offset) & ~HAMMER_BIGBLOCK_MASK64) {
next_offset = tmp_offset & ~HAMMER_BIGBLOCK_MASK64;
goto again;
}
}
offset = (int)next_offset & HAMMER_BIGBLOCK_MASK;
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(next_offset);
layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer1);
if (*errorp)
goto failed;
hammer_verify_layer1_crc(hmp, layer1);
if ((next_offset & HAMMER_BIGBLOCK_MASK) == 0 &&
layer1->blocks_free == 0) {
next_offset = HAMMER_ZONE_LAYER1_NEXT_OFFSET(next_offset);
if (hammer_check_volume(hmp, &next_offset))
goto failed;
goto again;
}
KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL);
layer2_offset = layer1->phys_offset +
HAMMER_BLOCKMAP_LAYER2_OFFSET(next_offset);
layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer2);
if (*errorp)
goto failed;
hammer_verify_layer2_crc(hmp, layer2);
if (layer2->zone && layer2->zone != zone) {
next_offset += (HAMMER_BIGBLOCK_SIZE - offset);
goto again;
}
if (offset < layer2->append_off) {
next_offset += layer2->append_off - offset;
goto again;
}
hammer_lock_ex(&hmp->blkmap_lock);
if (layer2->zone && layer2->zone != zone) {
hammer_unlock(&hmp->blkmap_lock);
next_offset += (HAMMER_BIGBLOCK_SIZE - offset);
goto again;
}
if (offset < layer2->append_off) {
hammer_unlock(&hmp->blkmap_lock);
next_offset += layer2->append_off - offset;
goto again;
}
base_off = hammer_xlate_to_zone2(next_offset & ~HAMMER_BIGBLOCK_MASK64);
resv = RB_LOOKUP(hammer_res_rb_tree, &hmp->rb_resv_root, base_off);
if (resv) {
if (resv->zone != zone) {
hammer_unlock(&hmp->blkmap_lock);
next_offset = HAMMER_ZONE_LAYER2_NEXT_OFFSET(next_offset);
goto again;
}
if (offset < resv->append_off) {
hammer_unlock(&hmp->blkmap_lock);
next_offset += resv->append_off - offset;
goto again;
}
++resv->refs;
} else {
resx = kmalloc(sizeof(*resv), hmp->m_misc,
M_WAITOK | M_ZERO | M_USE_RESERVE);
resx->refs = 1;
resx->zone = zone;
resx->zone_offset = base_off;
if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE)
resx->flags |= HAMMER_RESF_LAYER2FREE;
resv = RB_INSERT(hammer_res_rb_tree, &hmp->rb_resv_root, resx);
KKASSERT(resv == NULL);
resv = resx;
++hammer_count_reservations;
}
resv->append_off = offset + bytes;
if (bytes < HAMMER_BUFSIZE && (next_offset & HAMMER_BUFMASK) == 0) {
if (!vm_paging_min_dnc(HAMMER_BUFSIZE / PAGE_SIZE)) {
hammer_bnew(hmp, next_offset, errorp, &buffer3);
if (*errorp)
goto failed;
}
}
blockmap->next_offset = next_offset + bytes;
hammer_unlock(&hmp->blkmap_lock);
failed:
if (buffer1)
hammer_rel_buffer(buffer1, 0);
if (buffer2)
hammer_rel_buffer(buffer2, 0);
if (buffer3)
hammer_rel_buffer(buffer3, 0);
hammer_rel_volume(root_volume, 0);
*zone_offp = next_offset;
return(resv);
}
void
hammer_blockmap_reserve_complete(hammer_mount_t hmp, hammer_reserve_t resv)
{
hammer_off_t base_offset;
int error;
KKASSERT(resv->refs > 0);
KKASSERT(hammer_is_zone_raw_buffer(resv->zone_offset));
if (resv->refs == 1 && (resv->flags & HAMMER_RESF_LAYER2FREE)) {
resv->append_off = HAMMER_BIGBLOCK_SIZE;
base_offset = hammer_xlate_to_zoneX(resv->zone, resv->zone_offset);
error = hammer_del_buffers(hmp, base_offset,
resv->zone_offset,
HAMMER_BIGBLOCK_SIZE,
1);
if (hammer_debug_general & 0x20000) {
hkprintf("delbgblk %016jx error %d\n",
(intmax_t)base_offset, error);
}
if (error)
hammer_reserve_setdelay(hmp, resv);
}
if (--resv->refs == 0) {
if (hammer_debug_general & 0x20000) {
hkprintf("delresvr %016jx zone %02x\n",
(intmax_t)resv->zone_offset, resv->zone);
}
KKASSERT((resv->flags & HAMMER_RESF_ONDELAY) == 0);
RB_REMOVE(hammer_res_rb_tree, &hmp->rb_resv_root, resv);
kfree(resv, hmp->m_misc);
--hammer_count_reservations;
}
}
static void
hammer_reserve_setdelay_offset(hammer_mount_t hmp, hammer_off_t base_offset,
int zone, hammer_blockmap_layer2_t layer2)
{
hammer_reserve_t resv;
again:
resv = RB_LOOKUP(hammer_res_rb_tree, &hmp->rb_resv_root, base_offset);
if (resv == NULL) {
resv = kmalloc(sizeof(*resv), hmp->m_misc,
M_WAITOK | M_ZERO | M_USE_RESERVE);
resv->zone = zone;
resv->zone_offset = base_offset;
resv->refs = 0;
resv->append_off = HAMMER_BIGBLOCK_SIZE;
if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE)
resv->flags |= HAMMER_RESF_LAYER2FREE;
if (RB_INSERT(hammer_res_rb_tree, &hmp->rb_resv_root, resv)) {
kfree(resv, hmp->m_misc);
goto again;
}
++hammer_count_reservations;
} else {
if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE)
resv->flags |= HAMMER_RESF_LAYER2FREE;
}
hammer_reserve_setdelay(hmp, resv);
}
static void
hammer_reserve_setdelay(hammer_mount_t hmp, hammer_reserve_t resv)
{
if (resv->flags & HAMMER_RESF_ONDELAY) {
TAILQ_REMOVE(&hmp->delay_list, resv, delay_entry);
resv->flg_no = hmp->flusher.next + 1;
TAILQ_INSERT_TAIL(&hmp->delay_list, resv, delay_entry);
} else {
++resv->refs;
++hmp->rsv_fromdelay;
resv->flags |= HAMMER_RESF_ONDELAY;
resv->flg_no = hmp->flusher.next + 1;
TAILQ_INSERT_TAIL(&hmp->delay_list, resv, delay_entry);
}
}
void
hammer_reserve_clrdelay(hammer_mount_t hmp, hammer_reserve_t resv)
{
KKASSERT(resv->flags & HAMMER_RESF_ONDELAY);
resv->flags &= ~HAMMER_RESF_ONDELAY;
TAILQ_REMOVE(&hmp->delay_list, resv, delay_entry);
--hmp->rsv_fromdelay;
hammer_blockmap_reserve_complete(hmp, resv);
}
void
hammer_blockmap_free(hammer_transaction_t trans,
hammer_off_t zone_offset, int bytes)
{
hammer_mount_t hmp;
hammer_volume_t root_volume;
hammer_blockmap_t freemap;
hammer_blockmap_layer1_t layer1;
hammer_blockmap_layer2_t layer2;
hammer_buffer_t buffer1 = NULL;
hammer_buffer_t buffer2 = NULL;
hammer_off_t layer1_offset;
hammer_off_t layer2_offset;
hammer_off_t base_off;
int error;
int zone;
if (bytes == 0)
return;
hmp = trans->hmp;
bytes = HAMMER_DATA_DOALIGN(bytes);
KKASSERT(bytes <= HAMMER_XBUFSIZE);
KKASSERT(((zone_offset ^ (zone_offset + (bytes - 1))) &
~HAMMER_BIGBLOCK_MASK64) == 0);
zone = HAMMER_ZONE_DECODE(zone_offset);
KKASSERT(hammer_is_index_record(zone));
root_volume = trans->rootvol;
error = 0;
freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset);
layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1);
if (error)
goto failed;
KKASSERT(layer1->phys_offset &&
layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL);
hammer_verify_layer1_crc(hmp, layer1);
layer2_offset = layer1->phys_offset +
HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset);
layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2);
if (error)
goto failed;
hammer_verify_layer2_crc(hmp, layer2);
hammer_lock_ex(&hmp->blkmap_lock);
hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2));
KKASSERT(layer2->zone == zone);
layer2->bytes_free += bytes;
KKASSERT(layer2->bytes_free <= HAMMER_BIGBLOCK_SIZE);
if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) {
base_off = hammer_xlate_to_zone2(zone_offset &
~HAMMER_BIGBLOCK_MASK64);
hammer_reserve_setdelay_offset(hmp, base_off, zone, layer2);
if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) {
layer2->zone = 0;
layer2->append_off = 0;
hammer_modify_buffer(trans, buffer1,
layer1, sizeof(*layer1));
++layer1->blocks_free;
hammer_crc_set_layer1(hmp->version, layer1);
hammer_modify_buffer_done(buffer1);
hammer_modify_volume_field(trans,
trans->rootvol,
vol0_stat_freebigblocks);
++root_volume->ondisk->vol0_stat_freebigblocks;
hmp->copy_stat_freebigblocks =
root_volume->ondisk->vol0_stat_freebigblocks;
hammer_modify_volume_done(trans->rootvol);
}
}
hammer_crc_set_layer2(hmp->version, layer2);
hammer_modify_buffer_done(buffer2);
hammer_unlock(&hmp->blkmap_lock);
failed:
if (buffer1)
hammer_rel_buffer(buffer1, 0);
if (buffer2)
hammer_rel_buffer(buffer2, 0);
}
int
hammer_blockmap_dedup(hammer_transaction_t trans,
hammer_off_t zone_offset, int bytes)
{
hammer_mount_t hmp;
hammer_blockmap_t freemap;
hammer_blockmap_layer1_t layer1;
hammer_blockmap_layer2_t layer2;
hammer_buffer_t buffer1 = NULL;
hammer_buffer_t buffer2 = NULL;
hammer_off_t layer1_offset;
hammer_off_t layer2_offset;
int32_t temp;
int error;
int zone __debugvar;
if (bytes == 0)
return (0);
hmp = trans->hmp;
bytes = HAMMER_DATA_DOALIGN(bytes);
KKASSERT(bytes <= HAMMER_BIGBLOCK_SIZE);
KKASSERT(((zone_offset ^ (zone_offset + (bytes - 1))) &
~HAMMER_BIGBLOCK_MASK64) == 0);
zone = HAMMER_ZONE_DECODE(zone_offset);
KKASSERT(hammer_is_index_record(zone));
error = 0;
freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset);
layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1);
if (error)
goto failed;
KKASSERT(layer1->phys_offset &&
layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL);
hammer_verify_layer1_crc(hmp, layer1);
layer2_offset = layer1->phys_offset +
HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset);
layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2);
if (error)
goto failed;
hammer_verify_layer2_crc(hmp, layer2);
hammer_lock_ex(&hmp->blkmap_lock);
hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2));
KKASSERT(layer2->zone == zone);
temp = layer2->bytes_free - HAMMER_BIGBLOCK_SIZE * 2;
cpu_ccfence();
if (temp > layer2->bytes_free) {
error = ERANGE;
goto underflow;
}
layer2->bytes_free -= bytes;
KKASSERT(layer2->bytes_free <= HAMMER_BIGBLOCK_SIZE);
hammer_crc_set_layer2(hmp->version, layer2);
underflow:
hammer_modify_buffer_done(buffer2);
hammer_unlock(&hmp->blkmap_lock);
failed:
if (buffer1)
hammer_rel_buffer(buffer1, 0);
if (buffer2)
hammer_rel_buffer(buffer2, 0);
return (error);
}
int
hammer_blockmap_finalize(hammer_transaction_t trans,
hammer_reserve_t resv,
hammer_off_t zone_offset, int bytes)
{
hammer_mount_t hmp;
hammer_volume_t root_volume;
hammer_blockmap_t freemap;
hammer_blockmap_layer1_t layer1;
hammer_blockmap_layer2_t layer2;
hammer_buffer_t buffer1 = NULL;
hammer_buffer_t buffer2 = NULL;
hammer_off_t layer1_offset;
hammer_off_t layer2_offset;
int error;
int zone;
int offset;
if (bytes == 0)
return(0);
hmp = trans->hmp;
bytes = HAMMER_DATA_DOALIGN(bytes);
KKASSERT(bytes <= HAMMER_XBUFSIZE);
zone = HAMMER_ZONE_DECODE(zone_offset);
KKASSERT(hammer_is_index_record(zone));
root_volume = trans->rootvol;
error = 0;
freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset);
layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1);
if (error)
goto failed;
KKASSERT(layer1->phys_offset &&
layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL);
hammer_verify_layer1_crc(hmp, layer1);
layer2_offset = layer1->phys_offset +
HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset);
layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2);
if (error)
goto failed;
hammer_verify_layer2_crc(hmp, layer2);
hammer_lock_ex(&hmp->blkmap_lock);
hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2));
if (layer2->zone == 0) {
hammer_modify_buffer(trans, buffer1, layer1, sizeof(*layer1));
--layer1->blocks_free;
hammer_crc_set_layer1(hmp->version, layer1);
hammer_modify_buffer_done(buffer1);
layer2->zone = zone;
KKASSERT(layer2->bytes_free == HAMMER_BIGBLOCK_SIZE);
KKASSERT(layer2->append_off == 0);
hammer_modify_volume_field(trans,
trans->rootvol,
vol0_stat_freebigblocks);
--root_volume->ondisk->vol0_stat_freebigblocks;
hmp->copy_stat_freebigblocks =
root_volume->ondisk->vol0_stat_freebigblocks;
hammer_modify_volume_done(trans->rootvol);
}
if (layer2->zone != zone)
hdkprintf("layer2 zone mismatch %d %d\n", layer2->zone, zone);
KKASSERT(layer2->zone == zone);
KKASSERT(bytes != 0);
layer2->bytes_free -= bytes;
if (resv)
resv->flags &= ~HAMMER_RESF_LAYER2FREE;
offset = ((int)zone_offset & HAMMER_BIGBLOCK_MASK) + bytes;
if (layer2->append_off < offset)
layer2->append_off = offset;
hammer_crc_set_layer2(hmp->version, layer2);
hammer_modify_buffer_done(buffer2);
hammer_unlock(&hmp->blkmap_lock);
failed:
if (buffer1)
hammer_rel_buffer(buffer1, 0);
if (buffer2)
hammer_rel_buffer(buffer2, 0);
return(error);
}
int
hammer_blockmap_getfree(hammer_mount_t hmp, hammer_off_t zone_offset,
int *curp, int *errorp)
{
hammer_volume_t root_volume;
hammer_blockmap_t blockmap;
hammer_blockmap_t freemap;
hammer_blockmap_layer1_t layer1;
hammer_blockmap_layer2_t layer2;
hammer_buffer_t buffer = NULL;
hammer_off_t layer1_offset;
hammer_off_t layer2_offset;
int32_t bytes;
int zone;
zone = HAMMER_ZONE_DECODE(zone_offset);
KKASSERT(hammer_is_index_record(zone));
root_volume = hammer_get_root_volume(hmp, errorp);
if (*errorp) {
*curp = 0;
return(0);
}
blockmap = &hmp->blockmap[zone];
freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset);
layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer);
if (*errorp) {
*curp = 0;
bytes = 0;
goto failed;
}
KKASSERT(layer1->phys_offset);
hammer_verify_layer1_crc(hmp, layer1);
layer2_offset = layer1->phys_offset +
HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset);
layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer);
if (*errorp) {
*curp = 0;
bytes = 0;
goto failed;
}
hammer_verify_layer2_crc(hmp, layer2);
KKASSERT(layer2->zone == zone);
bytes = layer2->bytes_free;
if ((blockmap->next_offset ^ zone_offset) & ~HAMMER_BIGBLOCK_MASK64)
*curp = 0;
else
*curp = 1;
failed:
if (buffer)
hammer_rel_buffer(buffer, 0);
hammer_rel_volume(root_volume, 0);
if (hammer_debug_general & 0x4000) {
hdkprintf("%016jx -> %d\n", (intmax_t)zone_offset, bytes);
}
return(bytes);
}
hammer_off_t
hammer_blockmap_lookup_verify(hammer_mount_t hmp, hammer_off_t zone_offset,
int *errorp)
{
hammer_volume_t root_volume;
hammer_blockmap_t freemap;
hammer_blockmap_layer1_t layer1;
hammer_blockmap_layer2_t layer2;
hammer_buffer_t buffer = NULL;
hammer_off_t layer1_offset;
hammer_off_t layer2_offset;
hammer_off_t result_offset;
hammer_off_t base_off;
hammer_reserve_t resv __debugvar;
int zone;
zone = HAMMER_ZONE_DECODE(zone_offset);
result_offset = hammer_xlate_to_zone2(zone_offset);
root_volume = hammer_get_root_volume(hmp, errorp);
if (*errorp)
return(0);
freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
KKASSERT(freemap->phys_offset != 0);
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset);
layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer);
if (*errorp)
goto failed;
KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL);
hammer_verify_layer1_crc(hmp, layer1);
layer2_offset = layer1->phys_offset +
HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset);
layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer);
if (*errorp)
goto failed;
if (layer2->zone == 0) {
base_off = hammer_xlate_to_zone2(zone_offset &
~HAMMER_BIGBLOCK_MASK64);
resv = RB_LOOKUP(hammer_res_rb_tree, &hmp->rb_resv_root,
base_off);
KKASSERT(resv && resv->zone == zone);
} else if (layer2->zone != zone) {
hpanic("bad zone %d/%d", layer2->zone, zone);
}
hammer_verify_layer2_crc(hmp, layer2);
failed:
if (buffer)
hammer_rel_buffer(buffer, 0);
hammer_rel_volume(root_volume, 0);
if (hammer_debug_general & 0x0800) {
hdkprintf("%016jx -> %016jx\n",
(intmax_t)zone_offset, (intmax_t)result_offset);
}
return(result_offset);
}
int
_hammer_checkspace(hammer_mount_t hmp, int slop, int64_t *resp)
{
const int in_size = sizeof(struct hammer_inode_data) +
sizeof(union hammer_btree_elm);
const int rec_size = (sizeof(union hammer_btree_elm) * 2);
int64_t usedbytes;
usedbytes = hmp->rsv_inodes * in_size +
hmp->rsv_recs * rec_size +
hmp->rsv_databytes +
((int64_t)hmp->rsv_fromdelay << HAMMER_BIGBLOCK_BITS) +
((int64_t)hammer_limit_dirtybufspace) +
(slop << HAMMER_BIGBLOCK_BITS);
if (resp)
*resp = usedbytes;
if (hmp->copy_stat_freebigblocks >=
(usedbytes >> HAMMER_BIGBLOCK_BITS)) {
return(0);
}
return (ENOSPC);
}
static int
hammer_check_volume(hammer_mount_t hmp, hammer_off_t *offsetp)
{
hammer_blockmap_t freemap;
hammer_blockmap_layer1_t layer1;
hammer_buffer_t buffer1 = NULL;
hammer_off_t layer1_offset;
int error = 0;
freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(*offsetp);
layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1);
if (error)
goto end;
if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL)
hammer_skip_volume(offsetp);
end:
if (buffer1)
hammer_rel_buffer(buffer1, 0);
return(error);
}
static void
hammer_skip_volume(hammer_off_t *offsetp)
{
hammer_off_t offset;
int zone, vol_no;
offset = *offsetp;
zone = HAMMER_ZONE_DECODE(offset);
vol_no = HAMMER_VOL_DECODE(offset) + 1;
KKASSERT(vol_no <= HAMMER_MAX_VOLUMES);
if (vol_no == HAMMER_MAX_VOLUMES) {
vol_no = 0;
++zone;
}
*offsetp = HAMMER_ENCODE(zone, vol_no, 0);
}