#include <sys/buf2.h>
#include "hammer.h"
static void hammer_io_modify(hammer_io_t io, int count);
static void hammer_io_deallocate(struct buf *bp);
static void hammer_indirect_callback(struct bio *bio);
static void hammer_io_direct_write_complete(struct bio *nbio);
static int hammer_io_direct_uncache_callback(hammer_inode_t ip, void *data);
static void hammer_io_set_modlist(hammer_io_t io);
static __inline void hammer_io_flush_mark(hammer_volume_t volume);
static struct bio_ops hammer_bioops;
static int
hammer_mod_rb_compare(hammer_io_t io1, hammer_io_t io2)
{
hammer_off_t io1_offset;
hammer_off_t io2_offset;
io1_offset = HAMMER_ENCODE(0, io1->volume->vol_no, io1->offset);
io2_offset = HAMMER_ENCODE(0, io2->volume->vol_no, io2->offset);
if (io1_offset < io2_offset)
return(-1);
if (io1_offset > io2_offset)
return(1);
return(0);
}
RB_GENERATE(hammer_mod_rb_tree, hammer_io, rb_node, hammer_mod_rb_compare);
void
hammer_io_init(hammer_io_t io, hammer_volume_t volume, hammer_io_type_t type)
{
io->volume = volume;
io->hmp = volume->io.hmp;
io->type = type;
}
hammer_io_type_t
hammer_zone_to_iotype(int zone)
{
hammer_io_type_t iotype;
switch(zone) {
case HAMMER_ZONE_RAW_VOLUME_INDEX:
iotype = HAMMER_IOTYPE_VOLUME;
break;
case HAMMER_ZONE_RAW_BUFFER_INDEX:
case HAMMER_ZONE_FREEMAP_INDEX:
case HAMMER_ZONE_BTREE_INDEX:
case HAMMER_ZONE_META_INDEX:
iotype = HAMMER_IOTYPE_META_BUFFER;
break;
case HAMMER_ZONE_UNDO_INDEX:
iotype = HAMMER_IOTYPE_UNDO_BUFFER;
break;
case HAMMER_ZONE_LARGE_DATA_INDEX:
case HAMMER_ZONE_SMALL_DATA_INDEX:
iotype = HAMMER_IOTYPE_DATA_BUFFER;
break;
default:
iotype = HAMMER_IOTYPE_DUMMY;
break;
}
return(iotype);
}
static const char*
hammer_io_to_iostring(hammer_io_t io)
{
const char *iostr = NULL;
switch(io->type) {
case HAMMER_IOTYPE_VOLUME:
iostr = "volume";
break;
case HAMMER_IOTYPE_META_BUFFER:
switch(HAMMER_ZONE(HAMMER_ITOB(io)->zoneX_offset)) {
case HAMMER_ZONE_RAW_BUFFER:
iostr = "meta/raw_buffer";
break;
case HAMMER_ZONE_FREEMAP:
iostr = "meta/freemap";
break;
case HAMMER_ZONE_BTREE:
iostr = "meta/btree";
break;
case HAMMER_ZONE_META:
iostr = "meta/meta";
break;
}
break;
case HAMMER_IOTYPE_UNDO_BUFFER:
iostr = "undo";
break;
case HAMMER_IOTYPE_DATA_BUFFER:
switch(HAMMER_ZONE(HAMMER_ITOB(io)->zoneX_offset)) {
case HAMMER_ZONE_LARGE_DATA:
iostr = "data/large_data";
break;
case HAMMER_ZONE_SMALL_DATA:
iostr = "data/small_data";
break;
}
break;
case HAMMER_IOTYPE_DUMMY:
iostr = "dummy";
break;
default:
hpanic("bad io type");
break;
}
return(iostr);
}
static void
hammer_io_disassociate(hammer_io_t io)
{
struct buf *bp = io->bp;
KKASSERT(io->released);
KKASSERT(io->modified == 0);
KKASSERT(hammer_buf_peek_io(bp) == io);
buf_dep_init(bp);
io->bp = NULL;
if (bp->b_flags & B_LOCKED) {
atomic_add_int(&hammer_count_io_locked, -1);
bp->b_flags &= ~B_LOCKED;
}
if (io->reclaim) {
bp->b_flags |= B_NOCACHE|B_RELBUF;
io->reclaim = 0;
}
switch(io->type) {
case HAMMER_IOTYPE_VOLUME:
HAMMER_ITOV(io)->ondisk = NULL;
break;
case HAMMER_IOTYPE_DATA_BUFFER:
case HAMMER_IOTYPE_META_BUFFER:
case HAMMER_IOTYPE_UNDO_BUFFER:
HAMMER_ITOB(io)->ondisk = NULL;
break;
case HAMMER_IOTYPE_DUMMY:
hpanic("bad io type");
break;
}
}
void
hammer_io_wait(hammer_io_t io)
{
if (io->running) {
hammer_mount_t hmp = io->hmp;
lwkt_gettoken(&hmp->io_token);
while (io->running) {
io->waiting = 1;
tsleep_interlock(io, 0);
if (io->running)
tsleep(io, PINTERLOCKED, "hmrflw", hz);
}
lwkt_reltoken(&hmp->io_token);
}
}
void
hammer_io_wait_all(hammer_mount_t hmp, const char *ident, int doflush)
{
struct hammer_io iodummy;
hammer_io_t io;
lwkt_gettoken(&hmp->io_token);
if (TAILQ_EMPTY(&hmp->iorun_list)) {
lwkt_reltoken(&hmp->io_token);
if (doflush)
hammer_io_flush_sync(hmp);
return;
}
bzero(&iodummy, sizeof(iodummy));
iodummy.type = HAMMER_IOTYPE_DUMMY;
TAILQ_INSERT_TAIL(&hmp->iorun_list, &iodummy, iorun_entry);
while (TAILQ_FIRST(&hmp->iorun_list) != &iodummy) {
tsleep(&iodummy, 0, ident, 0);
}
TAILQ_REMOVE(&hmp->iorun_list, &iodummy, iorun_entry);
io = TAILQ_FIRST(&hmp->iorun_list);
if (io && io->type == HAMMER_IOTYPE_DUMMY)
wakeup(io);
lwkt_reltoken(&hmp->io_token);
if (doflush)
hammer_io_flush_sync(hmp);
}
void
hammer_io_clear_error(hammer_io_t io)
{
hammer_mount_t hmp = io->hmp;
lwkt_gettoken(&hmp->io_token);
if (io->ioerror) {
io->ioerror = 0;
hammer_rel(&io->lock);
KKASSERT(hammer_isactive(&io->lock));
}
lwkt_reltoken(&hmp->io_token);
}
void
hammer_io_clear_error_noassert(hammer_io_t io)
{
hammer_mount_t hmp = io->hmp;
lwkt_gettoken(&hmp->io_token);
if (io->ioerror) {
io->ioerror = 0;
hammer_rel(&io->lock);
}
lwkt_reltoken(&hmp->io_token);
}
void
hammer_io_notmeta(hammer_buffer_t buffer)
{
if ((buffer->io.bp->b_flags & B_NOTMETA) == 0) {
hammer_mount_t hmp = buffer->io.hmp;
lwkt_gettoken(&hmp->io_token);
buffer->io.bp->b_flags |= B_NOTMETA;
lwkt_reltoken(&hmp->io_token);
}
}
int
hammer_io_read(struct vnode *devvp, hammer_io_t io, int limit)
{
struct buf *bp;
int error;
if ((bp = io->bp) == NULL) {
int hce = hammer_cluster_enable;
atomic_add_long(&hammer_count_io_running_read, io->bytes);
if (hce && limit > io->bytes) {
error = cluster_read(devvp, io->offset + limit,
io->offset, io->bytes,
HAMMER_CLUSTER_SIZE,
HAMMER_CLUSTER_SIZE * hce,
&io->bp);
} else {
error = bread(devvp, io->offset, io->bytes, &io->bp);
}
hammer_stats_disk_read += io->bytes;
atomic_add_long(&hammer_count_io_running_read, -io->bytes);
bp = io->bp;
if ((hammer_debug_io & 0x0001) && (bp->b_flags & B_IOISSUED)) {
hdkprintf("zone2_offset %016jx %s\n",
(intmax_t)bp->b_bio2.bio_offset,
hammer_io_to_iostring(io));
}
bp->b_flags &= ~B_IOISSUED;
bp->b_ops = &hammer_bioops;
hammer_buf_attach_io(bp, io);
BUF_KERNPROC(bp);
KKASSERT(io->modified == 0);
KKASSERT(io->running == 0);
KKASSERT(io->waiting == 0);
io->released = 0;
} else {
error = 0;
}
return(error);
}
int
hammer_io_new(struct vnode *devvp, hammer_io_t io)
{
struct buf *bp;
if ((bp = io->bp) == NULL) {
io->bp = getblk(devvp, io->offset, io->bytes, 0, 0);
bp = io->bp;
bp->b_ops = &hammer_bioops;
hammer_buf_attach_io(bp, io);
io->released = 0;
KKASSERT(io->running == 0);
io->waiting = 0;
BUF_KERNPROC(bp);
} else {
if (io->released) {
regetblk(bp);
BUF_KERNPROC(bp);
io->released = 0;
}
}
hammer_io_modify(io, 0);
vfs_bio_clrbuf(bp);
return(0);
}
void
hammer_io_advance(hammer_io_t io)
{
if (io->bp)
buf_act_advance(io->bp);
}
int
hammer_io_inval(hammer_volume_t volume, hammer_off_t zone2_offset)
{
hammer_io_t io;
hammer_mount_t hmp;
hammer_off_t phys_offset;
struct buf *bp;
int error;
hmp = volume->io.hmp;
lwkt_gettoken(&hmp->io_token);
phys_offset = hammer_xlate_to_phys(volume->ondisk, zone2_offset);
if ((bp = findblk(volume->devvp, phys_offset, 0)) != NULL)
bremfree(bp);
else
bp = getblk(volume->devvp, phys_offset, HAMMER_BUFSIZE, 0, 0);
if ((io = hammer_buf_peek_io(bp)) != NULL) {
#if 0
hammer_ref(&io->lock);
hammer_io_clear_modify(io, 1);
bundirty(bp);
io->released = 0;
BUF_KERNPROC(bp);
io->reclaim = 1;
io->waitdep = 1;
KKASSERT(hammer_isactive(&io->lock) == 1);
hammer_rel_buffer(HAMMER_ITOB(io), 0);
#endif
bqrelse(bp);
error = EAGAIN;
} else {
KKASSERT((bp->b_flags & B_LOCKED) == 0);
bundirty(bp);
bp->b_flags |= B_NOCACHE|B_RELBUF;
brelse(bp);
error = 0;
}
lwkt_reltoken(&hmp->io_token);
return(error);
}
struct buf *
hammer_io_release(hammer_io_t io, int flush)
{
struct buf *bp;
if ((bp = io->bp) == NULL)
return(NULL);
if (io->modified) {
if (flush) {
hammer_io_flush(io, 0);
} else if (bp->b_flags & B_LOCKED) {
switch(io->type) {
case HAMMER_IOTYPE_DATA_BUFFER:
hammer_io_flush(io, 0);
break;
case HAMMER_IOTYPE_UNDO_BUFFER:
hammer_io_flush(io, hammer_undo_reclaim(io));
break;
default:
break;
}
}
}
if (io->waitdep && io->running) {
hammer_io_wait(io);
}
if ((flush || io->reclaim) && io->modified == 0 && io->running == 0) {
if (io->released) {
regetblk(bp);
BUF_KERNPROC(bp);
} else {
io->released = 1;
}
hammer_io_disassociate(io);
} else if (io->modified) {
switch(io->type) {
case HAMMER_IOTYPE_DATA_BUFFER:
case HAMMER_IOTYPE_UNDO_BUFFER:
if (io->released == 0) {
io->released = 1;
bp->b_flags |= B_CLUSTEROK;
bdwrite(bp);
}
break;
default:
break;
}
bp = NULL;
} else if (io->released == 0) {
io->released = 1;
if (bp->b_flags & B_LOCKED) {
hammer_io_disassociate(io);
} else {
if (io->reclaim) {
hammer_io_disassociate(io);
} else {
}
}
} else {
if (io->running == 0) {
regetblk(bp);
if ((bp->b_flags & B_LOCKED) || io->reclaim) {
hammer_io_disassociate(io);
} else {
}
} else {
bp = NULL;
}
}
return(bp);
}
void
hammer_io_flush(hammer_io_t io, int reclaim)
{
struct buf *bp;
hammer_mount_t hmp;
if (io->modified == 0)
return;
KKASSERT(io->bp);
KKASSERT(io->modify_refs <= 0);
hmp = io->hmp;
bp = io->bp;
if (io->released) {
regetblk(bp);
KKASSERT(io->released);
KKASSERT(io->bp == bp);
} else {
io->released = 1;
}
if (reclaim) {
io->reclaim = 1;
if ((bp->b_flags & B_LOCKED) == 0) {
bp->b_flags |= B_LOCKED;
atomic_add_int(&hammer_count_io_locked, 1);
}
}
hammer_ref(&io->lock);
hammer_io_clear_modify(io, 0);
hammer_rel(&io->lock);
if (hammer_debug_io & 0x0002)
hdkprintf("%016jx\n", bp->b_bio1.bio_offset);
io->running = 1;
atomic_add_long(&hmp->io_running_space, io->bytes);
atomic_add_long(&hammer_count_io_running_write, io->bytes);
lwkt_gettoken(&hmp->io_token);
TAILQ_INSERT_TAIL(&hmp->iorun_list, io, iorun_entry);
lwkt_reltoken(&hmp->io_token);
cluster_awrite(bp);
hammer_io_flush_mark(io->volume);
}
static
void
hammer_io_modify(hammer_io_t io, int count)
{
while (io->modify_refs < 0) {
io->waitmod = 1;
tsleep(io, 0, "hmrmod", 0);
}
KKASSERT(hammer_isactive(&io->lock) && io->bp != NULL);
io->modify_refs += count;
if (io->modified && io->released == 0)
return;
hammer_lock_ex(&io->lock);
if (io->released) {
regetblk(io->bp);
BUF_KERNPROC(io->bp);
io->released = 0;
}
if (io->modified == 0) {
hammer_io_set_modlist(io);
io->modified = 1;
}
hammer_unlock(&io->lock);
}
static __inline
void
hammer_io_modify_done(hammer_io_t io)
{
KKASSERT(io->modify_refs > 0);
--io->modify_refs;
if (io->modify_refs == 0 && io->waitmod) {
io->waitmod = 0;
wakeup(io);
}
}
void
hammer_io_write_interlock(hammer_io_t io)
{
hammer_mount_t hmp = io->hmp;
lwkt_gettoken(&hmp->io_token);
while (io->modify_refs != 0) {
io->waitmod = 1;
tsleep(io, 0, "hmrmod", 0);
}
io->modify_refs = -1;
lwkt_reltoken(&hmp->io_token);
}
void
hammer_io_done_interlock(hammer_io_t io)
{
KKASSERT(io->modify_refs == -1);
io->modify_refs = 0;
if (io->waitmod) {
io->waitmod = 0;
wakeup(io);
}
}
void
hammer_modify_volume(hammer_transaction_t trans, hammer_volume_t volume,
void *base, int len)
{
KKASSERT (trans == NULL || trans->sync_lock_refs > 0);
hammer_io_modify(&volume->io, 1);
if (len) {
intptr_t rel_offset = (intptr_t)base - (intptr_t)volume->ondisk;
KKASSERT((rel_offset & ~(intptr_t)HAMMER_BUFMASK) == 0);
hammer_generate_undo(trans,
HAMMER_ENCODE_RAW_VOLUME(volume->vol_no, rel_offset),
base, len);
}
}
void
hammer_modify_buffer(hammer_transaction_t trans, hammer_buffer_t buffer,
void *base, int len)
{
KKASSERT (trans == NULL || trans->sync_lock_refs > 0);
hammer_io_modify(&buffer->io, 1);
if (len) {
intptr_t rel_offset = (intptr_t)base - (intptr_t)buffer->ondisk;
KKASSERT((rel_offset & ~(intptr_t)HAMMER_BUFMASK) == 0);
hammer_generate_undo(trans,
buffer->zone2_offset + rel_offset,
base, len);
}
}
void
hammer_modify_volume_done(hammer_volume_t volume)
{
hammer_io_modify_done(&volume->io);
}
void
hammer_modify_buffer_done(hammer_buffer_t buffer)
{
hammer_io_modify_done(&buffer->io);
}
void
hammer_io_clear_modify(hammer_io_t io, int inval)
{
hammer_mount_t hmp;
if (io->modified == 0)
return;
hmp = io->hmp;
lwkt_gettoken(&hmp->io_token);
if (io->modified == 0) {
lwkt_reltoken(&hmp->io_token);
return;
}
KKASSERT(io->mod_root != NULL);
if (io->mod_root == &io->hmp->volu_root ||
io->mod_root == &io->hmp->meta_root) {
io->hmp->locked_dirty_space -= io->bytes;
atomic_add_long(&hammer_count_dirtybufspace, -io->bytes);
}
RB_REMOVE(hammer_mod_rb_tree, io->mod_root, io);
io->mod_root = NULL;
io->modified = 0;
lwkt_reltoken(&hmp->io_token);
if (io->gencrc == 0)
return;
io->gencrc = 0;
if (io->type == HAMMER_IOTYPE_META_BUFFER) {
hammer_buffer_t buffer = HAMMER_ITOB(io);
hammer_node_t node;
restart:
TAILQ_FOREACH(node, &buffer->node_list, entry) {
if ((node->flags & HAMMER_NODE_NEEDSCRC) == 0)
continue;
node->flags &= ~HAMMER_NODE_NEEDSCRC;
KKASSERT(node->ondisk);
if (inval == 0)
hammer_crc_set_btree(hmp->version, node->ondisk);
hammer_rel_node(node);
goto restart;
}
}
KKASSERT(hammer_isactive(&io->lock));
}
void
hammer_io_clear_modlist(hammer_io_t io)
{
hammer_mount_t hmp = io->hmp;
KKASSERT(io->modified == 0);
if (io->mod_root) {
lwkt_gettoken(&hmp->io_token);
if (io->mod_root) {
KKASSERT(io->mod_root == &io->hmp->lose_root);
RB_REMOVE(hammer_mod_rb_tree, io->mod_root, io);
io->mod_root = NULL;
}
lwkt_reltoken(&hmp->io_token);
}
}
static void
hammer_io_set_modlist(hammer_io_t io)
{
hammer_mount_t hmp = io->hmp;
lwkt_gettoken(&hmp->io_token);
KKASSERT(io->mod_root == NULL);
switch(io->type) {
case HAMMER_IOTYPE_VOLUME:
io->mod_root = &hmp->volu_root;
hmp->locked_dirty_space += io->bytes;
atomic_add_long(&hammer_count_dirtybufspace, io->bytes);
break;
case HAMMER_IOTYPE_META_BUFFER:
io->mod_root = &hmp->meta_root;
hmp->locked_dirty_space += io->bytes;
atomic_add_long(&hammer_count_dirtybufspace, io->bytes);
break;
case HAMMER_IOTYPE_UNDO_BUFFER:
io->mod_root = &hmp->undo_root;
break;
case HAMMER_IOTYPE_DATA_BUFFER:
io->mod_root = &hmp->data_root;
break;
case HAMMER_IOTYPE_DUMMY:
hpanic("bad io type");
break;
}
if (RB_INSERT(hammer_mod_rb_tree, io->mod_root, io)) {
hpanic("duplicate entry @ %d:%015jx",
io->volume->vol_no, io->offset);
}
lwkt_reltoken(&hmp->io_token);
}
static void
hammer_io_start(struct buf *bp)
{
}
static void
hammer_io_complete(struct buf *bp)
{
hammer_io_t io = hammer_buf_peek_io(bp);
hammer_mount_t hmp = io->hmp;
hammer_io_t ionext;
lwkt_gettoken(&hmp->io_token);
KKASSERT(io->released == 1);
if (io->running) {
if (bp->b_flags & B_ERROR) {
lwkt_gettoken(&hmp->fs_token);
hammer_critical_error(hmp, NULL, bp->b_error,
"while flushing meta-data");
lwkt_reltoken(&hmp->fs_token);
switch(io->type) {
case HAMMER_IOTYPE_UNDO_BUFFER:
break;
default:
if (io->ioerror == 0) {
io->ioerror = 1;
hammer_ref(&io->lock);
}
break;
}
bp->b_flags &= ~B_ERROR;
bundirty(bp);
#if 0
hammer_io_set_modlist(io);
io->modified = 1;
#endif
}
hammer_stats_disk_write += io->bytes;
atomic_add_long(&hammer_count_io_running_write, -io->bytes);
atomic_add_long(&hmp->io_running_space, -io->bytes);
KKASSERT(hmp->io_running_space >= 0);
io->running = 0;
if (TAILQ_FIRST(&hmp->iorun_list) == io) {
ionext = TAILQ_NEXT(io, iorun_entry);
if (ionext && ionext->type == HAMMER_IOTYPE_DUMMY)
wakeup(ionext);
}
TAILQ_REMOVE(&hmp->iorun_list, io, iorun_entry);
} else {
hammer_stats_disk_read += io->bytes;
}
if (io->waiting) {
io->waiting = 0;
wakeup(io);
}
if (bp->b_flags & B_LOCKED) {
atomic_add_int(&hammer_count_io_locked, -1);
bp->b_flags &= ~B_LOCKED;
hammer_io_deallocate(bp);
}
lwkt_reltoken(&hmp->io_token);
}
static void
hammer_io_deallocate(struct buf *bp)
{
hammer_io_t io = hammer_buf_peek_io(bp);
hammer_mount_t hmp;
hmp = io->hmp;
lwkt_gettoken(&hmp->io_token);
KKASSERT((bp->b_flags & B_LOCKED) == 0 && io->running == 0);
if (hammer_try_interlock_norefs(&io->lock) == 0) {
bp->b_flags |= B_LOCKED;
atomic_add_int(&hammer_count_io_locked, 1);
} else if (io->modified) {
bp->b_flags |= B_LOCKED;
atomic_add_int(&hammer_count_io_locked, 1);
hammer_put_interlock(&io->lock, 0);
} else {
hammer_io_disassociate(io);
if (io->type != HAMMER_IOTYPE_VOLUME) {
KKASSERT(io->bp == NULL);
KKASSERT(io->mod_root == NULL);
io->mod_root = &hmp->lose_root;
if (RB_INSERT(hammer_mod_rb_tree, io->mod_root, io)) {
hpanic("duplicate entry @ %d:%015jx",
io->volume->vol_no, io->offset);
}
}
hammer_put_interlock(&io->lock, 1);
}
lwkt_reltoken(&hmp->io_token);
}
static int
hammer_io_fsync(struct vnode *vp)
{
return(0);
}
static int
hammer_io_sync(struct mount *mp)
{
return(0);
}
static void
hammer_io_movedeps(struct buf *bp1, struct buf *bp2)
{
}
static int
hammer_io_checkread(struct buf *bp)
{
return(0);
}
static int
hammer_io_checkwrite(struct buf *bp)
{
hammer_io_t io = hammer_buf_peek_io(bp);
hammer_mount_t hmp = io->hmp;
lwkt_gettoken(&hmp->io_token);
if (io->type == HAMMER_IOTYPE_VOLUME ||
io->type == HAMMER_IOTYPE_META_BUFFER) {
if (!panicstr)
hpanic("illegal buffer");
if ((bp->b_flags & B_LOCKED) == 0) {
bp->b_flags |= B_LOCKED;
atomic_add_int(&hammer_count_io_locked, 1);
}
lwkt_reltoken(&hmp->io_token);
return(1);
}
if (hammer_try_interlock_norefs(&io->lock) == 0) {
bp->b_flags |= B_LOCKED;
atomic_add_int(&hammer_count_io_locked, 1);
lwkt_reltoken(&hmp->io_token);
return(1);
}
if (io->modify_refs == 0 && io->modified) {
hammer_ref(&io->lock);
hammer_io_clear_modify(io, 0);
hammer_rel(&io->lock);
} else if (io->modified) {
KKASSERT(io->type == HAMMER_IOTYPE_DATA_BUFFER);
}
KKASSERT(io->running == 0);
io->running = 1;
atomic_add_long(&io->hmp->io_running_space, io->bytes);
atomic_add_long(&hammer_count_io_running_write, io->bytes);
TAILQ_INSERT_TAIL(&io->hmp->iorun_list, io, iorun_entry);
hammer_put_interlock(&io->lock, 1);
lwkt_reltoken(&hmp->io_token);
return(0);
}
static int
hammer_io_countdeps(struct buf *bp, int n)
{
return(0);
}
static struct bio_ops hammer_bioops = {
.io_start = hammer_io_start,
.io_complete = hammer_io_complete,
.io_deallocate = hammer_io_deallocate,
.io_fsync = hammer_io_fsync,
.io_sync = hammer_io_sync,
.io_movedeps = hammer_io_movedeps,
.io_countdeps = hammer_io_countdeps,
.io_checkread = hammer_io_checkread,
.io_checkwrite = hammer_io_checkwrite,
};
int
hammer_io_direct_read(hammer_mount_t hmp, struct bio *bio,
hammer_btree_leaf_elm_t leaf)
{
hammer_off_t buf_offset;
hammer_off_t zone2_offset;
hammer_volume_t volume;
struct buf *bp;
struct bio *nbio;
int vol_no;
int error;
buf_offset = bio->bio_offset;
KKASSERT(hammer_is_zone_large_data(buf_offset));
bp = bio->bio_buf;
hammer_sync_buffers(hmp, buf_offset, bp->b_bufsize);
zone2_offset = hammer_blockmap_lookup(hmp, buf_offset, &error);
if (error)
goto done;
KKASSERT(hammer_is_zone_raw_buffer(zone2_offset));
vol_no = HAMMER_VOL_DECODE(zone2_offset);
volume = hammer_get_volume(hmp, vol_no, &error);
if (error == 0 && zone2_offset >= volume->maxbuf_off)
error = EIO;
if (error == 0) {
nbio = push_bio(bio);
nbio->bio_offset = hammer_xlate_to_phys(volume->ondisk,
zone2_offset);
hammer_stats_disk_read += bp->b_bufsize;
vn_strategy(volume->devvp, nbio);
}
hammer_rel_volume(volume, 0);
done:
if (error) {
hdkprintf("failed @ %016jx\n", (intmax_t)zone2_offset);
bp->b_error = error;
bp->b_flags |= B_ERROR;
biodone(bio);
}
return(error);
}
int
hammer_io_indirect_read(hammer_mount_t hmp, struct bio *bio,
hammer_btree_leaf_elm_t leaf)
{
hammer_off_t buf_offset;
hammer_off_t zone2_offset;
hammer_volume_t volume;
struct buf *bp;
int vol_no;
int error;
buf_offset = bio->bio_offset;
KKASSERT(hammer_is_zone_large_data(buf_offset));
bp = bio->bio_buf;
hammer_sync_buffers(hmp, buf_offset, bp->b_bufsize);
zone2_offset = hammer_blockmap_lookup(hmp, buf_offset, &error);
if (error)
goto done;
KKASSERT(hammer_is_zone_raw_buffer(zone2_offset));
vol_no = HAMMER_VOL_DECODE(zone2_offset);
volume = hammer_get_volume(hmp, vol_no, &error);
if (error == 0 && zone2_offset >= volume->maxbuf_off)
error = EIO;
if (error == 0) {
hammer_off_t limit;
int hce;
buf_offset = hammer_xlate_to_phys(volume->ondisk, zone2_offset);
if (leaf && hammer_verify_data) {
bio->bio_caller_info1.uvalue32 = leaf->data_crc;
bio->bio_caller_info2.index = 1;
} else {
bio->bio_caller_info2.index = 0;
}
bio->bio_caller_info3.ptr = hmp;
hce = hammer_cluster_enable;
if (hce > 0) {
limit = HAMMER_BIGBLOCK_DOALIGN(zone2_offset);
limit -= zone2_offset;
cluster_readcb(volume->devvp, limit, buf_offset,
bp->b_bufsize,
B_NOTMETA,
HAMMER_CLUSTER_SIZE,
HAMMER_CLUSTER_SIZE * hce,
hammer_indirect_callback,
bio);
} else {
breadcb(volume->devvp, buf_offset, bp->b_bufsize,
B_NOTMETA,
hammer_indirect_callback, bio);
}
}
hammer_rel_volume(volume, 0);
done:
if (error) {
hdkprintf("failed @ %016jx\n", (intmax_t)zone2_offset);
bp->b_error = error;
bp->b_flags |= B_ERROR;
biodone(bio);
}
return(error);
}
static void
hammer_indirect_callback(struct bio *bio)
{
struct buf *bp = bio->bio_buf;
struct buf *obp;
struct bio *obio;
hammer_mount_t hmp;
if ((bio->bio_flags & BIO_DONE) == 0)
bpdone(bp, 0);
bio->bio_flags &= ~(BIO_DONE | BIO_SYNC);
obio = bio->bio_caller_info1.ptr;
obp = obio->bio_buf;
hmp = obio->bio_caller_info3.ptr;
if (bp->b_flags & B_ERROR) {
obp->b_flags |= B_ERROR;
obp->b_error = bp->b_error;
} else if (obio->bio_caller_info2.index &&
obio->bio_caller_info1.uvalue32 !=
hammer_datacrc(hmp->version,
bp->b_data, obp->b_bufsize) &&
obio->bio_caller_info1.uvalue32 !=
hammer_datacrc(HAMMER_VOL_VERSION_SIX,
bp->b_data, obp->b_bufsize)) {
obp->b_flags |= B_ERROR;
obp->b_error = EIO;
} else {
KKASSERT(bp->b_bufsize >= obp->b_bufsize);
bcopy(bp->b_data, obp->b_data, obp->b_bufsize);
obp->b_resid = 0;
obp->b_flags |= B_AGE;
}
biodone(obio);
bqrelse(bp);
}
int
hammer_io_direct_write(hammer_mount_t hmp, struct bio *bio,
hammer_record_t record)
{
hammer_btree_leaf_elm_t leaf = &record->leaf;
hammer_off_t buf_offset;
hammer_off_t zone2_offset;
hammer_volume_t volume;
hammer_buffer_t buffer;
struct buf *bp;
struct bio *nbio;
char *ptr;
int vol_no;
int error;
buf_offset = leaf->data_offset;
KKASSERT(hammer_is_zone_record(buf_offset));
KKASSERT(bio->bio_buf->b_cmd == BUF_CMD_WRITE);
if ((buf_offset & HAMMER_BUFMASK) == 0 &&
leaf->data_len >= HAMMER_BUFSIZE) {
zone2_offset = hammer_blockmap_lookup(hmp, buf_offset, &error);
vol_no = HAMMER_VOL_DECODE(zone2_offset);
volume = hammer_get_volume(hmp, vol_no, &error);
if (error == 0 && zone2_offset >= volume->maxbuf_off)
error = EIO;
if (error == 0) {
bp = bio->bio_buf;
KKASSERT((bp->b_bufsize & HAMMER_BUFMASK) == 0);
nbio = push_bio(bio);
nbio->bio_offset = zone2_offset;
nbio->bio_done = hammer_io_direct_write_complete;
nbio->bio_caller_info1.ptr = record;
record->zone2_offset = zone2_offset;
record->gflags |= HAMMER_RECG_DIRECT_IO |
HAMMER_RECG_DIRECT_INVAL;
nbio = push_bio(nbio);
nbio->bio_offset = hammer_xlate_to_phys(volume->ondisk,
zone2_offset);
hammer_stats_disk_write += bp->b_bufsize;
hammer_ip_replace_bulk(hmp, record);
vn_strategy(volume->devvp, nbio);
hammer_io_flush_mark(volume);
}
hammer_rel_volume(volume, 0);
} else {
KKASSERT(((buf_offset ^ (buf_offset + leaf->data_len - 1)) & ~HAMMER_BUFMASK64) == 0);
buffer = NULL;
ptr = hammer_bread(hmp, buf_offset, &error, &buffer);
if (error == 0) {
bp = bio->bio_buf;
bp->b_flags |= B_AGE;
hammer_io_modify(&buffer->io, 1);
bcopy(bp->b_data, ptr, leaf->data_len);
hammer_io_modify_done(&buffer->io);
hammer_rel_buffer(buffer, 0);
bp->b_resid = 0;
hammer_ip_replace_bulk(hmp, record);
biodone(bio);
}
}
if (error) {
hdkprintf("failed @ %016jx\n", (intmax_t)leaf->data_offset);
bp = bio->bio_buf;
bp->b_resid = 0;
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
biodone(bio);
record->flags |= HAMMER_RECF_DELETED_FE;
hammer_rel_mem_record(record);
}
return(error);
}
static
void
hammer_io_direct_write_complete(struct bio *nbio)
{
struct bio *obio;
struct buf *bp;
hammer_record_t record;
hammer_mount_t hmp;
record = nbio->bio_caller_info1.ptr;
KKASSERT(record != NULL);
hmp = record->ip->hmp;
lwkt_gettoken(&hmp->io_token);
bp = nbio->bio_buf;
obio = pop_bio(nbio);
if (bp->b_flags & B_ERROR) {
lwkt_gettoken(&hmp->fs_token);
hammer_critical_error(hmp, record->ip, bp->b_error,
"while writing bulk data");
lwkt_reltoken(&hmp->fs_token);
bp->b_flags |= B_INVAL;
}
KKASSERT(record->gflags & HAMMER_RECG_DIRECT_IO);
if (record->gflags & HAMMER_RECG_DIRECT_WAIT) {
record->gflags &= ~(HAMMER_RECG_DIRECT_IO |
HAMMER_RECG_DIRECT_WAIT);
wakeup(&record->flags);
} else {
record->gflags &= ~HAMMER_RECG_DIRECT_IO;
}
lwkt_reltoken(&hmp->io_token);
biodone(obio);
}
void
hammer_io_direct_wait(hammer_record_t record)
{
hammer_mount_t hmp = record->ip->hmp;
if (record->gflags & HAMMER_RECG_DIRECT_IO) {
lwkt_gettoken(&hmp->io_token);
while (record->gflags & HAMMER_RECG_DIRECT_IO) {
record->gflags |= HAMMER_RECG_DIRECT_WAIT;
tsleep(&record->flags, 0, "hmdiow", 0);
}
lwkt_reltoken(&hmp->io_token);
}
if (record->gflags & HAMMER_RECG_DIRECT_INVAL) {
KKASSERT(record->leaf.data_offset);
hammer_del_buffers(hmp, record->leaf.data_offset,
record->zone2_offset, record->leaf.data_len,
1);
record->gflags &= ~HAMMER_RECG_DIRECT_INVAL;
}
}
void
hammer_io_direct_uncache(hammer_mount_t hmp, hammer_btree_leaf_elm_t leaf)
{
struct hammer_inode_info iinfo;
int zone;
if (leaf->base.rec_type != HAMMER_RECTYPE_DATA)
return;
zone = HAMMER_ZONE_DECODE(leaf->data_offset);
if (zone != HAMMER_ZONE_LARGE_DATA_INDEX)
return;
iinfo.obj_id = leaf->base.obj_id;
iinfo.obj_asof = 0;
iinfo.obj_localization = leaf->base.localization &
HAMMER_LOCALIZE_PSEUDOFS_MASK;
iinfo.u.leaf = leaf;
hammer_scan_inode_snapshots(hmp, &iinfo,
hammer_io_direct_uncache_callback,
leaf);
}
static int
hammer_io_direct_uncache_callback(hammer_inode_t ip, void *data)
{
hammer_inode_info_t iinfo = data;
hammer_off_t file_offset;
struct vnode *vp;
struct buf *bp;
int blksize;
if (ip->vp == NULL)
return(0);
file_offset = iinfo->u.leaf->base.key - iinfo->u.leaf->data_len;
blksize = iinfo->u.leaf->data_len;
KKASSERT((blksize & HAMMER_BUFMASK) == 0);
hammer_ref(&ip->lock);
if (hammer_get_vnode(ip, &vp) == 0) {
if ((bp = findblk(ip->vp, file_offset, FINDBLK_TEST)) != NULL &&
bp->b_bio2.bio_offset != NOOFFSET) {
bp = getblk(ip->vp, file_offset, blksize, 0, 0);
bp->b_bio2.bio_offset = NOOFFSET;
brelse(bp);
}
vput(vp);
}
hammer_rel_inode(ip, 0);
return(0);
}
static __inline void
hammer_io_flush_mark(hammer_volume_t volume)
{
atomic_set_int(&volume->vol_flags, HAMMER_VOLF_NEEDFLUSH);
}
void
hammer_io_flush_sync(hammer_mount_t hmp)
{
hammer_volume_t volume;
struct buf *bp_base = NULL;
struct buf *bp;
RB_FOREACH(volume, hammer_vol_rb_tree, &hmp->rb_vols_root) {
if (volume->vol_flags & HAMMER_VOLF_NEEDFLUSH) {
atomic_clear_int(&volume->vol_flags,
HAMMER_VOLF_NEEDFLUSH);
bp = getpbuf(NULL);
bp->b_bio1.bio_offset = 0;
bp->b_bufsize = 0;
bp->b_bcount = 0;
bp->b_cmd = BUF_CMD_FLUSH;
bp->b_bio1.bio_caller_info1.cluster_head = bp_base;
bp->b_bio1.bio_done = biodone_sync;
bp->b_bio1.bio_flags |= BIO_SYNC;
bp_base = bp;
vn_strategy(volume->devvp, &bp->b_bio1);
}
}
while ((bp = bp_base) != NULL) {
bp_base = bp->b_bio1.bio_caller_info1.cluster_head;
biowait(&bp->b_bio1, "hmrFLS");
relpbuf(bp, NULL);
}
}
void
hammer_io_limit_backlog(hammer_mount_t hmp)
{
waitrunningbufspace();
}