]> git.proxmox.com Git - mirror_qemu.git/blob - block/qed-cluster.c
Merge branch 'spice.v23.pull' of git://anongit.freedesktop.org/spice/qemu
[mirror_qemu.git] / block / qed-cluster.c
1 /*
2 * QEMU Enhanced Disk Format Cluster functions
3 *
4 * Copyright IBM, Corp. 2010
5 *
6 * Authors:
7 * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
8 * Anthony Liguori <aliguori@us.ibm.com>
9 *
10 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
11 * See the COPYING.LIB file in the top-level directory.
12 *
13 */
14
15 #include "qed.h"
16
17 /**
18 * Count the number of contiguous data clusters
19 *
20 * @s: QED state
21 * @table: L2 table
22 * @index: First cluster index
23 * @n: Maximum number of clusters
24 * @offset: Set to first cluster offset
25 *
26 * This function scans tables for contiguous allocated or free clusters.
27 */
28 static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s,
29 QEDTable *table,
30 unsigned int index,
31 unsigned int n,
32 uint64_t *offset)
33 {
34 unsigned int end = MIN(index + n, s->table_nelems);
35 uint64_t last = table->offsets[index];
36 unsigned int i;
37
38 *offset = last;
39
40 for (i = index + 1; i < end; i++) {
41 if (last == 0) {
42 /* Counting free clusters */
43 if (table->offsets[i] != 0) {
44 break;
45 }
46 } else {
47 /* Counting allocated clusters */
48 if (table->offsets[i] != last + s->header.cluster_size) {
49 break;
50 }
51 last = table->offsets[i];
52 }
53 }
54 return i - index;
55 }
56
57 typedef struct {
58 BDRVQEDState *s;
59 uint64_t pos;
60 size_t len;
61
62 QEDRequest *request;
63
64 /* User callback */
65 QEDFindClusterFunc *cb;
66 void *opaque;
67 } QEDFindClusterCB;
68
69 static void qed_find_cluster_cb(void *opaque, int ret)
70 {
71 QEDFindClusterCB *find_cluster_cb = opaque;
72 BDRVQEDState *s = find_cluster_cb->s;
73 QEDRequest *request = find_cluster_cb->request;
74 uint64_t offset = 0;
75 size_t len = 0;
76 unsigned int index;
77 unsigned int n;
78
79 if (ret) {
80 goto out;
81 }
82
83 index = qed_l2_index(s, find_cluster_cb->pos);
84 n = qed_bytes_to_clusters(s,
85 qed_offset_into_cluster(s, find_cluster_cb->pos) +
86 find_cluster_cb->len);
87 n = qed_count_contiguous_clusters(s, request->l2_table->table,
88 index, n, &offset);
89
90 ret = offset ? QED_CLUSTER_FOUND : QED_CLUSTER_L2;
91 len = MIN(find_cluster_cb->len, n * s->header.cluster_size -
92 qed_offset_into_cluster(s, find_cluster_cb->pos));
93
94 if (offset && !qed_check_cluster_offset(s, offset)) {
95 ret = -EINVAL;
96 }
97
98 out:
99 find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len);
100 qemu_free(find_cluster_cb);
101 }
102
103 /**
104 * Find the offset of a data cluster
105 *
106 * @s: QED state
107 * @request: L2 cache entry
108 * @pos: Byte position in device
109 * @len: Number of bytes
110 * @cb: Completion function
111 * @opaque: User data for completion function
112 *
113 * This function translates a position in the block device to an offset in the
114 * image file. It invokes the cb completion callback to report back the
115 * translated offset or unallocated range in the image file.
116 *
117 * If the L2 table exists, request->l2_table points to the L2 table cache entry
118 * and the caller must free the reference when they are finished. The cache
119 * entry is exposed in this way to avoid callers having to read the L2 table
120 * again later during request processing. If request->l2_table is non-NULL it
121 * will be unreferenced before taking on the new cache entry.
122 */
123 void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
124 size_t len, QEDFindClusterFunc *cb, void *opaque)
125 {
126 QEDFindClusterCB *find_cluster_cb;
127 uint64_t l2_offset;
128
129 /* Limit length to L2 boundary. Requests are broken up at the L2 boundary
130 * so that a request acts on one L2 table at a time.
131 */
132 len = MIN(len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos);
133
134 l2_offset = s->l1_table->offsets[qed_l1_index(s, pos)];
135 if (!l2_offset) {
136 cb(opaque, QED_CLUSTER_L1, 0, len);
137 return;
138 }
139 if (!qed_check_table_offset(s, l2_offset)) {
140 cb(opaque, -EINVAL, 0, 0);
141 return;
142 }
143
144 find_cluster_cb = qemu_malloc(sizeof(*find_cluster_cb));
145 find_cluster_cb->s = s;
146 find_cluster_cb->pos = pos;
147 find_cluster_cb->len = len;
148 find_cluster_cb->cb = cb;
149 find_cluster_cb->opaque = opaque;
150 find_cluster_cb->request = request;
151
152 qed_read_l2_table(s, request, l2_offset,
153 qed_find_cluster_cb, find_cluster_cb);
154 }