root / block / qed-check.c @ 7a39fe58
History | View | Annotate | Download (5.3 kB)
1 | 01979a98 | Stefan Hajnoczi | /*
|
---|---|---|---|
2 | 01979a98 | Stefan Hajnoczi | * QEMU Enhanced Disk Format Consistency Check
|
3 | 01979a98 | Stefan Hajnoczi | *
|
4 | 01979a98 | Stefan Hajnoczi | * Copyright IBM, Corp. 2010
|
5 | 01979a98 | Stefan Hajnoczi | *
|
6 | 01979a98 | Stefan Hajnoczi | * Authors:
|
7 | 01979a98 | Stefan Hajnoczi | * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
8 | 01979a98 | Stefan Hajnoczi | *
|
9 | 01979a98 | Stefan Hajnoczi | * This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
10 | 01979a98 | Stefan Hajnoczi | * See the COPYING.LIB file in the top-level directory.
|
11 | 01979a98 | Stefan Hajnoczi | *
|
12 | 01979a98 | Stefan Hajnoczi | */
|
13 | 01979a98 | Stefan Hajnoczi | |
14 | 01979a98 | Stefan Hajnoczi | #include "qed.h" |
15 | 01979a98 | Stefan Hajnoczi | |
16 | 01979a98 | Stefan Hajnoczi | typedef struct { |
17 | 01979a98 | Stefan Hajnoczi | BDRVQEDState *s; |
18 | 01979a98 | Stefan Hajnoczi | BdrvCheckResult *result; |
19 | 01979a98 | Stefan Hajnoczi | bool fix; /* whether to fix invalid offsets */ |
20 | 01979a98 | Stefan Hajnoczi | |
21 | 01979a98 | Stefan Hajnoczi | size_t nclusters; |
22 | 01979a98 | Stefan Hajnoczi | uint32_t *used_clusters; /* referenced cluster bitmap */
|
23 | 01979a98 | Stefan Hajnoczi | |
24 | 01979a98 | Stefan Hajnoczi | QEDRequest request; |
25 | 01979a98 | Stefan Hajnoczi | } QEDCheck; |
26 | 01979a98 | Stefan Hajnoczi | |
27 | 01979a98 | Stefan Hajnoczi | static bool qed_test_bit(uint32_t *bitmap, uint64_t n) { |
28 | 01979a98 | Stefan Hajnoczi | return !!(bitmap[n / 32] & (1 << (n % 32))); |
29 | 01979a98 | Stefan Hajnoczi | } |
30 | 01979a98 | Stefan Hajnoczi | |
31 | 01979a98 | Stefan Hajnoczi | static void qed_set_bit(uint32_t *bitmap, uint64_t n) { |
32 | 01979a98 | Stefan Hajnoczi | bitmap[n / 32] |= 1 << (n % 32); |
33 | 01979a98 | Stefan Hajnoczi | } |
34 | 01979a98 | Stefan Hajnoczi | |
35 | 01979a98 | Stefan Hajnoczi | /**
|
36 | 01979a98 | Stefan Hajnoczi | * Set bitmap bits for clusters
|
37 | 01979a98 | Stefan Hajnoczi | *
|
38 | 01979a98 | Stefan Hajnoczi | * @check: Check structure
|
39 | 01979a98 | Stefan Hajnoczi | * @offset: Starting offset in bytes
|
40 | 01979a98 | Stefan Hajnoczi | * @n: Number of clusters
|
41 | 01979a98 | Stefan Hajnoczi | */
|
42 | 01979a98 | Stefan Hajnoczi | static bool qed_set_used_clusters(QEDCheck *check, uint64_t offset, |
43 | 01979a98 | Stefan Hajnoczi | unsigned int n) |
44 | 01979a98 | Stefan Hajnoczi | { |
45 | 01979a98 | Stefan Hajnoczi | uint64_t cluster = qed_bytes_to_clusters(check->s, offset); |
46 | 01979a98 | Stefan Hajnoczi | unsigned int corruptions = 0; |
47 | 01979a98 | Stefan Hajnoczi | |
48 | 01979a98 | Stefan Hajnoczi | while (n-- != 0) { |
49 | 01979a98 | Stefan Hajnoczi | /* Clusters should only be referenced once */
|
50 | 01979a98 | Stefan Hajnoczi | if (qed_test_bit(check->used_clusters, cluster)) {
|
51 | 01979a98 | Stefan Hajnoczi | corruptions++; |
52 | 01979a98 | Stefan Hajnoczi | } |
53 | 01979a98 | Stefan Hajnoczi | |
54 | 01979a98 | Stefan Hajnoczi | qed_set_bit(check->used_clusters, cluster); |
55 | 01979a98 | Stefan Hajnoczi | cluster++; |
56 | 01979a98 | Stefan Hajnoczi | } |
57 | 01979a98 | Stefan Hajnoczi | |
58 | 01979a98 | Stefan Hajnoczi | check->result->corruptions += corruptions; |
59 | 01979a98 | Stefan Hajnoczi | return corruptions == 0; |
60 | 01979a98 | Stefan Hajnoczi | } |
61 | 01979a98 | Stefan Hajnoczi | |
62 | 01979a98 | Stefan Hajnoczi | /**
|
63 | 01979a98 | Stefan Hajnoczi | * Check an L2 table
|
64 | 01979a98 | Stefan Hajnoczi | *
|
65 | 01979a98 | Stefan Hajnoczi | * @ret: Number of invalid cluster offsets
|
66 | 01979a98 | Stefan Hajnoczi | */
|
67 | 01979a98 | Stefan Hajnoczi | static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table) |
68 | 01979a98 | Stefan Hajnoczi | { |
69 | 01979a98 | Stefan Hajnoczi | BDRVQEDState *s = check->s; |
70 | 01979a98 | Stefan Hajnoczi | unsigned int i, num_invalid = 0; |
71 | 01979a98 | Stefan Hajnoczi | |
72 | 01979a98 | Stefan Hajnoczi | for (i = 0; i < s->table_nelems; i++) { |
73 | 01979a98 | Stefan Hajnoczi | uint64_t offset = table->offsets[i]; |
74 | 01979a98 | Stefan Hajnoczi | |
75 | 01979a98 | Stefan Hajnoczi | if (!offset) {
|
76 | 01979a98 | Stefan Hajnoczi | continue;
|
77 | 01979a98 | Stefan Hajnoczi | } |
78 | 01979a98 | Stefan Hajnoczi | |
79 | 01979a98 | Stefan Hajnoczi | /* Detect invalid cluster offset */
|
80 | 01979a98 | Stefan Hajnoczi | if (!qed_check_cluster_offset(s, offset)) {
|
81 | 01979a98 | Stefan Hajnoczi | if (check->fix) {
|
82 | 01979a98 | Stefan Hajnoczi | table->offsets[i] = 0;
|
83 | 01979a98 | Stefan Hajnoczi | } else {
|
84 | 01979a98 | Stefan Hajnoczi | check->result->corruptions++; |
85 | 01979a98 | Stefan Hajnoczi | } |
86 | 01979a98 | Stefan Hajnoczi | |
87 | 01979a98 | Stefan Hajnoczi | num_invalid++; |
88 | 01979a98 | Stefan Hajnoczi | continue;
|
89 | 01979a98 | Stefan Hajnoczi | } |
90 | 01979a98 | Stefan Hajnoczi | |
91 | 01979a98 | Stefan Hajnoczi | qed_set_used_clusters(check, offset, 1);
|
92 | 01979a98 | Stefan Hajnoczi | } |
93 | 01979a98 | Stefan Hajnoczi | |
94 | 01979a98 | Stefan Hajnoczi | return num_invalid;
|
95 | 01979a98 | Stefan Hajnoczi | } |
96 | 01979a98 | Stefan Hajnoczi | |
97 | 01979a98 | Stefan Hajnoczi | /**
|
98 | 01979a98 | Stefan Hajnoczi | * Descend tables and check each cluster is referenced once only
|
99 | 01979a98 | Stefan Hajnoczi | */
|
100 | 01979a98 | Stefan Hajnoczi | static int qed_check_l1_table(QEDCheck *check, QEDTable *table) |
101 | 01979a98 | Stefan Hajnoczi | { |
102 | 01979a98 | Stefan Hajnoczi | BDRVQEDState *s = check->s; |
103 | 01979a98 | Stefan Hajnoczi | unsigned int i, num_invalid_l1 = 0; |
104 | 01979a98 | Stefan Hajnoczi | int ret, last_error = 0; |
105 | 01979a98 | Stefan Hajnoczi | |
106 | 01979a98 | Stefan Hajnoczi | /* Mark L1 table clusters used */
|
107 | 01979a98 | Stefan Hajnoczi | qed_set_used_clusters(check, s->header.l1_table_offset, |
108 | 01979a98 | Stefan Hajnoczi | s->header.table_size); |
109 | 01979a98 | Stefan Hajnoczi | |
110 | 01979a98 | Stefan Hajnoczi | for (i = 0; i < s->table_nelems; i++) { |
111 | 01979a98 | Stefan Hajnoczi | unsigned int num_invalid_l2; |
112 | 01979a98 | Stefan Hajnoczi | uint64_t offset = table->offsets[i]; |
113 | 01979a98 | Stefan Hajnoczi | |
114 | 01979a98 | Stefan Hajnoczi | if (!offset) {
|
115 | 01979a98 | Stefan Hajnoczi | continue;
|
116 | 01979a98 | Stefan Hajnoczi | } |
117 | 01979a98 | Stefan Hajnoczi | |
118 | 01979a98 | Stefan Hajnoczi | /* Detect invalid L2 offset */
|
119 | 01979a98 | Stefan Hajnoczi | if (!qed_check_table_offset(s, offset)) {
|
120 | 01979a98 | Stefan Hajnoczi | /* Clear invalid offset */
|
121 | 01979a98 | Stefan Hajnoczi | if (check->fix) {
|
122 | 01979a98 | Stefan Hajnoczi | table->offsets[i] = 0;
|
123 | 01979a98 | Stefan Hajnoczi | } else {
|
124 | 01979a98 | Stefan Hajnoczi | check->result->corruptions++; |
125 | 01979a98 | Stefan Hajnoczi | } |
126 | 01979a98 | Stefan Hajnoczi | |
127 | 01979a98 | Stefan Hajnoczi | num_invalid_l1++; |
128 | 01979a98 | Stefan Hajnoczi | continue;
|
129 | 01979a98 | Stefan Hajnoczi | } |
130 | 01979a98 | Stefan Hajnoczi | |
131 | 01979a98 | Stefan Hajnoczi | if (!qed_set_used_clusters(check, offset, s->header.table_size)) {
|
132 | 01979a98 | Stefan Hajnoczi | continue; /* skip an invalid table */ |
133 | 01979a98 | Stefan Hajnoczi | } |
134 | 01979a98 | Stefan Hajnoczi | |
135 | 01979a98 | Stefan Hajnoczi | ret = qed_read_l2_table_sync(s, &check->request, offset); |
136 | 01979a98 | Stefan Hajnoczi | if (ret) {
|
137 | 01979a98 | Stefan Hajnoczi | check->result->check_errors++; |
138 | 01979a98 | Stefan Hajnoczi | last_error = ret; |
139 | 01979a98 | Stefan Hajnoczi | continue;
|
140 | 01979a98 | Stefan Hajnoczi | } |
141 | 01979a98 | Stefan Hajnoczi | |
142 | 01979a98 | Stefan Hajnoczi | num_invalid_l2 = qed_check_l2_table(check, |
143 | 01979a98 | Stefan Hajnoczi | check->request.l2_table->table); |
144 | 01979a98 | Stefan Hajnoczi | |
145 | 01979a98 | Stefan Hajnoczi | /* Write out fixed L2 table */
|
146 | 01979a98 | Stefan Hajnoczi | if (num_invalid_l2 > 0 && check->fix) { |
147 | 01979a98 | Stefan Hajnoczi | ret = qed_write_l2_table_sync(s, &check->request, 0,
|
148 | 01979a98 | Stefan Hajnoczi | s->table_nelems, false);
|
149 | 01979a98 | Stefan Hajnoczi | if (ret) {
|
150 | 01979a98 | Stefan Hajnoczi | check->result->check_errors++; |
151 | 01979a98 | Stefan Hajnoczi | last_error = ret; |
152 | 01979a98 | Stefan Hajnoczi | continue;
|
153 | 01979a98 | Stefan Hajnoczi | } |
154 | 01979a98 | Stefan Hajnoczi | } |
155 | 01979a98 | Stefan Hajnoczi | } |
156 | 01979a98 | Stefan Hajnoczi | |
157 | 01979a98 | Stefan Hajnoczi | /* Drop reference to final table */
|
158 | 01979a98 | Stefan Hajnoczi | qed_unref_l2_cache_entry(check->request.l2_table); |
159 | 01979a98 | Stefan Hajnoczi | check->request.l2_table = NULL;
|
160 | 01979a98 | Stefan Hajnoczi | |
161 | 01979a98 | Stefan Hajnoczi | /* Write out fixed L1 table */
|
162 | 01979a98 | Stefan Hajnoczi | if (num_invalid_l1 > 0 && check->fix) { |
163 | 01979a98 | Stefan Hajnoczi | ret = qed_write_l1_table_sync(s, 0, s->table_nelems);
|
164 | 01979a98 | Stefan Hajnoczi | if (ret) {
|
165 | 01979a98 | Stefan Hajnoczi | check->result->check_errors++; |
166 | 01979a98 | Stefan Hajnoczi | last_error = ret; |
167 | 01979a98 | Stefan Hajnoczi | } |
168 | 01979a98 | Stefan Hajnoczi | } |
169 | 01979a98 | Stefan Hajnoczi | |
170 | 01979a98 | Stefan Hajnoczi | return last_error;
|
171 | 01979a98 | Stefan Hajnoczi | } |
172 | 01979a98 | Stefan Hajnoczi | |
173 | 01979a98 | Stefan Hajnoczi | /**
|
174 | 01979a98 | Stefan Hajnoczi | * Check for unreferenced (leaked) clusters
|
175 | 01979a98 | Stefan Hajnoczi | */
|
176 | 01979a98 | Stefan Hajnoczi | static void qed_check_for_leaks(QEDCheck *check) |
177 | 01979a98 | Stefan Hajnoczi | { |
178 | 01979a98 | Stefan Hajnoczi | BDRVQEDState *s = check->s; |
179 | 01979a98 | Stefan Hajnoczi | size_t i; |
180 | 01979a98 | Stefan Hajnoczi | |
181 | 01979a98 | Stefan Hajnoczi | for (i = s->header.header_size; i < check->nclusters; i++) {
|
182 | 01979a98 | Stefan Hajnoczi | if (!qed_test_bit(check->used_clusters, i)) {
|
183 | 01979a98 | Stefan Hajnoczi | check->result->leaks++; |
184 | 01979a98 | Stefan Hajnoczi | } |
185 | 01979a98 | Stefan Hajnoczi | } |
186 | 01979a98 | Stefan Hajnoczi | } |
187 | 01979a98 | Stefan Hajnoczi | |
188 | 01979a98 | Stefan Hajnoczi | int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix) |
189 | 01979a98 | Stefan Hajnoczi | { |
190 | 01979a98 | Stefan Hajnoczi | QEDCheck check = { |
191 | 01979a98 | Stefan Hajnoczi | .s = s, |
192 | 01979a98 | Stefan Hajnoczi | .result = result, |
193 | 01979a98 | Stefan Hajnoczi | .nclusters = qed_bytes_to_clusters(s, s->file_size), |
194 | 01979a98 | Stefan Hajnoczi | .request = { .l2_table = NULL },
|
195 | 01979a98 | Stefan Hajnoczi | .fix = fix, |
196 | 01979a98 | Stefan Hajnoczi | }; |
197 | 01979a98 | Stefan Hajnoczi | int ret;
|
198 | 01979a98 | Stefan Hajnoczi | |
199 | 01979a98 | Stefan Hajnoczi | check.used_clusters = qemu_mallocz(((check.nclusters + 31) / 32) * |
200 | 01979a98 | Stefan Hajnoczi | sizeof(check.used_clusters[0])); |
201 | 01979a98 | Stefan Hajnoczi | |
202 | 01979a98 | Stefan Hajnoczi | ret = qed_check_l1_table(&check, s->l1_table); |
203 | 01979a98 | Stefan Hajnoczi | if (ret == 0) { |
204 | 01979a98 | Stefan Hajnoczi | /* Only check for leaks if entire image was scanned successfully */
|
205 | 01979a98 | Stefan Hajnoczi | qed_check_for_leaks(&check); |
206 | 01979a98 | Stefan Hajnoczi | } |
207 | 01979a98 | Stefan Hajnoczi | |
208 | 01979a98 | Stefan Hajnoczi | qemu_free(check.used_clusters); |
209 | 01979a98 | Stefan Hajnoczi | return ret;
|
210 | 01979a98 | Stefan Hajnoczi | } |