📄 block-vhd.c
字号:
if (test_batmap(s, blk)) {
DBG(TLOG_DBG, "batmap set for 0x%04x\n", blk);
return VHD_BM_BIT_SET;
}
bm = get_bitmap(s, blk);
if (!bm)
return VHD_BM_NOT_CACHED;
/* bump lru count */
touch_bitmap(s, bm);
if (test_vhd_flag(bm->status, VHD_FLAG_BM_READ_PENDING))
return VHD_BM_READ_PENDING;
return ((vhd_bitmap_test(&s->vhd, bm->map, sec)) ?
VHD_BM_BIT_SET : VHD_BM_BIT_CLEAR);
}
static int
read_bitmap_cache_span(struct vhd_state *s,
uint64_t sector, int nr_secs, int value)
{
int ret;
u32 blk, sec;
struct vhd_bitmap *bm;
/* in fixed disks, every block is present */
if (s->vhd.footer.type == HD_TYPE_FIXED)
return nr_secs;
sec = sector % s->spb;
blk = sector / s->spb;
if (test_batmap(s, blk))
return MIN(nr_secs, s->spb - sec);
bm = get_bitmap(s, blk);
ASSERT(bm && bitmap_valid(bm));
for (ret = 0; sec < s->spb && ret < nr_secs; sec++, ret++)
if (vhd_bitmap_test(&s->vhd, bm->map, sec) != value)
break;
return ret;
}
static inline struct vhd_request *
alloc_vhd_request(struct vhd_state *s)
{
struct vhd_request *req = NULL;
if (s->vreq_free_count > 0) {
req = s->vreq_free[--s->vreq_free_count];
ASSERT(req->treq.secs == 0);
init_vhd_request(s, req);
return req;
}
return NULL;
}
static inline void
free_vhd_request(struct vhd_state *s, struct vhd_request *req)
{
memset(req, 0, sizeof(struct vhd_request));
s->vreq_free[s->vreq_free_count++] = req;
}
static inline void
aio_read(struct vhd_state *s, struct vhd_request *req, uint64_t offset)
{
struct tiocb *tiocb = &req->tiocb;
td_prep_read(tiocb, s->vhd.fd, req->treq.buf,
vhd_sectors_to_bytes(req->treq.secs),
offset, vhd_complete, req);
td_queue_tiocb(s->driver, tiocb);
s->queued++;
s->reads++;
s->read_size += req->treq.secs;
TRACE(s);
}
static inline void
aio_write(struct vhd_state *s, struct vhd_request *req, uint64_t offset)
{
struct tiocb *tiocb = &req->tiocb;
td_prep_write(tiocb, s->vhd.fd, req->treq.buf,
vhd_sectors_to_bytes(req->treq.secs),
offset, vhd_complete, req);
td_queue_tiocb(s->driver, tiocb);
s->queued++;
s->writes++;
s->write_size += req->treq.secs;
TRACE(s);
}
static inline uint64_t
reserve_new_block(struct vhd_state *s, uint32_t blk)
{
int gap = 0;
ASSERT(!test_vhd_flag(s->bat.status, VHD_FLAG_BAT_WRITE_STARTED));
/* data region of segment should begin on page boundary */
if ((s->next_db + s->bm_secs) % s->spp)
gap = (s->spp - ((s->next_db + s->bm_secs) % s->spp));
s->bat.pbw_blk = blk;
s->bat.pbw_offset = s->next_db + gap;
return s->next_db;
}
static int
schedule_bat_write(struct vhd_state *s)
{
int i;
u32 blk;
char *buf;
u64 offset;
struct vhd_request *req;
ASSERT(bat_locked(s));
req = &s->bat.req;
buf = s->bat.bat_buf;
blk = s->bat.pbw_blk;
init_vhd_request(s, req);
memcpy(buf, &bat_entry(s, blk - (blk % 128)), 512);
((u32 *)buf)[blk % 128] = s->bat.pbw_offset;
for (i = 0; i < 128; i++)
BE32_OUT(&((u32 *)buf)[i]);
offset = s->vhd.header.table_offset + (blk - (blk % 128)) * 4;
req->treq.secs = 1;
req->treq.buf = buf;
req->op = VHD_OP_BAT_WRITE;
req->next = NULL;
aio_write(s, req, offset);
set_vhd_flag(s->bat.status, VHD_FLAG_BAT_WRITE_STARTED);
DBG(TLOG_DBG, "blk: 0x%04x, pbwo: 0x%08"PRIx64", "
"table_offset: 0x%08"PRIx64"\n", blk, s->bat.pbw_offset, offset);
return 0;
}
static void
schedule_zero_bm_write(struct vhd_state *s,
struct vhd_bitmap *bm, uint64_t lb_end)
{
uint64_t offset;
struct vhd_request *req = &s->bat.zero_req;
init_vhd_request(s, req);
offset = vhd_sectors_to_bytes(lb_end);
req->op = VHD_OP_ZERO_BM_WRITE;
req->treq.sec = s->bat.pbw_blk * s->spb;
req->treq.secs = (s->bat.pbw_offset - lb_end) + s->bm_secs;
req->treq.buf = vhd_zeros(vhd_sectors_to_bytes(req->treq.secs));
req->next = NULL;
DBG(TLOG_DBG, "blk: 0x%04x, writing zero bitmap at 0x%08"PRIx64"\n",
s->bat.pbw_blk, offset);
lock_bitmap(bm);
add_to_transaction(&bm->tx, req);
aio_write(s, req, offset);
}
static int
update_bat(struct vhd_state *s, uint32_t blk)
{
int err;
uint64_t lb_end;
struct vhd_bitmap *bm;
ASSERT(bat_entry(s, blk) == DD_BLK_UNUSED);
if (bat_locked(s)) {
ASSERT(s->bat.pbw_blk == blk);
return 0;
}
/* empty bitmap could already be in
* cache if earlier bat update failed */
bm = get_bitmap(s, blk);
if (!bm) {
/* install empty bitmap in cache */
err = alloc_vhd_bitmap(s, &bm, blk);
if (err)
return err;
install_bitmap(s, bm);
}
lock_bat(s);
lb_end = reserve_new_block(s, blk);
schedule_zero_bm_write(s, bm, lb_end);
set_vhd_flag(bm->tx.status, VHD_FLAG_TX_UPDATE_BAT);
return 0;
}
static int
allocate_block(struct vhd_state *s, uint32_t blk)
{
char *zeros;
int err, gap;
uint64_t offset, size;
struct vhd_bitmap *bm;
ASSERT(bat_entry(s, blk) == DD_BLK_UNUSED);
if (bat_locked(s)) {
ASSERT(s->bat.pbw_blk == blk);
if (s->bat.req.error)
return -EBUSY;
return 0;
}
gap = 0;
s->bat.pbw_blk = blk;
offset = vhd_sectors_to_bytes(s->next_db);
/* data region of segment should begin on page boundary */
if ((s->next_db + s->bm_secs) % s->spp) {
gap = (s->spp - ((s->next_db + s->bm_secs) % s->spp));
s->next_db += gap;
}
s->bat.pbw_offset = s->next_db;
DBG(TLOG_DBG, "blk: 0x%04x, pbwo: 0x%08"PRIx64"\n",
blk, s->bat.pbw_offset);
if (lseek(s->vhd.fd, offset, SEEK_SET) == (off_t)-1) {
ERR(errno, "lseek failed\n");
return -errno;
}
size = vhd_sectors_to_bytes(s->spb + s->bm_secs + gap);
err = write(s->vhd.fd, vhd_zeros(size), size);
if (err != size) {
err = (err == -1 ? -errno : -EIO);
ERR(err, "write failed");
return err;
}
/* empty bitmap could already be in
* cache if earlier bat update failed */
bm = get_bitmap(s, blk);
if (!bm) {
/* install empty bitmap in cache */
err = alloc_vhd_bitmap(s, &bm, blk);
if (err)
return err;
install_bitmap(s, bm);
}
lock_bat(s);
lock_bitmap(bm);
schedule_bat_write(s);
add_to_transaction(&bm->tx, &s->bat.req);
return 0;
}
static int
schedule_data_read(struct vhd_state *s, td_request_t treq, vhd_flag_t flags)
{
u64 offset;
u32 blk = 0, sec = 0;
struct vhd_bitmap *bm;
struct vhd_request *req;
if (s->vhd.footer.type == HD_TYPE_FIXED) {
offset = vhd_sectors_to_bytes(treq.sec);
goto make_request;
}
blk = treq.sec / s->spb;
sec = treq.sec % s->spb;
bm = get_bitmap(s, blk);
offset = bat_entry(s, blk);
ASSERT(offset != DD_BLK_UNUSED);
ASSERT(test_batmap(s, blk) || (bm && bitmap_valid(bm)));
offset += s->bm_secs + sec;
offset = vhd_sectors_to_bytes(offset);
make_request:
req = alloc_vhd_request(s);
if (!req)
return -EBUSY;
req->treq = treq;
req->flags = flags;
req->op = VHD_OP_DATA_READ;
req->next = NULL;
aio_read(s, req, offset);
DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", blk: 0x%04x, sec: 0x%04x, "
"nr_secs: 0x%04x, offset: 0x%08"PRIx64", flags: 0x%08x, buf: %p\n",
s->vhd.file, treq.sec, blk, sec, treq.secs, offset, req->flags,
treq.buf);
return 0;
}
static int
schedule_data_write(struct vhd_state *s, td_request_t treq, vhd_flag_t flags)
{
int err;
u64 offset;
u32 blk = 0, sec = 0;
struct vhd_bitmap *bm = NULL;
struct vhd_request *req;
if (s->vhd.footer.type == HD_TYPE_FIXED) {
offset = vhd_sectors_to_bytes(treq.sec);
goto make_request;
}
blk = treq.sec / s->spb;
sec = treq.sec % s->spb;
offset = bat_entry(s, blk);
if (test_vhd_flag(flags, VHD_FLAG_REQ_UPDATE_BAT)) {
if (test_vhd_flag(s->flags, VHD_FLAG_OPEN_PREALLOCATE))
err = allocate_block(s, blk);
else
err = update_bat(s, blk);
if (err)
return err;
offset = s->bat.pbw_offset;
}
offset += s->bm_secs + sec;
offset = vhd_sectors_to_bytes(offset);
make_request:
req = alloc_vhd_request(s);
if (!req)
return -EBUSY;
req->treq = treq;
req->flags = flags;
req->op = VHD_OP_DATA_WRITE;
req->next = NULL;
if (test_vhd_flag(flags, VHD_FLAG_REQ_UPDATE_BITMAP)) {
bm = get_bitmap(s, blk);
ASSERT(bm && bitmap_valid(bm));
lock_bitmap(bm);
if (bm->tx.closed) {
add_to_tail(&bm->queue, req);
set_vhd_flag(req->flags, VHD_FLAG_REQ_QUEUED);
} else
add_to_transaction(&bm->tx, req);
}
aio_write(s, req, offset);
DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", blk: 0x%04x, sec: 0x%04x, "
"nr_secs: 0x%04x, offset: 0x%08"PRIx64", flags: 0x%08x\n",
s->vhd.file, treq.sec, blk, sec, treq.secs, offset, req->flags);
return 0;
}
static int
schedule_bitmap_read(struct vhd_state *s, uint32_t blk)
{
int err;
u64 offset;
struct vhd_bitmap *bm;
struct vhd_request *req = NULL;
ASSERT(vhd_type_dynamic(&s->vhd));
offset = bat_entry(s, blk);
ASSERT(offset != DD_BLK_UNUSED);
ASSERT(!get_bitmap(s, blk));
offset = vhd_sectors_to_bytes(offset);
err = alloc_vhd_bitmap(s, &bm, blk);
if (err)
return err;
req = &bm->req;
init_vhd_request(s, req);
req->treq.sec = blk * s->spb;
req->treq.secs = s->bm_secs;
req->treq.buf = bm->map;
req->treq.cb = NULL;
req->op = VHD_OP_BITMAP_READ;
req->next = NULL;
aio_read(s, req, offset);
lock_bitmap(bm);
install_bitmap(s, bm);
set_vhd_flag(bm->status, VHD_FLAG_BM_READ_PENDING);
DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", blk: 0x%04x, nr_secs: 0x%04x, "
"offset: 0x%08"PRIx64"\n", s->vhd.file, req->treq.sec, blk,
req->treq.secs, offset);
return 0;
}
static void
schedule_bitmap_write(struct vhd_state *s, uint32_t blk)
{
u64 offset;
struct vhd_bitmap *bm;
struct vhd_request *req;
bm = get_bitmap(s, blk);
offset = bat_entry(s, blk);
ASSERT(vhd_type_dynamic(&s->vhd));
ASSERT(bm && bitmap_valid(bm) &&
!test_vhd_flag(bm->status, VHD_FLAG_BM_WRITE_PENDING));
if (offset == DD_BLK_UNUSED) {
ASSERT(bat_locked(s) && s->bat.pbw_blk == blk);
offset = s->bat.pbw_offset;
}
offset = vhd_sectors_to_bytes(offset);
req = &bm->req;
init_vhd_request(s, req);
req->treq.sec = blk * s->spb;
req->treq.secs = s->bm_secs;
req->treq.buf = bm->shadow;
req->treq.cb = NULL;
req->op = VHD_OP_BITMAP_WRITE;
req->next = NULL;
aio_write(s, req, offset);
lock_bitmap(bm);
touch_bitmap(s, bm); /* bump lru count */
set_vhd_flag(bm->status, VHD_FLAG_BM_WRITE_PENDING);
DBG(TLOG_DBG, "%s: blk: 0x%04x, sec: 0x%08"PRIx64", nr_secs: 0x%04x, "
"offset: 0x%"PRIx64"\n", s->vhd.file, blk, req->treq.sec,
req->treq.secs, offset);
}
/*
* queued requests will be submitted once the bitmap
* describing them is read and the requests are validated.
*/
static int
__vhd_queue_request(struct vhd_state *s, uint8_t op, td_request_t treq)
{
u32 blk;
struct vhd_bitmap *bm;
struct vhd_request *req;
ASSERT(vhd_type_dynamic(&s->vhd));
blk = treq.sec / s->spb;
bm = get_bitmap(s, blk);
ASSERT(bm && test_vhd_flag(bm->status, VHD_FLAG_BM_READ_PENDING));
req = alloc_vhd_request(s);
if (!req)
return -EBUSY;
req->treq = treq;
req->op = op;
req->next = NULL;
add_to_tail(&bm->waiting, req);
lock_bitmap(bm);
DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", blk: 0x%04x nr_secs: 0x%04x, "
"op: %u\n", s->vhd.file, treq.sec, blk, treq.secs, op);
TRACE(s);
return 0;
}
static void
vhd_queue_read(td_driver_t *driver, td_request_t treq)
{
struct vhd_state *s = (struct vhd_state *)driver->data;
DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", secs: 0x%04x (seg: %d)\n",
s->vhd.file, treq.sec, treq.secs, treq.sidx);
while (treq.secs) {
int err;
td_request_t clone;
err = 0;
clone = treq;
switch (read_bitmap_cache(s, clone.sec, VHD_OP_DATA_READ)) {
case -EINVAL:
err = -EINVAL;
goto fail;
case VHD_BM_BAT_CLEAR:
clone.secs = MIN(clone.secs, s->spb - (clone.sec % s->spb));
td_forward_request(clone);
break;
case VHD_BM_BIT_CLEAR:
clone.secs = read_bitmap_cache_span(s, clone.sec, clone.secs, 0);
td_forward_request(clone);
break;
case VHD_BM_BIT_SET:
clone.secs = read_bitmap_cache_span(s, clone.sec, clone.secs, 1);
err = schedule_data_read(s, clone, 0);
if (err)
goto fail;
break;
case VHD_BM_NOT_CACHED:
err = schedule_bitmap_read(s, clone.sec / s->spb);
if (err)
goto fail;
clone.secs = MIN(clone.secs, s->spb - (clone.sec % s->spb));
err = __vhd_queue_request(s, VHD_OP_DATA_READ, clone);
if (err)
goto fail;
break;
case VHD_BM_READ_PENDING:
clone.secs = MIN(clone.secs, s->spb - (clone.sec % s->spb));
err = __vhd_queue_request(s, VHD_OP_DATA_READ, clone);
if (err)
goto fail;
break;
case VHD_BM_BAT_LOCKED:
default:
ASSERT(0);
break;
}
treq.sec += clone.secs;
treq.secs -= clone.secs;
treq.buf += vhd_sectors_to_bytes(clone.secs);
continue;
fail:
clone.secs = treq.secs;
td_complete_request(clone, err);
break;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -