xref: /openbmc/qemu/block/qed-check.c (revision 7c6da3de)
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 
72     for (i = 0; i < s->table_nelems; i++) {
73         uint64_t offset = table->offsets[i];
74 
75         if (qed_offset_is_unalloc_cluster(offset) ||
76             qed_offset_is_zero_cluster(offset)) {
77             continue;
78         }
79 
80         /* Detect invalid cluster offset */
81         if (!qed_check_cluster_offset(s, offset)) {
82             if (check->fix) {
83                 table->offsets[i] = 0;
84             } else {
85                 check->result->corruptions++;
86             }
87 
88             num_invalid++;
89             continue;
90         }
91 
92         qed_set_used_clusters(check, offset, 1);
93     }
94 
95     return num_invalid;
96 }
97 
98 /**
99  * Descend tables and check each cluster is referenced once only
100  */
101 static int qed_check_l1_table(QEDCheck *check, QEDTable *table)
102 {
103     BDRVQEDState *s = check->s;
104     unsigned int i, num_invalid_l1 = 0;
105     int ret, last_error = 0;
106 
107     /* Mark L1 table clusters used */
108     qed_set_used_clusters(check, s->header.l1_table_offset,
109                           s->header.table_size);
110 
111     for (i = 0; i < s->table_nelems; i++) {
112         unsigned int num_invalid_l2;
113         uint64_t offset = table->offsets[i];
114 
115         if (qed_offset_is_unalloc_cluster(offset)) {
116             continue;
117         }
118 
119         /* Detect invalid L2 offset */
120         if (!qed_check_table_offset(s, offset)) {
121             /* Clear invalid offset */
122             if (check->fix) {
123                 table->offsets[i] = 0;
124             } else {
125                 check->result->corruptions++;
126             }
127 
128             num_invalid_l1++;
129             continue;
130         }
131 
132         if (!qed_set_used_clusters(check, offset, s->header.table_size)) {
133             continue; /* skip an invalid table */
134         }
135 
136         ret = qed_read_l2_table_sync(s, &check->request, offset);
137         if (ret) {
138             check->result->check_errors++;
139             last_error = ret;
140             continue;
141         }
142 
143         num_invalid_l2 = qed_check_l2_table(check,
144                                             check->request.l2_table->table);
145 
146         /* Write out fixed L2 table */
147         if (num_invalid_l2 > 0 && check->fix) {
148             ret = qed_write_l2_table_sync(s, &check->request, 0,
149                                           s->table_nelems, false);
150             if (ret) {
151                 check->result->check_errors++;
152                 last_error = ret;
153                 continue;
154             }
155         }
156     }
157 
158     /* Drop reference to final table */
159     qed_unref_l2_cache_entry(check->request.l2_table);
160     check->request.l2_table = NULL;
161 
162     /* Write out fixed L1 table */
163     if (num_invalid_l1 > 0 && check->fix) {
164         ret = qed_write_l1_table_sync(s, 0, s->table_nelems);
165         if (ret) {
166             check->result->check_errors++;
167             last_error = ret;
168         }
169     }
170 
171     return last_error;
172 }
173 
174 /**
175  * Check for unreferenced (leaked) clusters
176  */
177 static void qed_check_for_leaks(QEDCheck *check)
178 {
179     BDRVQEDState *s = check->s;
180     uint64_t i;
181 
182     for (i = s->header.header_size; i < check->nclusters; i++) {
183         if (!qed_test_bit(check->used_clusters, i)) {
184             check->result->leaks++;
185         }
186     }
187 }
188 
189 int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
190 {
191     QEDCheck check = {
192         .s = s,
193         .result = result,
194         .nclusters = qed_bytes_to_clusters(s, s->file_size),
195         .request = { .l2_table = NULL },
196         .fix = fix,
197     };
198     int ret;
199 
200     check.used_clusters = g_malloc0(((check.nclusters + 31) / 32) *
201                                        sizeof(check.used_clusters[0]));
202 
203     ret = qed_check_l1_table(&check, s->l1_table);
204     if (ret == 0) {
205         /* Only check for leaks if entire image was scanned successfully */
206         qed_check_for_leaks(&check);
207     }
208 
209     g_free(check.used_clusters);
210     return ret;
211 }
212