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