📄 block-qcow2.c.svn-base
字号:
sn = &s->snapshots[snapshot_index]; if (update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0) goto fail; if (grow_l1_table(bs, sn->l1_size) < 0) goto fail; s->l1_size = sn->l1_size; l1_size2 = s->l1_size * sizeof(uint64_t); /* copy the snapshot l1 table to the current l1 table */ if (bdrv_pread(s->hd, sn->l1_table_offset, s->l1_table, l1_size2) != l1_size2) goto fail; if (bdrv_pwrite(s->hd, s->l1_table_offset, s->l1_table, l1_size2) != l1_size2) goto fail; for(i = 0;i < s->l1_size; i++) { be64_to_cpus(&s->l1_table[i]); } if (update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0) goto fail;#ifdef DEBUG_ALLOC check_refcounts(bs);#endif return 0; fail: return -EIO;}static int qcow_snapshot_delete(BlockDriverState *bs, const char *snapshot_id){ BDRVQcowState *s = bs->opaque; QCowSnapshot *sn; int snapshot_index, ret; snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); if (snapshot_index < 0) return -ENOENT; sn = &s->snapshots[snapshot_index]; ret = update_snapshot_refcount(bs, sn->l1_table_offset, sn->l1_size, -1); if (ret < 0) return ret; /* must update the copied flag on the current cluster offsets */ ret = update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); if (ret < 0) return ret; free_clusters(bs, sn->l1_table_offset, sn->l1_size * sizeof(uint64_t)); qemu_free(sn->id_str); qemu_free(sn->name); memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn)); s->nb_snapshots--; ret = qcow_write_snapshots(bs); if (ret < 0) { /* XXX: restore snapshot if error ? */ return ret; }#ifdef DEBUG_ALLOC check_refcounts(bs);#endif return 0;}static int qcow_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab){ BDRVQcowState *s = bs->opaque; QEMUSnapshotInfo *sn_tab, *sn_info; QCowSnapshot *sn; int i; sn_tab = qemu_mallocz(s->nb_snapshots * sizeof(QEMUSnapshotInfo)); if (!sn_tab) goto fail; for(i = 0; i < s->nb_snapshots; i++) { sn_info = sn_tab + i; sn = s->snapshots + i; pstrcpy(sn_info->id_str, sizeof(sn_info->id_str), sn->id_str); pstrcpy(sn_info->name, sizeof(sn_info->name), sn->name); sn_info->vm_state_size = sn->vm_state_size; sn_info->date_sec = sn->date_sec; sn_info->date_nsec = sn->date_nsec; sn_info->vm_clock_nsec = sn->vm_clock_nsec; } *psn_tab = sn_tab; return s->nb_snapshots; fail: qemu_free(sn_tab); *psn_tab = NULL; return -ENOMEM;}/*********************************************************//* refcount handling */static int refcount_init(BlockDriverState *bs){ BDRVQcowState *s = bs->opaque; int ret, refcount_table_size2, i; s->refcount_block_cache = qemu_malloc(s->cluster_size); if (!s->refcount_block_cache) goto fail; refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); s->refcount_table = qemu_malloc(refcount_table_size2); if (!s->refcount_table) goto fail; if (s->refcount_table_size > 0) { ret = bdrv_pread(s->hd, s->refcount_table_offset, s->refcount_table, refcount_table_size2); if (ret != refcount_table_size2) goto fail; for(i = 0; i < s->refcount_table_size; i++) be64_to_cpus(&s->refcount_table[i]); } return 0; fail: return -ENOMEM;}static void refcount_close(BlockDriverState *bs){ BDRVQcowState *s = bs->opaque; qemu_free(s->refcount_block_cache); qemu_free(s->refcount_table);}static int load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset){ BDRVQcowState *s = bs->opaque; int ret; ret = bdrv_pread(s->hd, refcount_block_offset, s->refcount_block_cache, s->cluster_size); if (ret != s->cluster_size) return -EIO; s->refcount_block_cache_offset = refcount_block_offset; return 0;}static int get_refcount(BlockDriverState *bs, int64_t cluster_index){ BDRVQcowState *s = bs->opaque; int refcount_table_index, block_index; int64_t refcount_block_offset; refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); if (refcount_table_index >= s->refcount_table_size) return 0; refcount_block_offset = s->refcount_table[refcount_table_index]; if (!refcount_block_offset) return 0; if (refcount_block_offset != s->refcount_block_cache_offset) { /* better than nothing: return allocated if read error */ if (load_refcount_block(bs, refcount_block_offset) < 0) return 1; } block_index = cluster_index & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); return be16_to_cpu(s->refcount_block_cache[block_index]);}/* return < 0 if error */static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size){ BDRVQcowState *s = bs->opaque; int i, nb_clusters; nb_clusters = (size + s->cluster_size - 1) >> s->cluster_bits; for(;;) { if (get_refcount(bs, s->free_cluster_index) == 0) { s->free_cluster_index++; for(i = 1; i < nb_clusters; i++) { if (get_refcount(bs, s->free_cluster_index) != 0) goto not_found; s->free_cluster_index++; }#ifdef DEBUG_ALLOC2 printf("alloc_clusters: size=%lld -> %lld\n", size, (s->free_cluster_index - nb_clusters) << s->cluster_bits);#endif return (s->free_cluster_index - nb_clusters) << s->cluster_bits; } else { not_found: s->free_cluster_index++; } }}static int64_t alloc_clusters(BlockDriverState *bs, int64_t size){ int64_t offset; offset = alloc_clusters_noref(bs, size); update_refcount(bs, offset, size, 1); return offset;}/* only used to allocate compressed sectors. We try to allocate contiguous sectors. size must be <= cluster_size */static int64_t alloc_bytes(BlockDriverState *bs, int size){ BDRVQcowState *s = bs->opaque; int64_t offset, cluster_offset; int free_in_cluster; assert(size > 0 && size <= s->cluster_size); if (s->free_byte_offset == 0) { s->free_byte_offset = alloc_clusters(bs, s->cluster_size); } redo: free_in_cluster = s->cluster_size - (s->free_byte_offset & (s->cluster_size - 1)); if (size <= free_in_cluster) { /* enough space in current cluster */ offset = s->free_byte_offset; s->free_byte_offset += size; free_in_cluster -= size; if (free_in_cluster == 0) s->free_byte_offset = 0; if ((offset & (s->cluster_size - 1)) != 0) update_cluster_refcount(bs, offset >> s->cluster_bits, 1); } else { offset = alloc_clusters(bs, s->cluster_size); cluster_offset = s->free_byte_offset & ~(s->cluster_size - 1); if ((cluster_offset + s->cluster_size) == offset) { /* we are lucky: contiguous data */ offset = s->free_byte_offset; update_cluster_refcount(bs, offset >> s->cluster_bits, 1); s->free_byte_offset += size; } else { s->free_byte_offset = offset; goto redo; } } return offset;}static void free_clusters(BlockDriverState *bs, int64_t offset, int64_t size){ update_refcount(bs, offset, size, -1);}static int grow_refcount_table(BlockDriverState *bs, int min_size){ BDRVQcowState *s = bs->opaque; int new_table_size, new_table_size2, refcount_table_clusters, i, ret; uint64_t *new_table; int64_t table_offset; uint64_t data64; uint32_t data32; int old_table_size; int64_t old_table_offset; if (min_size <= s->refcount_table_size) return 0; /* compute new table size */ refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3); for(;;) { if (refcount_table_clusters == 0) { refcount_table_clusters = 1; } else { refcount_table_clusters = (refcount_table_clusters * 3 + 1) / 2; } new_table_size = refcount_table_clusters << (s->cluster_bits - 3); if (min_size <= new_table_size) break; }#ifdef DEBUG_ALLOC2 printf("grow_refcount_table from %d to %d\n", s->refcount_table_size, new_table_size);#endif new_table_size2 = new_table_size * sizeof(uint64_t); new_table = qemu_mallocz(new_table_size2); if (!new_table) return -ENOMEM; memcpy(new_table, s->refcount_table, s->refcount_table_size * sizeof(uint64_t)); for(i = 0; i < s->refcount_table_size; i++) cpu_to_be64s(&new_table[i]); /* Note: we cannot update the refcount now to avoid recursion */ table_offset = alloc_clusters_noref(bs, new_table_size2); ret = bdrv_pwrite(s->hd, table_offset, new_table, new_table_size2); if (ret != new_table_size2) goto fail; for(i = 0; i < s->refcount_table_size; i++) be64_to_cpus(&new_table[i]); data64 = cpu_to_be64(table_offset); if (bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_offset), &data64, sizeof(data64)) != sizeof(data64)) goto fail; data32 = cpu_to_be32(refcount_table_clusters); if (bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_clusters), &data32, sizeof(data32)) != sizeof(data32)) goto fail; qemu_free(s->refcount_table); old_table_offset = s->refcount_table_offset; old_table_size = s->refcount_table_size; s->refcount_table = new_table; s->refcount_table_size = new_table_size; s->refcount_table_offset = table_offset; update_refcount(bs, table_offset, new_table_size2, 1); free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t)); return 0; fail: free_clusters(bs, table_offset, new_table_size2); qemu_free(new_table); return -EIO;}/* addend must be 1 or -1 *//* XXX: cache several refcount block clusters ? */static int update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, int addend){ BDRVQcowState *s = bs->opaque; int64_t offset, refcount_block_offset; int ret, refcount_table_index, block_index, refcount; uint64_t data64; refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); if (refcount_table_index >= s->refcount_table_size) { if (addend < 0) return -EINVAL; ret = grow_refcount_table(bs, refcount_table_index + 1); if (ret < 0) return ret; } refcount_block_offset = s->refcount_table[refcount_table_index]; if (!refcount_block_offset) { if (addend < 0) return -EINVAL; /* create a new refcount block */ /* Note: we cannot update the refcount now to avoid recursion */ offset = alloc_clusters_noref(bs, s->cluster_size); memset(s->refcount_block_cache, 0, s->cluster_size); ret = bdrv_pwrite(s->hd, offset, s->refcount_block_cache, s->cluster_size); if (ret != s->cluster_size) return -EINVAL; s->refcount_table[refcount_table_index] = offset; data64 = cpu_to_be64(offset); ret = bdrv_pwrite(s->hd, s->refcount_table_offset + refcount_table_index * sizeof(uint64_t), &data64, sizeof(data64)); if (ret != sizeof(data64)) return -EINVAL; refcount_block_offset = offset; s->refcount_block_cache_offset = offset; update_refcount(bs, offset, s->cluster_size, 1); } else { if (refcount_block_offset != s->refcount_block_cache_offset) { if (load_refcount_block(bs, refcount_block_offset) < 0) return -EIO; } } /* we can update the count and save it */ block_index = cluster_index & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); refcount = be16_to_cpu(s->refcount_block_cache[block_index]); refcount += addend; if (refcount < 0 || refcount > 0xffff) return -EINVAL; if (refcount == 0 && cluster_index < s->free_cluster_index) { s->free_cluster_index = cluster_index; } s->refcount_block_cache[block_index] = cpu_to_be16(refcount); if (bdrv_pwrite(s->hd, refcount_block_offset + (block_index << REFCOUNT_SHIFT), &s->refcount_block_cache[block_index], 2) != 2) return -EIO; return refcount;}static void update_refcount(BlockDriverState *bs, int64_t offset, int64_t length, int addend){ BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset;#ifdef DEBUG_ALLOC2 printf("update_refcount: offset=%lld size=%lld addend=%d\n", offset, length, addend);#endif if (length <= 0) return; start = offset & ~(s->cluster_size - 1); last = (offset + length - 1) & ~(s->cluster_size - 1); for(cluster_offset = start; cluster_offset <= last;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -