]>
Commit | Line | Data |
---|---|---|
298800ca SH |
1 | /* |
2 | * QEMU Enhanced Disk Format Cluster functions | |
3 | * | |
4 | * Copyright IBM, Corp. 2010 | |
5 | * | |
6 | * Authors: | |
7 | * Stefan Hajnoczi <[email protected]> | |
8 | * Anthony Liguori <[email protected]> | |
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 | } |