Line data Source code
1 : // SPDX-License-Identifier: GPL-3.0-or-later
2 : // Copyright (C) 2026 Andrea Mazzoleni
3 :
4 : #include "portable.h"
5 :
6 : #include "elem.h"
7 : #include "parity.h"
8 : #include "state.h"
9 : #include "stream.h"
10 : #include "locate.h"
11 :
12 : struct snapraid_parity_entry {
13 : uint32_t block_size;
14 : block_off_t low; /**< Lower position in the parity */
15 : block_off_t high; /**< Higher position in the parity */
16 : block_off_t fragments; /**< Number of fragments in the parity */
17 : struct snapraid_file* file;
18 : struct snapraid_disk* disk;
19 : tommy_node node;
20 : };
21 :
22 : struct snapraid_locate_info {
23 : block_off_t block_max; /**< Number of blocks in the parity */
24 : data_off_t parity_size; /**< Size in bytes of the parity */
25 : block_off_t tail_block; /**< Number of blocks of the tail of the parity */
26 : block_off_t low_to_free_tail_block; /**< First block that has to be freed */
27 : };
28 :
29 61484 : static int parity_entry_compare(const void* void_a, const void* void_b)
30 : {
31 61484 : const struct snapraid_parity_entry* entry_a = void_a;
32 61484 : const struct snapraid_parity_entry* entry_b = void_b;
33 :
34 61484 : if (entry_a->high < entry_b->high)
35 36483 : return -1;
36 25001 : if (entry_a->high > entry_b->high)
37 15466 : return 1;
38 :
39 9535 : if (entry_a->low < entry_b->low)
40 3691 : return -1;
41 5844 : if (entry_a->low > entry_b->low)
42 3170 : return 1;
43 :
44 2674 : return strcmp(entry_a->file->sub, entry_b->file->sub);
45 : }
46 :
47 11267 : static void dump_entry(void* void_entry)
48 : {
49 : char esc_buffer[ESC_MAX];
50 11267 : struct snapraid_parity_entry* entry = void_entry;
51 11267 : printf("%12" PRIu64 " ", entry->low * (uint64_t)entry->block_size);
52 11267 : printf("%12" PRIu64 " ", (entry->high - entry->low + 1) * (uint64_t)entry->block_size);
53 11267 : printf("%8u ", entry->fragments);
54 11267 : printf("%s\n", fmt_term(entry->disk, entry->file->sub, esc_buffer));
55 11267 : }
56 :
57 11267 : static void add_size_to_sum(void* arg, void* void_entry)
58 : {
59 11267 : struct snapraid_parity_entry* entry = void_entry;
60 11267 : data_off_t* size_sum = arg;
61 :
62 11267 : *size_sum += entry->file->size;
63 11267 : }
64 :
65 78921 : static void collect_parity_block_file(uint32_t block_size, struct snapraid_disk* disk, struct snapraid_file* file, tommy_list* file_list, block_off_t low_to_free_tail_block)
66 : {
67 78921 : block_off_t parity_low = 0; /* lower block */
68 78921 : block_off_t parity_high = 0; /* higher block */
69 78921 : block_off_t parity_fragments = 0; /* number of fragments */
70 :
71 276775 : for (block_off_t i = 0; i < file->blockmax; ++i) {
72 197854 : block_off_t parity_pos = fs_file2par_find(disk, file, i);
73 :
74 : /* check if a valid position is found */
75 197854 : if (parity_pos == POS_NULL) {
76 : /* block not yet allocated */
77 0 : continue;
78 : }
79 :
80 197854 : if (parity_fragments == 0) {
81 78802 : parity_low = parity_pos;
82 78802 : parity_high = parity_pos;
83 78802 : parity_fragments = 1;
84 : } else {
85 119052 : if (parity_pos != parity_high + 1)
86 241 : ++parity_fragments;
87 119052 : parity_high = parity_pos;
88 : }
89 : }
90 :
91 78921 : if (parity_fragments == 0)
92 119 : return; /* not allocated */
93 :
94 78802 : if (low_to_free_tail_block > 0 && parity_high < low_to_free_tail_block)
95 56277 : return; /* entry not in the range to free */
96 :
97 : /* found a relevant block so add the corresponding file */
98 22525 : struct snapraid_parity_entry* entry = malloc_nofail(sizeof(struct snapraid_parity_entry));
99 22525 : entry->block_size = block_size;
100 22525 : entry->file = file;
101 22525 : entry->disk = disk;
102 22525 : entry->low = parity_low;
103 22525 : entry->high = parity_high;
104 22525 : entry->fragments = parity_fragments;
105 22525 : tommy_list_insert_tail(file_list, &entry->node, entry);
106 : }
107 :
108 5 : void state_locate_info(struct snapraid_state* state, uint64_t parity_tail, struct snapraid_locate_info* info)
109 : {
110 5 : uint32_t block_size = state->block_size;
111 :
112 5 : info->block_max = parity_allocated_size(state);
113 5 : info->parity_size = info->block_max * (uint64_t)block_size;
114 :
115 5 : info->tail_block = (parity_tail + block_size - 1) / block_size;
116 :
117 5 : if (info->tail_block > info->block_max)
118 0 : info->low_to_free_tail_block = 0;
119 : else
120 5 : info->low_to_free_tail_block = info->block_max - info->tail_block;
121 5 : }
122 :
123 5 : void state_locate(struct snapraid_state* state, uint64_t parity_tail)
124 : {
125 : char buf[64];
126 5 : uint32_t block_size = state->block_size;
127 : block_off_t low_to_free_tail_block;
128 :
129 5 : printf("SnapRAID locate report:\n");
130 5 : printf("\n");
131 :
132 5 : if (parity_tail == 0) {
133 1 : printf("Locate all files\n\n");
134 1 : low_to_free_tail_block = 0;
135 : } else {
136 4 : printf("Locate files within the tail of %sB of the parity\n\n", fmt_size(parity_tail, buf, sizeof(buf)));
137 :
138 : struct snapraid_locate_info info;
139 4 : state_locate_info(state, parity_tail, &info);
140 :
141 4 : printf("Current parity size is %sB\n", fmt_size(info.parity_size, buf, sizeof(buf)));
142 4 : if (info.tail_block > info.block_max) {
143 0 : printf("Specified tail greater than the parity size! Operate on the full parity\n");
144 : }
145 :
146 4 : low_to_free_tail_block = info.low_to_free_tail_block;
147 : }
148 :
149 5 : msg_progress("Collecting files with offset greater or equal to %" PRIu64 "\n", low_to_free_tail_block * (uint64_t)block_size);
150 :
151 : tommy_list files;
152 5 : tommy_list_init(&files);
153 35 : for (tommy_node* i = tommy_list_head(&state->disklist); i != 0; i = i->next) {
154 30 : struct snapraid_disk* disk = i->data;
155 56402 : for (tommy_node* j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
156 56372 : struct snapraid_file* file = j->data;
157 56372 : collect_parity_block_file(block_size, disk, file, &files, low_to_free_tail_block);
158 : }
159 : }
160 :
161 5 : printf("\n");
162 :
163 5 : if (tommy_list_count(&files) == 0) {
164 0 : printf("No files located in the specified parity tail.\n");
165 : } else {
166 5 : tommy_list_sort(&files, parity_entry_compare);
167 :
168 5 : data_off_t total_size_located = 0;
169 :
170 5 : tommy_list_foreach_arg(&files, add_size_to_sum, &total_size_located);
171 :
172 5 : printf("Located data in this range: %sB\n", fmt_size(total_size_located, buf, sizeof(buf)));
173 :
174 5 : printf("\n");
175 :
176 : /* |<##################################################################72>|####80>| */
177 5 : printf(" Offset Span Frags\n");
178 :
179 5 : tommy_list_foreach(&files, dump_entry);
180 : }
181 :
182 5 : tommy_list_foreach(&files, free);
183 5 : }
184 :
185 2 : void state_locate_mark_tail_blocks_for_resync(struct snapraid_state* state, uint64_t parity_tail)
186 : {
187 : char buf[64];
188 2 : uint32_t block_size = state->block_size;
189 : block_off_t low_to_free_tail_block;
190 :
191 2 : if (parity_tail == 0) {
192 1 : printf("Forcing reallocation of all files\n\n");
193 1 : low_to_free_tail_block = 0;
194 : } else {
195 1 : msg_progress("Forcing reallocation of all files within the tail of %sB of the parity\n\n", fmt_size(parity_tail, buf, sizeof(buf)));
196 :
197 : struct snapraid_locate_info info;
198 1 : state_locate_info(state, parity_tail, &info);
199 :
200 1 : printf("Current parity size is %sB\n", fmt_size(info.parity_size, buf, sizeof(buf)));
201 1 : if (info.tail_block > info.block_max) {
202 0 : printf("Specified tail greater than the parity size! Operate on the full parity\n");
203 : }
204 :
205 1 : low_to_free_tail_block = info.low_to_free_tail_block;
206 : }
207 :
208 2 : msg_progress("Collecting files with offset greater or equal to %" PRIu64 "\n", low_to_free_tail_block * (uint64_t)block_size);
209 :
210 : tommy_list files;
211 2 : tommy_list_init(&files);
212 14 : for (tommy_node* i = tommy_list_head(&state->disklist); i != 0; i = i->next) {
213 12 : struct snapraid_disk* disk = i->data;
214 22561 : for (tommy_node* j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
215 22549 : struct snapraid_file* file = j->data;
216 22549 : collect_parity_block_file(block_size, disk, file, &files, low_to_free_tail_block);
217 : }
218 : }
219 :
220 2 : printf("\n");
221 :
222 2 : if (tommy_list_count(&files) == 0) {
223 0 : printf("No files located in the specified parity tail.\n");
224 : } else {
225 : /* process all the files partiall or fully overlapping the free zone */
226 11260 : for (tommy_node* j = tommy_list_head(&files); j != 0; j = j->next) {
227 11258 : struct snapraid_parity_entry* entry = j->data;
228 11258 : struct snapraid_file* file = entry->file;
229 11258 : struct snapraid_disk* disk = entry->disk;
230 :
231 : /*
232 : * Reallocate the full file, not only the part of in the free zone
233 : * this is required because the files blocks have to be in order in
234 : * in the parity file.
235 : * not reallocating the file head will prevent the reallocation of the file tail
236 : */
237 40108 : for (block_off_t f = 0; f < file->blockmax; ++f) {
238 28850 : block_off_t parity_pos = fs_file2par_find(disk, file, f);
239 :
240 28850 : if (parity_pos == POS_NULL) {
241 : /* block not yet allocated */
242 0 : continue;
243 : }
244 :
245 28850 : struct snapraid_block* block = fs_file2block_get(file, f);
246 28850 : if (block_state_get(block) == BLOCK_STATE_BLK) {
247 : /* convert from BLK to REP */
248 28850 : block_state_set(block, BLOCK_STATE_REP);
249 : }
250 : }
251 : }
252 : }
253 :
254 2 : tommy_list_foreach(&files, free);
255 2 : }
256 :
|