#include <sys/bio.h>
#include <sys/malloc.h>
#include <sys/uuid.h>
#include <dev/disk/dm/dm.h>
MALLOC_DEFINE(M_DMDMIRROR, "dm_dmirror", "Device Mapper Target DMIRROR");
#define MEDIA_UNSTABLE 0x0001
#define MEDIA_READ_DEGRADED 0x0002
#define MEDIA_WRITE_DEGRADED 0x0004
#define MEDIA_MASTER 0x0008
#define UNINITIALIZED 0x0010
#define OLD_UNSTABLE 0x0020
#define OLD_MSATER 0x0040
#define DISK_ONLINE 0x0001
#define dmirror_set_bio_disk(bio, x) ((bio)->bio_caller_info1.ptr = (x))
#define dmirror_get_bio_disk(bio) ((bio)?((bio)->bio_caller_info1.ptr):NULL)
#define dmirror_set_bio_seg(bio, x) ((bio)->bio_caller_info2.offset = (x))
#define dmirror_get_bio_segno(bio) ((bio)?((bio)->bio_caller_info2.offset):0)
#define dmirror_set_bio_retries(bio, x) ((bio)->bio_caller_info3.value = (x))
#define dmirror_get_bio_retries(bio) ((bio)?((bio)->bio_caller_info3.value):0)
#define dmirror_set_bio_mbuf(bio, x) ((bio)->bio_caller_info3.ptr = (x))
#define dmirror_get_bio_mbuf(bio) ((bio)?((bio)->bio_caller_info3.ptr):NULL)
typedef struct segdesc {
uint32_t flags;
uint32_t zf_bitmap;
uint8_t disk_no;
uint8_t spare1;
uint16_t spare2;
uint32_t spare3;
} segdesc_t;
typedef struct dmirror_disk {
uint32_t flags;
dm_pdev_t *pdev;
} dmirror_disk_t;
typedef struct target_dmirror_config {
size_t params_len;
dmirror_disk_t disks[4];
uint8_t ndisks;
} dm_target_dmirror_config_t;
static
struct bio*
dmirror_clone_bio(struct bio *obio)
{
struct bio *bio;
struct buf *mbp;
struct buf *bp;
mbp = obio->bio_buf;
bp = getpbuf(NULL);
BUF_KERNPROC(bp);
bp->b_vp = mbp->b_vp;
bp->b_cmd = mbp->b_cmd;
bp->b_data = (char *)mbp->b_data;
bp->b_resid = bp->b_bcount = mbp->b_bcount;
bp->b_bufsize = bp->b_bcount;
bio = &bp->b_bio1;
bio->bio_offset = obio->bio_offset;
return (bio);
}
static void
dmirror_write_done(struct bio *bio)
{
dmirror_disk_t disk;
off_t segno;
struct bio *obio, *mbio;
int retries;
disk = dmirror_get_bio_disk(bio);
segno = dmirror_get_bio_segno(bio);
mbio = dmirror_get_bio_mbuf(bio);
if (bio->bio_buf->b_flags & B_ERROR) {
}
obio = pop_bio(bio);
biodone(obio);
}
void
dmirror_issue_write(dmirror_disk_t disk, struct bio *bio)
{
dmirror_set_bio_disk(bio, disk);
dmirror_set_bio_segno(bio, SEGNO_FROM_OFFSET(bio->bio_offset));
bio->bio_done = dmirror_write_done;
vn_strategy(disk->pdev, bio);
}
void
dmirror_write(dm_target_crypt_config_t config, struct bio *bio)
{
dmirror_disk_t disk, m_disk;
struct bio *wbio1, *wbio2;
segdesc_t segdesc;
int i, masters = 0;
for(i = 0; i < XXX config->ndisks; i++) {
disk = &config->disks[i];
segdesc = SEGDESC_FROM_OFFSET(disk, bio->bio_offset);
if (segdesc->flags & MEDIA_MASTER) {
if (++masters == 1)
m_disk = disk;
}
}
if (masters == 1) {
dmirror_set_bio_mbuf(bio, NULL);
dmirror_issue_write(m_disk, bio);
} else {
wbio1 = dmirror_clone_bio(bio);
wbio2 = dmirror_clone_bio(bio);
dmirror_set_bio_mbuf(wbio1, bio);
dmirror_set_bio_mbuf(wbio2, bio);
dmirror_issue_write(XXX disk1, wbio1);
dmirror_issue_write(XXX disk2, wbio2);
}
}
static void
segdesc_set_flag(dmirror_disk_t disk, off_t segno, int flag)
{
foo |= flag;
}
static void
segdesc_clear_flag(dmirror_disk_t disk, off_t segno, int flag)
{
foo &= ~flag;
}
static void
dmirror_read_done(struct bio *bio)
{
dmirror_disk_t disk;
off_t segno;
struct bio *obio;
int retries;
disk = dmirror_get_bio_disk(bio);
segno = dmirror_get_bio_segno(bio);
retries = dmirror_get_bio_retries(bio);
if (bio->bio_buf->b_flags & B_ERROR) {
segdesc_set_flag(disk, segno, MEDIA_READ_DEGRADED);
if (retries < disk->config->max_retries) {
dmirror_set_bio_retries(bio, retries + 1);
bio->bio_buf->b_flags &= ~(B_ERROR | B_INVAL);
dmirror_issue_read(next_disk, bio);
return;
}
}
obio = pop_bio(bio);
biodone(obio);
}
void
dmirror_issue_read(dmirror_disk_t disk, struct bio *bio)
{
dmirror_set_bio_disk(bio, disk);
dmirror_set_bio_segno(bio, SEGNO_FROM_OFFSET(bio->bio_offset));
bio->bio_done = dmirror_read_done;
vn_strategy(disk->pdev, bio);
}
void
dmirror_read(dm_target_crypt_config_t config, struct bio *bio)
{
dmirror_disk_t disk, m_disk;
segdesc_t segdesc;
int i, masters = 0;
for(i = 0; i < XXX config->ndisks; i++) {
disk = &config->disks[i];
segdesc = SEGDESC_FROM_OFFSET(disk, bio->bio_offset);
if (segdesc->flags & MEDIA_MASTER) {
if (++masters == 1)
m_disk = disk;
}
}
if (masters > 1) {
biodone(foo);
return;
}
if (masters == 1) {
segdesc = SEGDESC_FROM_OFFSET(m_disk, bio->bio_offset);
if (segdesc->flags & UNINITIALIZED) {
}
dmirror_issue_read(m_disk, bio);
} else {
m_disk = NULL;
for (i = 0; i < config->ndisks; i++) {
disk = &config->disks[i];
segdesc = SEGDESC_FROM_OFFSET(disk, bio->bio_offset);
if (!(segdesc->flags & MEDIA_READ_DEGRADED)) {
m_disk = disk;
break;
}
}
if (m_disk) {
dmirror_set_bio_retries(bio, 0);
dmirror_issue_read(m_disk, bio);
} else {
m_disk = &config->disks[i];
dmirror_set_bio_retries(bio, 0);
dmirror_issue_read(m_disk, bio);
}
}
}
int
dm_target_dmirror_strategy(dm_table_entry_t *table_en, struct buf *bp)
{
struct bio *bio, *split_bio1, *split_bio2;
struct buf *bp;
off_t bseg, eseg, seg_end;
size_t fsb;
int split_transaction = 0;
dm_target_crypt_config_t *priv;
priv = table_en->target_config;
if ((bp->b_cmd == BUF_CMD_READ) || (bp->b_cmd == BUF_CMD_WRITE)) {
if (((bp->b_bcount % DEV_BSIZE) != 0) || (bp->b_bcount == 0)) {
kprintf("dm_target_dmirror_strategy: can't really handle bp->b_bcount = %d\n", bp->b_bcount);
bp->b_error = EINVAL;
bp->b_flags |= B_ERROR | B_INVAL;
biodone(&bp->b_bio1);
return 0;
}
bseg = SEGNO_FROM_OFFSET(bp->b_bio1.bio_offset);
eseg = SEGNO_FROM_OFFSET(bp->b_bio1.bio_offset + bp->b_resid);
seg_end = OFFSET_FROM_SEGNO(eseg);
if (bseg != eseg) {
split_transaction = 1;
fsb = seg_end - bp->b_bio1.bio_offset;
nestbuf = getpbuf(NULL);
nestiobuf_setup(&bp->b_bio1, nestbuf, 0, fsb);
split_bio1 = push_bio(&nestbuf->b_bio1);
split_bio1->bio_offset = bp->b_bio1.bio_offset +
priv->block_offset*DEV_BSIZE;
nestbuf = getpbuf(NULL);
nestiobuf_setup(&bp->b_bio1, nestbuf, fsb, bp->b_resid - fsb);
split_bio2 = push_bio(&nestbuf->b_bio1);
split_bio2->bio_offset = bp->b_bio1.bio_offset + fsb +
priv->block_offset*DEV_BSIZE;
}
}
switch (bp->b_cmd) {
case BUF_CMD_READ:
if (split_transaction) {
dmirror_read(priv, split_bio1);
dmirror_read(priv, split_bio2);
} else {
bio = push_bio(&bp->b_bio1);
bio->bio_offset = bp->b_bio1.bio_offset + priv->block_offset*DEV_BSIZE;
dmirror_read(priv, bio);
}
break;
case BUF_CMD_WRITE:
if (split_transaction) {
dmirror_write(priv, split_bio1);
dmirror_write(priv, split_bio2);
} else {
bio = push_bio(&bp->b_bio1);
bio->bio_offset = bp->b_bio1.bio_offset + priv->block_offset*DEV_BSIZE;
dmirror_write(priv, bio);
}
break;
default:
vn_strategy(priv->pdev[0]->pdev_vnode, &bp->b_bio1);
vn_strategy(priv->pdev[1]->pdev_vnode, &bp->b_bio1);
}
return 0;
}