上一篇
http://blog.chinaunix.net/uid-29718549-id-4445694.htm 说的在线快照的一点问题,这次记录一下在线删除快照的核心流程,对应qemu 的 block-commit命令,就是实现在qcow2磁盘链表中删除磁盘,合并数据的过程
变量名对应的链表视图:
-> [BASE] -> [INTER1] -> [INTER2] -> [TOP]
-
end = s->common.len >> BDRV_SECTOR_BITS;
-
buf = qemu_blockalign(top, COMMIT_BUFFER_SIZE);
-
for (sector_num = 0; sector_num < end; sector_num += n) {\\遍历top中所有的块
-
uint64_t delay_ns = 0;
-
bool copy;
-
-
wait:
-
/* Note that even when no rate limit is applied we need to yield
-
* with no pending I/O here so that bdrv_drain_all() returns.
-
*/
-
block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns);
-
if (block_job_is_cancelled(&s->common)) {
-
break;
-
}
-
/* Copy if allocated above the base */
-
/*以下这一坨代码负责检查指定范围内(一次检查1024个sector)需不需要执行commit*/
-
ret = bdrv_is_allocated_above(top, base, sector_num,
-
COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE, \\一次检查1024个块
-
&n);\\检查这个范围的块是否在链表中任意磁盘中被写入过,如果有写入(返回值1)->执行以下的copy动作
-
intermediate = top;
-
while (intermediate && intermediate != base)\\遍历链表top(包含top)->base的所有中间部分磁盘
-
ret = bdrv_is_allocated(intermediate, sector_num, nb_sectors,
-
&pnum_inter);
-
ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum);
-
bdrv_get_block_status_co_entry
-
bdrv_co_get_block_status(bs, data->sector_num, data->nb_sectors,
-
data->pnum);\\<qcow2_co_get_block_status>
-
*pnum = nb_sectors;
-
qemu_co_mutex_lock(&s->lock);\\获取状态码之前加锁
-
ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset);\\下面这坨下会详解
-
l1_bits = s->l2_bits + s->cluster_bits;
-
l1_index = offset >> l1_bits;\\这里的offset就是sector_num << 9,也就是seek点
-
if (l1_index >= s->l1_size) ret = QCOW2_CLUSTER_UNALLOCATED;\\offset超出了BDRVQcowState记录的L1表范围,返回未分配
-
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
-
if (l2_offset == NULL) ret=QCOW2_CLUSTER_UNALLOCATED\\在L1 table中无记录,返回未分配
-
ret = l2_load(bs, l2_offset, &l2_table);\\读出L2 table
-
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
-
nb_clusters = size_to_clusters(s, nb_needed << 9);
-
ret = qcow2_get_cluster_type(*cluster_offset);
-
qemu_co_mutex_unlock(&s->lock);\\获取状态码之后解锁
-
/*以上这一坨代码负责检查指定范围内(一次检查1024个sector)需不需要执行commit*/
-
copy = (ret == 1);
-
trace_commit_one_iteration(s, sector_num, n, ret);
-
if (copy) {
-
if (s->common.speed) {\\这部分限速控制
-
delay_ns = ratelimit_calculate_delay(&s->limit, n);
-
if (delay_ns > 0) {
-
goto wait;
-
}
-
}
-
ret = commit_populate(top, base, sector_num, n, buf);\\读出->写入
-
ret = bdrv_read(bs, sector_num, buf, nb_sectors);
-
ret = bdrv_write(base, sector_num, buf, nb_sectors);
-
bytes_written += n * BDRV_SECTOR_SIZE;
-
}
-
if (ret < 0) {
-
if (s->on_error == BLOCKDEV_ON_ERROR_STOP ||
-
s->on_error == BLOCKDEV_ON_ERROR_REPORT||
-
(s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) {
-
goto exit_free_buf;
-
} else {
-
n = 0;
-
continue;
-
}
-
}
-
/* Publish progress */
-
s->common.offset += n * BDRV_SECTOR_SIZE;
-
}
-
-
ret = 0;
-
-
if (!block_job_is_cancelled(&s->common) && sector_num == end) {
-
/* success */
-
ret = bdrv_drop_intermediate(active, top, base);\\丢弃中间的部分,里面是改点属性
-
}
阅读(2388) | 评论(0) | 转发(0) |