#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/devicestat.h>
#include <sys/disk.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/udev.h>
#include <sys/uuid.h>
#include <sys/kern_syscall.h>
#include <sys/dmsg.h>
#include <sys/xdiskioctl.h>
#include <sys/buf2.h>
struct xa_softc;
struct xa_softc_tree;
RB_HEAD(xa_softc_tree, xa_softc);
RB_PROTOTYPE(xa_softc_tree, xa_softc, rbnode, xa_softc_cmp);
static int xa_active;
SYSCTL_INT(_debug, OID_AUTO, xa_active, CTLFLAG_RW, &xa_active, 0,
"Number of active xdisk IOs");
static uint64_t xa_last;
SYSCTL_ULONG(_debug, OID_AUTO, xa_last, CTLFLAG_RW, &xa_last, 0,
"Offset of last xdisk IO");
static int xa_debug = 1;
SYSCTL_INT(_debug, OID_AUTO, xa_debug, CTLFLAG_RW, &xa_debug, 0,
"xdisk debugging");
struct xa_tag {
TAILQ_ENTRY(xa_tag) entry;
struct xa_softc *sc;
dmsg_blk_error_t status;
kdmsg_state_t *state;
struct bio *bio;
int waiting;
int async;
int done;
};
typedef struct xa_tag xa_tag_t;
struct xa_softc {
struct kdmsg_state_list spanq;
RB_ENTRY(xa_softc) rbnode;
cdev_t dev;
struct devstat stats;
struct disk_info info;
struct disk disk;
uuid_t peer_id;
int unit;
int opencnt;
int spancnt;
uint64_t keyid;
int serializing;
int last_error;
int terminating;
char peer_label[64];
char pfs_label[64];
xa_tag_t *open_tag;
TAILQ_HEAD(, bio) bioq;
TAILQ_HEAD(, xa_tag) tag_freeq;
TAILQ_HEAD(, xa_tag) tag_pendq;
struct lock lk;
};
typedef struct xa_softc xa_softc_t;
struct xa_iocom {
TAILQ_ENTRY(xa_iocom) entry;
kdmsg_iocom_t iocom;
xa_softc_t dummysc;
};
typedef struct xa_iocom xa_iocom_t;
static int xa_softc_cmp(xa_softc_t *sc1, xa_softc_t *sc2);
RB_GENERATE(xa_softc_tree, xa_softc, rbnode, xa_softc_cmp);
static struct xa_softc_tree xa_device_tree;
#define MAXTAGS 64
static int xdisk_attach(struct xdisk_attach_ioctl *xaioc);
static int xdisk_detach(struct xdisk_attach_ioctl *xaioc);
static void xaio_exit(kdmsg_iocom_t *iocom);
static int xaio_rcvdmsg(kdmsg_msg_t *msg);
static void xa_terminate_check(struct xa_softc *sc);
static xa_tag_t *xa_setup_cmd(xa_softc_t *sc, struct bio *bio);
static void xa_start(xa_tag_t *tag, kdmsg_msg_t *msg, int async);
static void xa_done(xa_tag_t *tag, int wasbio);
static void xa_release(xa_tag_t *tag, int wasbio);
static uint32_t xa_wait(xa_tag_t *tag);
static int xa_sync_completion(kdmsg_state_t *state, kdmsg_msg_t *msg);
static int xa_bio_completion(kdmsg_state_t *state, kdmsg_msg_t *msg);
static void xa_restart_deferred(xa_softc_t *sc);
#define xa_printf(level, ctl, ...) \
if (xa_debug >= (level)) kprintf("xdisk: " ctl, __VA_ARGS__)
MALLOC_DEFINE(M_XDISK, "Networked disk client", "Network Disks");
static d_open_t xdisk_open;
static d_close_t xdisk_close;
static d_ioctl_t xdisk_ioctl;
static struct dev_ops xdisk_ops = {
{ "xdisk", 0, D_MPSAFE | D_TRACKCLOSE },
.d_open = xdisk_open,
.d_close = xdisk_close,
.d_ioctl = xdisk_ioctl
};
static d_open_t xa_open;
static d_close_t xa_close;
static d_ioctl_t xa_ioctl;
static d_strategy_t xa_strategy;
static d_psize_t xa_size;
static struct dev_ops xa_ops = {
{ "xa", 0, D_DISK | D_CANFREE | D_MPSAFE | D_TRACKCLOSE },
.d_open = xa_open,
.d_close = xa_close,
.d_ioctl = xa_ioctl,
.d_read = physread,
.d_write = physwrite,
.d_strategy = xa_strategy,
.d_psize = xa_size
};
static int xdisk_opencount;
static cdev_t xdisk_dev;
struct lock xdisk_lk;
static TAILQ_HEAD(, xa_iocom) xaiocomq;
static int
xdisk_modevent(module_t mod, int type, void *data)
{
switch (type) {
case MOD_LOAD:
TAILQ_INIT(&xaiocomq);
RB_INIT(&xa_device_tree);
lockinit(&xdisk_lk, "xdisk", 0, 0);
xdisk_dev = make_dev(&xdisk_ops, 0,
UID_ROOT, GID_WHEEL, 0600, "xdisk");
break;
case MOD_UNLOAD:
case MOD_SHUTDOWN:
if (!RB_EMPTY(&xa_device_tree))
return (EBUSY);
if (xdisk_opencount || TAILQ_FIRST(&xaiocomq))
return (EBUSY);
if (xdisk_dev) {
destroy_dev(xdisk_dev);
xdisk_dev = NULL;
}
dev_ops_remove_all(&xdisk_ops);
dev_ops_remove_all(&xa_ops);
break;
default:
break;
}
return 0;
}
DEV_MODULE(xdisk, xdisk_modevent, 0);
static int
xa_softc_cmp(xa_softc_t *sc1, xa_softc_t *sc2)
{
return(strcmp(sc1->pfs_label, sc2->pfs_label));
}
static int
xdisk_open(struct dev_open_args *ap)
{
lockmgr(&xdisk_lk, LK_EXCLUSIVE);
++xdisk_opencount;
lockmgr(&xdisk_lk, LK_RELEASE);
return(0);
}
static int
xdisk_close(struct dev_close_args *ap)
{
lockmgr(&xdisk_lk, LK_EXCLUSIVE);
--xdisk_opencount;
lockmgr(&xdisk_lk, LK_RELEASE);
return(0);
}
static int
xdisk_ioctl(struct dev_ioctl_args *ap)
{
int error;
switch(ap->a_cmd) {
case XDISKIOCATTACH:
error = xdisk_attach((void *)ap->a_data);
break;
case XDISKIOCDETACH:
error = xdisk_detach((void *)ap->a_data);
break;
default:
error = ENOTTY;
break;
}
return error;
}
static int
xdisk_attach(struct xdisk_attach_ioctl *xaioc)
{
xa_iocom_t *xaio;
struct file *fp;
fp = holdfp(curthread, xaioc->fd, -1);
if (fp == NULL)
return EINVAL;
xa_printf(1, "xdisk_attach fp=%p\n", fp);
lockmgr(&xdisk_lk, LK_EXCLUSIVE);
xaio = kmalloc(sizeof(*xaio), M_XDISK, M_WAITOK | M_ZERO);
kdmsg_iocom_init(&xaio->iocom, xaio,
KDMSG_IOCOMF_AUTOCONN,
M_XDISK, xaio_rcvdmsg);
xaio->iocom.exit_func = xaio_exit;
kdmsg_iocom_reconnect(&xaio->iocom, fp, "xdisk");
xaio->iocom.auto_lnk_conn.peer_type = DMSG_PEER_CLIENT;
xaio->iocom.auto_lnk_conn.proto_version = DMSG_SPAN_PROTO_1;
xaio->iocom.auto_lnk_conn.peer_mask = 1LLU << DMSG_PEER_BLOCK;
ksnprintf(xaio->iocom.auto_lnk_conn.peer_label,
sizeof(xaio->iocom.auto_lnk_conn.peer_label),
"%s/xdisk",
hostname);
TAILQ_INSERT_TAIL(&xaiocomq, xaio, entry);
kdmsg_iocom_autoinitiate(&xaio->iocom, NULL);
lockmgr(&xdisk_lk, LK_RELEASE);
return 0;
}
static int
xdisk_detach(struct xdisk_attach_ioctl *xaioc)
{
return EINVAL;
}
static
void
xaio_exit(kdmsg_iocom_t *iocom)
{
xa_iocom_t *xaio = iocom->handle;
lockmgr(&xdisk_lk, LK_EXCLUSIVE);
xa_printf(1, "%s", "xdisk_detach [xaio_exit()]\n");
TAILQ_REMOVE(&xaiocomq, xaio, entry);
lockmgr(&xdisk_lk, LK_RELEASE);
kdmsg_iocom_uninit(&xaio->iocom);
kfree(xaio, M_XDISK);
}
static int
xaio_rcvdmsg(kdmsg_msg_t *msg)
{
kdmsg_state_t *state = msg->state;
xa_iocom_t *xaio = state->iocom->handle;
xa_softc_t *sc;
if (state) {
xa_printf(4,
"xdisk - rcvmsg state=%p rx=%08x tx=%08x msgcmd=%08x\n",
state, state->rxcmd, state->txcmd,
msg->any.head.cmd);
}
lockmgr(&xdisk_lk, LK_EXCLUSIVE);
switch(msg->tcmd) {
case DMSG_LNK_SPAN | DMSGF_CREATE | DMSGF_DELETE:
kdmsg_msg_reply(msg, 0);
break;
case DMSG_LNK_SPAN | DMSGF_CREATE:
bcopy(msg->any.lnk_span.peer_label, xaio->dummysc.peer_label,
sizeof(xaio->dummysc.peer_label));
xaio->dummysc.peer_label[
sizeof(xaio->dummysc.peer_label) - 1] = 0;
bcopy(msg->any.lnk_span.pfs_label, xaio->dummysc.pfs_label,
sizeof(xaio->dummysc.pfs_label));
xaio->dummysc.pfs_label[
sizeof(xaio->dummysc.pfs_label) - 1] = 0;
xa_printf(3, "LINK_SPAN state %p create for %s\n",
msg->state, msg->any.lnk_span.pfs_label);
sc = RB_FIND(xa_softc_tree, &xa_device_tree, &xaio->dummysc);
if (sc == NULL) {
xa_softc_t *sctmp;
xa_tag_t *tag;
cdev_t dev;
int unit;
int n;
sc = kmalloc(sizeof(*sc), M_XDISK, M_WAITOK | M_ZERO);
bcopy(msg->any.lnk_span.peer_label, sc->peer_label,
sizeof(sc->peer_label));
sc->peer_label[sizeof(sc->peer_label) - 1] = 0;
bcopy(msg->any.lnk_span.pfs_label, sc->pfs_label,
sizeof(sc->pfs_label));
sc->pfs_label[sizeof(sc->pfs_label) - 1] = 0;
unit = -1;
do {
++unit;
RB_FOREACH(sctmp, xa_softc_tree,
&xa_device_tree) {
if (sctmp->unit == unit)
break;
}
} while (sctmp);
sc->unit = unit;
sc->serializing = 1;
sc->spancnt = 1;
lockinit(&sc->lk, "xalk", 0, 0);
TAILQ_INIT(&sc->spanq);
TAILQ_INIT(&sc->bioq);
TAILQ_INIT(&sc->tag_freeq);
TAILQ_INIT(&sc->tag_pendq);
lockmgr(&sc->lk, LK_EXCLUSIVE);
RB_INSERT(xa_softc_tree, &xa_device_tree, sc);
TAILQ_INSERT_TAIL(&sc->spanq, msg->state, user_entry);
msg->state->any.xa_sc = sc;
for (n = 0; n < MAXTAGS; ++n) {
tag = kmalloc(sizeof(*tag),
M_XDISK, M_WAITOK|M_ZERO);
tag->sc = sc;
TAILQ_INSERT_TAIL(&sc->tag_freeq, tag, entry);
}
if (sc->dev == NULL) {
dev = disk_create(unit, &sc->disk, &xa_ops);
dev->si_drv1 = sc;
sc->dev = dev;
devstat_add_entry(&sc->stats, "xa", unit,
DEV_BSIZE,
DEVSTAT_NO_ORDERED_TAGS,
DEVSTAT_TYPE_DIRECT |
DEVSTAT_TYPE_IF_OTHER,
DEVSTAT_PRIORITY_OTHER);
}
sc->info.d_media_blksize =
msg->any.lnk_span.media.block.blksize;
if (sc->info.d_media_blksize <= 0)
sc->info.d_media_blksize = 1;
sc->info.d_media_blocks =
msg->any.lnk_span.media.block.bytes /
sc->info.d_media_blksize;
sc->info.d_dsflags = DSO_MBRQUIET | DSO_RAWPSIZE;
sc->info.d_secpertrack = 32;
sc->info.d_nheads = 64;
sc->info.d_secpercyl = sc->info.d_secpertrack *
sc->info.d_nheads;
sc->info.d_ncylinders = 0;
if (sc->pfs_label[0])
sc->info.d_serialno = sc->pfs_label;
disk_setdiskinfo(&sc->disk, &sc->info);
xa_restart_deferred(sc);
lockmgr(&sc->lk, LK_RELEASE);
} else {
lockmgr(&sc->lk, LK_EXCLUSIVE);
++sc->spancnt;
TAILQ_INSERT_TAIL(&sc->spanq, msg->state, user_entry);
msg->state->any.xa_sc = sc;
if (sc->serializing == 0 && sc->open_tag == NULL) {
sc->serializing = 1;
xa_restart_deferred(sc);
}
lockmgr(&sc->lk, LK_RELEASE);
if (sc->dev && sc->dev->si_disk) {
xa_printf(1, "reprobe disk: %s\n",
sc->pfs_label);
disk_msg_send(DISK_DISK_REPROBE,
sc->dev->si_disk,
NULL);
}
}
xa_printf(2, "sc %p spancnt %d\n", sc, sc->spancnt);
kdmsg_msg_result(msg, 0);
break;
case DMSG_LNK_SPAN | DMSGF_DELETE:
sc = msg->state->any.xa_sc;
xa_printf(3, "LINK_SPAN state %p delete for %s (sc=%p)\n",
msg->state, (sc ? sc->pfs_label : "(null)"), sc);
lockmgr(&sc->lk, LK_EXCLUSIVE);
msg->state->any.xa_sc = NULL;
TAILQ_REMOVE(&sc->spanq, msg->state, user_entry);
--sc->spancnt;
xa_printf(2, "sc %p spancnt %d\n", sc, sc->spancnt);
if (sc->spancnt &&
sc->serializing == 0 && sc->open_tag == NULL) {
sc->serializing = 1;
xa_restart_deferred(sc);
}
lockmgr(&sc->lk, LK_RELEASE);
kdmsg_msg_reply(msg, 0);
#if 0
if (sc->spancnt == 0)
xa_terminate_check(sc);
#endif
break;
case DMSG_LNK_SPAN | DMSGF_DELETE | DMSGF_REPLY:
xa_printf(3, "LINK_SPAN state %p delete+reply\n",
msg->state);
break;
case DMSG_LNK_SPAN | DMSGF_REPLY:
xa_printf(3, "LINK_SPAN state %p reply\n",
msg->state);
break;
case DMSG_DBG_SHELL:
kdmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
break;
case DMSG_DBG_SHELL | DMSGF_REPLY:
if (msg->aux_data) {
msg->aux_data[msg->aux_size - 1] = 0;
xa_printf(0, "DEBUGMSG: %s\n", msg->aux_data);
}
break;
default:
if (msg->any.head.cmd & DMSGF_CREATE)
kdmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
break;
}
lockmgr(&xdisk_lk, LK_RELEASE);
return 0;
}
static
void
xa_terminate_check(struct xa_softc *sc)
{
xa_tag_t *tag;
xa_printf(1, "Terminate check xa%d (%d,%d,%d) sc=%p ",
sc->unit,
sc->opencnt, sc->serializing, sc->spancnt,
sc);
if (sc->opencnt || sc->serializing || sc->spancnt ||
TAILQ_FIRST(&sc->bioq) || TAILQ_FIRST(&sc->tag_pendq)) {
xa_printf(1, "%s", "(leave intact)\n");
return;
}
RB_REMOVE(xa_softc_tree, &xa_device_tree, sc);
sc->terminating = 1;
if (sc->dev) {
disk_destroy(&sc->disk);
devstat_remove_entry(&sc->stats);
sc->dev->si_drv1 = NULL;
sc->dev = NULL;
}
xa_printf(1, "%s", "(remove from tree)\n");
sc->serializing = 1;
KKASSERT(sc->opencnt == 0);
KKASSERT(TAILQ_EMPTY(&sc->tag_pendq));
while ((tag = TAILQ_FIRST(&sc->tag_freeq)) != NULL) {
TAILQ_REMOVE(&sc->tag_freeq, tag, entry);
tag->sc = NULL;
kfree(tag, M_XDISK);
}
kfree(sc, M_XDISK);
}
static int
xa_open(struct dev_open_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
xa_softc_t *sc;
int error;
dev->si_bsize_phys = 512;
dev->si_bsize_best = 32768;
lockmgr(&xdisk_lk, LK_EXCLUSIVE);
again:
sc = dev->si_drv1;
if (sc == NULL) {
lockmgr(&xdisk_lk, LK_RELEASE);
return ENXIO;
}
if (sc->serializing) {
tsleep(sc, 0, "xarace", hz / 10);
goto again;
}
if (sc->terminating) {
lockmgr(&xdisk_lk, LK_RELEASE);
return ENXIO;
}
sc->serializing = 1;
if (sc->opencnt++ > 0) {
sc->serializing = 0;
wakeup(sc);
lockmgr(&xdisk_lk, LK_RELEASE);
return(0);
}
if (sc->open_tag == NULL) {
lockmgr(&sc->lk, LK_EXCLUSIVE);
xa_restart_deferred(sc);
lockmgr(&sc->lk, LK_RELEASE);
} else {
sc->serializing = 0;
wakeup(sc);
}
lockmgr(&xdisk_lk, LK_RELEASE);
lockmgr(&xdisk_lk, LK_EXCLUSIVE);
while (sc->serializing)
lksleep(sc, &xdisk_lk, 0, "xaopen", hz);
error = sc->last_error;
if (error) {
KKASSERT(sc->opencnt > 0);
--sc->opencnt;
xa_terminate_check(sc);
sc = NULL;
}
lockmgr(&xdisk_lk, LK_RELEASE);
return (error);
}
static int
xa_close(struct dev_close_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
xa_softc_t *sc;
xa_tag_t *tag;
lockmgr(&xdisk_lk, LK_EXCLUSIVE);
sc = dev->si_drv1;
if (sc == NULL)
return ENXIO;
if (sc->terminating) {
lockmgr(&sc->lk, LK_RELEASE);
return ENXIO;
}
lockmgr(&sc->lk, LK_EXCLUSIVE);
if (sc->opencnt == 1 && sc->open_tag) {
tag = sc->open_tag;
sc->open_tag = NULL;
lockmgr(&sc->lk, LK_RELEASE);
kdmsg_state_reply(tag->state, 0);
xa_wait(tag);
} else {
lockmgr(&sc->lk, LK_RELEASE);
}
KKASSERT(sc->opencnt > 0);
--sc->opencnt;
xa_terminate_check(sc);
lockmgr(&xdisk_lk, LK_RELEASE);
return(0);
}
static int
xa_strategy(struct dev_strategy_args *ap)
{
xa_softc_t *sc = ap->a_head.a_dev->si_drv1;
xa_tag_t *tag;
struct bio *bio = ap->a_bio;
devstat_start_transaction(&sc->stats);
atomic_add_int(&xa_active, 1);
xa_last = bio->bio_offset;
lockmgr(&sc->lk, LK_EXCLUSIVE);
tag = xa_setup_cmd(sc, bio);
if (tag)
xa_start(tag, NULL, 1);
lockmgr(&sc->lk, LK_RELEASE);
return(0);
}
static int
xa_ioctl(struct dev_ioctl_args *ap)
{
return(ENOTTY);
}
static int
xa_size(struct dev_psize_args *ap)
{
struct xa_softc *sc;
if ((sc = ap->a_head.a_dev->si_drv1) == NULL)
return (ENXIO);
ap->a_result = sc->info.d_media_blocks;
return (0);
}
static xa_tag_t *
xa_setup_cmd(xa_softc_t *sc, struct bio *bio)
{
xa_tag_t *tag;
if ((tag = TAILQ_FIRST(&sc->tag_freeq)) != NULL) {
TAILQ_REMOVE(&sc->tag_freeq, tag, entry);
tag->bio = bio;
TAILQ_INSERT_TAIL(&sc->tag_pendq, tag, entry);
}
if (tag == NULL && bio) {
TAILQ_INSERT_TAIL(&sc->bioq, bio, bio_act);
}
return (tag);
}
static void
xa_start(xa_tag_t *tag, kdmsg_msg_t *msg, int async)
{
xa_softc_t *sc = tag->sc;
tag->done = 0;
tag->async = async;
tag->status.head.error = DMSG_ERR_IO;
if (msg == NULL) {
struct bio *bio;
struct buf *bp;
kdmsg_state_t *trans;
if (sc->opencnt == 0 || sc->open_tag == NULL) {
TAILQ_FOREACH(trans, &sc->spanq, user_entry) {
if ((trans->rxcmd & DMSGF_DELETE) == 0)
break;
}
} else {
trans = sc->open_tag->state;
}
if (trans == NULL)
goto skip;
KKASSERT(tag->bio);
bio = tag->bio;
bp = bio->bio_buf;
switch(bp->b_cmd) {
case BUF_CMD_READ:
msg = kdmsg_msg_alloc(trans,
DMSG_BLK_READ |
DMSGF_CREATE |
DMSGF_DELETE,
xa_bio_completion, tag);
msg->any.blk_read.keyid = sc->keyid;
msg->any.blk_read.offset = bio->bio_offset;
msg->any.blk_read.bytes = bp->b_bcount;
break;
case BUF_CMD_WRITE:
msg = kdmsg_msg_alloc(trans,
DMSG_BLK_WRITE |
DMSGF_CREATE | DMSGF_DELETE,
xa_bio_completion, tag);
msg->any.blk_write.keyid = sc->keyid;
msg->any.blk_write.offset = bio->bio_offset;
msg->any.blk_write.bytes = bp->b_bcount;
msg->aux_data = bp->b_data;
msg->aux_size = bp->b_bcount;
break;
case BUF_CMD_FLUSH:
msg = kdmsg_msg_alloc(trans,
DMSG_BLK_FLUSH |
DMSGF_CREATE | DMSGF_DELETE,
xa_bio_completion, tag);
msg->any.blk_flush.keyid = sc->keyid;
msg->any.blk_flush.offset = bio->bio_offset;
msg->any.blk_flush.bytes = bp->b_bcount;
break;
case BUF_CMD_FREEBLKS:
msg = kdmsg_msg_alloc(trans,
DMSG_BLK_FREEBLKS |
DMSGF_CREATE | DMSGF_DELETE,
xa_bio_completion, tag);
msg->any.blk_freeblks.keyid = sc->keyid;
msg->any.blk_freeblks.offset = bio->bio_offset;
msg->any.blk_freeblks.bytes = bp->b_bcount;
break;
default:
bp->b_flags |= B_ERROR;
bp->b_error = EIO;
devstat_end_transaction_buf(&sc->stats, bp);
atomic_add_int(&xa_active, -1);
biodone(bio);
tag->bio = NULL;
break;
}
}
skip:
if (msg) {
tag->state = msg->state;
lockmgr(&sc->lk, LK_RELEASE);
kdmsg_msg_write(msg);
lockmgr(&sc->lk, LK_EXCLUSIVE);
} else if (tag->bio &&
(tag->bio->bio_buf->b_flags & B_FAILONDIS) == 0) {
TAILQ_INSERT_TAIL(&sc->bioq, tag->bio, bio_act);
tag->bio = NULL;
lockmgr(&sc->lk, LK_RELEASE);
xa_done(tag, 1);
lockmgr(&sc->lk, LK_EXCLUSIVE);
} else {
lockmgr(&sc->lk, LK_RELEASE);
tag->status.head.error = DMSG_ERR_IO;
xa_done(tag, 1);
lockmgr(&sc->lk, LK_EXCLUSIVE);
}
}
static uint32_t
xa_wait(xa_tag_t *tag)
{
xa_softc_t *sc = tag->sc;
uint32_t error;
lockmgr(&sc->lk, LK_EXCLUSIVE);
tag->waiting = 1;
while (tag->done == 0)
lksleep(tag, &sc->lk, 0, "xawait", 0);
lockmgr(&sc->lk, LK_RELEASE);
error = tag->status.head.error;
tag->waiting = 0;
xa_release(tag, 0);
return error;
}
static void
xa_done(xa_tag_t *tag, int wasbio)
{
KKASSERT(tag->bio == NULL);
tag->state = NULL;
tag->done = 1;
if (tag->waiting)
wakeup(tag);
if (tag->async)
xa_release(tag, wasbio);
}
static
void
xa_release(xa_tag_t *tag, int wasbio)
{
xa_softc_t *sc = tag->sc;
struct bio *bio;
if ((bio = tag->bio) != NULL) {
struct buf *bp = bio->bio_buf;
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
devstat_end_transaction_buf(&sc->stats, bp);
atomic_add_int(&xa_active, -1);
biodone(bio);
tag->bio = NULL;
}
lockmgr(&sc->lk, LK_EXCLUSIVE);
if (wasbio && sc->open_tag &&
(bio = TAILQ_FIRST(&sc->bioq)) != NULL) {
TAILQ_REMOVE(&sc->bioq, bio, bio_act);
tag->bio = bio;
xa_start(tag, NULL, 1);
} else {
TAILQ_REMOVE(&sc->tag_pendq, tag, entry);
TAILQ_INSERT_TAIL(&sc->tag_freeq, tag, entry);
}
lockmgr(&sc->lk, LK_RELEASE);
}
static int
xa_sync_completion(kdmsg_state_t *state, kdmsg_msg_t *msg)
{
xa_tag_t *tag = state->any.any;
xa_softc_t *sc;
struct bio *bio;
xa_printf(1, "xa_sync_completion: tag %p msg %08x state %p\n",
tag, msg->any.head.cmd, msg->state);
if (tag == NULL) {
if (msg->any.head.cmd & DMSGF_CREATE)
kdmsg_state_reply(state, DMSG_ERR_LOSTLINK);
return 0;
}
sc = tag->sc;
lockmgr(&sc->lk, LK_EXCLUSIVE);
if (msg->any.head.cmd & DMSGF_CREATE) {
switch(msg->any.head.cmd & DMSGF_CMDSWMASK) {
case DMSG_LNK_ERROR | DMSGF_REPLY:
bzero(&tag->status, sizeof(tag->status));
tag->status.head = msg->any.head;
break;
case DMSG_BLK_ERROR | DMSGF_REPLY:
tag->status = msg->any.blk_error;
break;
}
sc->last_error = tag->status.head.error;
xa_printf(1, "blk_open completion status %d\n",
sc->last_error);
if (sc->last_error == 0) {
while ((bio = TAILQ_FIRST(&sc->bioq)) != NULL) {
tag = xa_setup_cmd(sc, NULL);
if (tag == NULL)
break;
TAILQ_REMOVE(&sc->bioq, bio, bio_act);
tag->bio = bio;
xa_start(tag, NULL, 1);
}
}
sc->serializing = 0;
wakeup(sc);
}
if (msg->any.head.cmd & DMSGF_DELETE) {
kdmsg_state_reply(tag->state, 0);
if (sc->open_tag == tag) {
sc->open_tag = NULL;
xa_done(tag, 0);
} else {
tag->async = 0;
xa_done(tag, 0);
}
}
lockmgr(&sc->lk, LK_RELEASE);
return (0);
}
static int
xa_bio_completion(kdmsg_state_t *state, kdmsg_msg_t *msg)
{
xa_tag_t *tag = state->any.any;
xa_softc_t *sc = tag->sc;
struct bio *bio;
struct buf *bp;
if ((bio = tag->bio) == NULL)
goto handle_done;
bp = bio->bio_buf;
switch(msg->any.head.cmd & DMSGF_CMDSWMASK) {
case DMSG_LNK_ERROR | DMSGF_REPLY:
bzero(&tag->status, sizeof(tag->status));
tag->status.head = msg->any.head;
if (tag->status.head.error)
tag->status.resid = bp->b_bcount;
else
tag->status.resid = 0;
break;
case DMSG_BLK_ERROR | DMSGF_REPLY:
tag->status = msg->any.blk_error;
break;
}
if (tag->status.head.error &&
(msg->any.head.cmd & DMSGF_DELETE) && sc->opencnt) {
if (tag->status.head.error != DMSG_ERR_IO)
goto handle_repend;
}
switch(bp->b_cmd) {
case BUF_CMD_READ:
if (msg->aux_data && msg->aux_size) {
if (msg->aux_size < bp->b_bcount) {
bcopy(msg->aux_data, bp->b_data, msg->aux_size);
bzero(bp->b_data + msg->aux_size,
bp->b_bcount - msg->aux_size);
} else {
bcopy(msg->aux_data, bp->b_data, bp->b_bcount);
}
} else {
bzero(bp->b_data, bp->b_bcount);
}
case BUF_CMD_WRITE:
case BUF_CMD_FLUSH:
case BUF_CMD_FREEBLKS:
default:
if (tag->status.resid > bp->b_bcount)
tag->status.resid = bp->b_bcount;
bp->b_resid = tag->status.resid;
if (tag->status.head.error != 0) {
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
} else {
bp->b_resid = 0;
}
devstat_end_transaction_buf(&sc->stats, bp);
atomic_add_int(&xa_active, -1);
biodone(bio);
tag->bio = NULL;
break;
}
handle_done:
if (msg->any.head.cmd & DMSGF_DELETE) {
xa_done(tag, 1);
if ((state->txcmd & DMSGF_DELETE) == 0)
kdmsg_msg_reply(msg, 0);
}
return (0);
handle_repend:
tag->bio = NULL;
if (bio->bio_buf->b_flags & B_FAILONDIS) {
xa_printf(1, "xa_strategy: lost link, fail probe bp %p\n",
bio->bio_buf);
bio->bio_buf->b_error = ENXIO;
bio->bio_buf->b_flags |= B_ERROR;
biodone(bio);
bio = NULL;
} else {
xa_printf(1, "xa_strategy: lost link, requeue bp %p\n",
bio->bio_buf);
}
xa_done(tag, 0);
if ((state->txcmd & DMSGF_DELETE) == 0)
kdmsg_msg_reply(msg, 0);
if (bio) {
lockmgr(&sc->lk, LK_EXCLUSIVE);
TAILQ_INSERT_TAIL(&sc->bioq, bio, bio_act);
lockmgr(&sc->lk, LK_RELEASE);
}
return (0);
}
static
void
xa_restart_deferred(xa_softc_t *sc)
{
kdmsg_state_t *span;
kdmsg_msg_t *msg;
xa_tag_t *tag;
int error;
KKASSERT(sc->serializing);
if (sc->opencnt == 0) {
sc->serializing = 0;
wakeup(sc);
} else if (sc->open_tag == NULL) {
error = 0;
TAILQ_FOREACH(span, &sc->spanq, user_entry) {
if ((span->rxcmd & DMSGF_DELETE) == 0)
break;
}
if (span == NULL)
error = ENXIO;
if (error == 0) {
tag = xa_setup_cmd(sc, NULL);
if (tag == NULL)
error = ENXIO;
}
if (error == 0) {
sc->open_tag = tag;
msg = kdmsg_msg_alloc(span,
DMSG_BLK_OPEN |
DMSGF_CREATE,
xa_sync_completion, tag);
msg->any.blk_open.modes = DMSG_BLKOPEN_RD;
xa_printf(1,
"BLK_OPEN tag %p state %p "
"span-state %p\n",
tag, msg->state, span);
xa_start(tag, msg, 0);
}
if (error) {
sc->serializing = 0;
wakeup(sc);
}
} else {
sc->serializing = 0;
wakeup(sc);
}
}