Line data Source code
1 : // SPDX-License-Identifier: GPL-3.0-or-later
2 : // Copyright (C) 2011 Andrea Mazzoleni
3 :
4 : #include "portable.h"
5 :
6 : #include "util.h"
7 : #include "elem.h"
8 : #include "state.h"
9 : #include "parity.h"
10 : #include "handle.h"
11 : #include "io.h"
12 : #include "raid/raid.h"
13 :
14 : /****************************************************************************/
15 : /* dry */
16 :
17 0 : static const char* es(int err)
18 : {
19 0 : if (is_hw(err))
20 0 : return "error_io";
21 : else
22 0 : return "error";
23 : }
24 :
25 57102 : static void dry_data_reader(struct snapraid_worker* worker, struct snapraid_task* task)
26 : {
27 57102 : struct snapraid_io* io = worker->io;
28 57102 : struct snapraid_state* state = io->state;
29 57102 : struct snapraid_handle* handle = worker->handle;
30 57102 : struct snapraid_disk* disk = handle->disk;
31 57102 : block_off_t blockcur = task->position;
32 57102 : unsigned char* buffer = task->buffer;
33 : int ret;
34 : char esc_buffer[ESC_MAX];
35 :
36 : /* if the disk position is not used */
37 57102 : if (!disk) {
38 : /* use an empty block */
39 4687 : memset(buffer, 0, state->block_size);
40 4687 : task->state = TASK_STATE_DONE;
41 5528 : return;
42 : }
43 :
44 : /* get the block */
45 52415 : task->block = fs_par2block_find(disk, blockcur);
46 :
47 : /* if the block is not used */
48 52415 : if (!block_has_file(task->block)) {
49 : /* use an empty block */
50 841 : memset(buffer, 0, state->block_size);
51 841 : task->state = TASK_STATE_DONE;
52 841 : return;
53 : }
54 :
55 : /* get the file of this block */
56 51574 : task->file = fs_par2file_get(disk, blockcur, &task->file_pos);
57 :
58 : /* if the file is different than the current one, close it */
59 51574 : if (handle->file != 0 && handle->file != task->file) {
60 : /* keep a pointer at the file we are going to close for error reporting */
61 20803 : struct snapraid_file* report = handle->file;
62 20803 : ret = handle_close(handle);
63 20803 : if (ret == -1) {
64 : /* LCOV_EXCL_START */
65 : /*
66 : * This one is really an unexpected error, because we are only reading
67 : * and closing a descriptor should never fail
68 : */
69 : log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
70 : log_fatal_errno(errno, disk->name);
71 : log_fatal(errno, "Stopping at block %u\n", blockcur);
72 :
73 : if (is_hw(errno)) {
74 : task->state = TASK_STATE_IOERROR;
75 : } else {
76 : task->state = TASK_STATE_ERROR;
77 : }
78 : return;
79 : /* LCOV_EXCL_STOP */
80 : }
81 : }
82 :
83 51574 : ret = handle_open(handle, task->file, state->file_mode, log_error, 0); /* for missing file don't output a message */
84 51574 : if (ret == -1) {
85 0 : log_tag("%s:%u:%s:%s: Open error. %s.\n", es(errno), blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
86 0 : if (is_hw(errno)) {
87 : /* LCOV_EXCL_START */
88 : log_fatal_errno(errno, disk->name);
89 : log_fatal(errno, "Stopping at block %u\n", blockcur);
90 : task->state = TASK_STATE_IOERROR;
91 : return;
92 : /* LCOV_EXCL_STOP */
93 : }
94 :
95 0 : task->state = TASK_STATE_ERROR_CONTINUE;
96 0 : return;
97 : }
98 :
99 51574 : task->read_size = handle_read(handle, task->file_pos, buffer, state->block_size, log_error, 0);
100 51574 : if (task->read_size == -1) {
101 0 : log_tag("%s:%u:%s:%s: Read error at position %u. %s.\n", es(errno), blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
102 0 : if (is_hw(errno)) {
103 : /* LCOV_EXCL_START */
104 : log_error_errno(errno, disk->name);
105 : task->state = TASK_STATE_IOERROR_CONTINUE;
106 : return;
107 : /* LCOV_EXCL_STOP */
108 : }
109 :
110 0 : task->state = TASK_STATE_ERROR_CONTINUE;
111 0 : return;
112 : }
113 :
114 : /* store the path of the opened file */
115 51574 : pathcpy(task->path, sizeof(task->path), handle->path);
116 :
117 51574 : task->state = TASK_STATE_DONE;
118 : }
119 :
120 57102 : static void dry_parity_reader(struct snapraid_worker* worker, struct snapraid_task* task)
121 : {
122 57102 : struct snapraid_io* io = worker->io;
123 57102 : struct snapraid_state* state = io->state;
124 57102 : struct snapraid_parity_handle* parity_handle = worker->parity_handle;
125 57102 : unsigned level = parity_handle->level;
126 57102 : block_off_t blockcur = task->position;
127 57102 : unsigned char* buffer = task->buffer;
128 : int ret;
129 :
130 : /* read the parity */
131 57102 : ret = parity_read(parity_handle, blockcur, buffer, state->block_size, log_error);
132 57102 : if (ret == -1) {
133 0 : log_tag("parity_%s:%u:%s: Read error. %s.\n", es(errno), blockcur, lev_config_name(level), strerror(errno));
134 0 : if (is_hw(errno)) {
135 : /* LCOV_EXCL_START */
136 : log_error_errno(errno, lev_config_name(level));
137 : task->state = TASK_STATE_IOERROR_CONTINUE;
138 : return;
139 : /* LCOV_EXCL_STOP */
140 : }
141 :
142 0 : task->state = TASK_STATE_ERROR_CONTINUE;
143 0 : return;
144 : }
145 :
146 57102 : task->state = TASK_STATE_DONE;
147 : }
148 :
149 3 : static int state_dry_process(struct snapraid_state* state, struct snapraid_parity_handle* parity_handle, block_off_t blockstart, block_off_t blockmax)
150 : {
151 : struct snapraid_io io;
152 : struct snapraid_handle* handle;
153 : unsigned diskmax;
154 : block_off_t blockcur;
155 : unsigned j;
156 : unsigned buffermax;
157 : int ret;
158 : data_off_t countsize;
159 : block_off_t countpos;
160 : block_off_t countmax;
161 : unsigned soft_error;
162 : unsigned io_error;
163 : unsigned l;
164 : unsigned* waiting_map;
165 : unsigned waiting_mac;
166 : char esc_buffer[ESC_MAX];
167 :
168 3 : handle = handle_mapping(state, &diskmax);
169 :
170 : /* we need 1 * data + 2 * parity */
171 3 : buffermax = diskmax + 2 * state->level;
172 :
173 : /* initialize the io threads */
174 3 : io_init(&io, state, state->opt.io_cache, buffermax, dry_data_reader, handle, diskmax, dry_parity_reader, 0, parity_handle, state->level);
175 :
176 : /* possibly waiting disks */
177 3 : waiting_mac = diskmax > RAID_PARITY_MAX ? diskmax : RAID_PARITY_MAX;
178 3 : waiting_map = malloc_nofail(waiting_mac * sizeof(unsigned));
179 :
180 3 : soft_error = 0;
181 3 : io_error = 0;
182 :
183 : /* drop until now */
184 3 : state_usage_waste(state);
185 :
186 3 : countmax = blockmax - blockstart;
187 3 : countsize = 0;
188 3 : countpos = 0;
189 :
190 : /* start all the worker threads */
191 3 : io_start(&io, blockstart, blockmax, 0);
192 :
193 3 : blockcur = blockstart;
194 :
195 3 : int alert = state_progress_begin(state, blockstart, blockmax, countmax);
196 3 : if (alert > 0)
197 0 : goto end;
198 3 : if (alert < 0)
199 0 : goto bail;
200 :
201 9517 : while (1) {
202 : void** buffer;
203 :
204 : /* go to the next block */
205 9520 : blockcur = io_read_next(&io, &buffer);
206 9520 : if (blockcur >= blockmax)
207 3 : break;
208 :
209 : /* until now is scheduling */
210 9517 : state_usage_sched(state);
211 :
212 : /* for each disk, process the block */
213 66619 : for (j = 0; j < diskmax; ++j) {
214 : struct snapraid_task* task;
215 : int read_size;
216 : struct snapraid_block* block;
217 : struct snapraid_disk* disk;
218 : unsigned diskcur;
219 :
220 : /* until now is misc */
221 57102 : state_usage_misc(state);
222 :
223 : /* get the next task */
224 57102 : task = io_data_read(&io, &diskcur, waiting_map, &waiting_mac);
225 :
226 : /* until now is disk */
227 57102 : state_usage_disk(state, handle, waiting_map, waiting_mac);
228 :
229 : /* get the task results */
230 57102 : disk = task->disk;
231 57102 : block = task->block;
232 57102 : read_size = task->read_size;
233 :
234 : /* if the disk position is not used */
235 57102 : if (!disk)
236 5528 : continue;
237 :
238 52415 : state_usage_file(state, disk, task->file);
239 :
240 : /* if the block is not used */
241 52415 : if (!block_has_file(block))
242 841 : continue;
243 :
244 : /* handle error conditions */
245 51574 : if (task->state == TASK_STATE_IOERROR) {
246 : /* LCOV_EXCL_START */
247 : ++io_error;
248 : goto bail;
249 : /* LCOV_EXCL_STOP */
250 : }
251 51574 : if (task->state == TASK_STATE_ERROR) {
252 : /* LCOV_EXCL_START */
253 : ++soft_error;
254 : goto bail;
255 : /* LCOV_EXCL_STOP */
256 : }
257 51574 : if (task->state == TASK_STATE_ERROR_CONTINUE) {
258 0 : ++soft_error;
259 0 : continue;
260 : }
261 51574 : if (task->state == TASK_STATE_IOERROR_CONTINUE) {
262 0 : ++io_error;
263 0 : if (io_error >= state->opt.io_error_limit) {
264 : /* LCOV_EXCL_START */
265 : log_fatal(EIO, "DANGER! Too many input/output errors in the %s disk. It isn't possible to continue.\n", disk->dir);
266 : log_fatal(EIO, "Stopping at block %u\n", blockcur);
267 : goto bail;
268 : /* LCOV_EXCL_STOP */
269 : }
270 :
271 : /* otherwise continue */
272 0 : continue;
273 : }
274 51574 : if (task->state != TASK_STATE_DONE) {
275 : /* LCOV_EXCL_START */
276 : log_fatal(EINTERNAL, "Internal inconsistency in task state\n");
277 : os_abort();
278 : /* LCOV_EXCL_STOP */
279 : }
280 :
281 51574 : countsize += read_size;
282 : }
283 :
284 : /* until now is misc */
285 9517 : state_usage_misc(state);
286 :
287 : /* read the parity */
288 66619 : for (l = 0; l < state->level; ++l) {
289 : struct snapraid_task* task;
290 : unsigned levcur;
291 :
292 57102 : task = io_parity_read(&io, &levcur, waiting_map, &waiting_mac);
293 :
294 : /* until now is parity */
295 57102 : state_usage_parity(state, waiting_map, waiting_mac);
296 :
297 : /* handle error conditions */
298 57102 : if (task->state == TASK_STATE_IOERROR) {
299 : /* LCOV_EXCL_START */
300 : ++io_error;
301 : goto bail;
302 : /* LCOV_EXCL_STOP */
303 : }
304 57102 : if (task->state == TASK_STATE_ERROR) {
305 : /* LCOV_EXCL_START */
306 : ++soft_error;
307 : goto bail;
308 : /* LCOV_EXCL_STOP */
309 : }
310 57102 : if (task->state == TASK_STATE_ERROR_CONTINUE) {
311 0 : ++soft_error;
312 0 : continue;
313 : }
314 57102 : if (task->state == TASK_STATE_IOERROR_CONTINUE) {
315 0 : ++io_error;
316 0 : if (io_error >= state->opt.io_error_limit) {
317 : /* LCOV_EXCL_START */
318 : log_fatal(EIO, "DANGER! Too many input/output errors in the %s disk. It isn't possible to continue.\n", lev_name(levcur));
319 : log_fatal(EIO, "Stopping at block %u\n", blockcur);
320 : goto bail;
321 : /* LCOV_EXCL_STOP */
322 : }
323 0 : continue;
324 : }
325 57102 : if (task->state != TASK_STATE_DONE) {
326 : /* LCOV_EXCL_START */
327 : log_fatal(EINTERNAL, "Internal inconsistency in task state\n");
328 : os_abort();
329 : /* LCOV_EXCL_STOP */
330 : }
331 : }
332 :
333 : /* count the number of processed block */
334 9517 : ++countpos;
335 :
336 : /* progress */
337 9517 : if (state_progress(state, &io, blockcur, countpos, countmax, countsize)) {
338 : /* LCOV_EXCL_START */
339 : break;
340 : /* LCOV_EXCL_STOP */
341 : }
342 :
343 : /* thermal control */
344 9517 : if (state_thermal_alarm(state)) {
345 : /* until now is misc */
346 0 : state_usage_misc(state);
347 :
348 0 : state_progress_stop(state);
349 :
350 0 : state_thermal_cooldown(state);
351 :
352 0 : state_progress_restart(state);
353 :
354 : /* drop until now */
355 0 : state_usage_waste(state);
356 : }
357 : }
358 :
359 3 : end:
360 3 : state_progress_end(state, countpos, countmax, countsize, "Nothing to dry.\n");
361 :
362 3 : state_usage_print(state);
363 :
364 3 : bail:
365 : /* stop all the worker threads */
366 3 : io_stop(&io);
367 :
368 21 : for (j = 0; j < diskmax; ++j) {
369 18 : struct snapraid_file* file = handle[j].file;
370 18 : struct snapraid_disk* disk = handle[j].disk;
371 18 : ret = handle_close(&handle[j]);
372 18 : if (ret == -1) {
373 : /* LCOV_EXCL_START */
374 : log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
375 : log_fatal_errno(errno, disk->name);
376 :
377 : if (is_hw(errno)) {
378 : ++io_error;
379 : } else {
380 : ++soft_error;
381 : }
382 : /* continue, as we are already exiting */
383 : /* LCOV_EXCL_STOP */
384 : }
385 : }
386 :
387 3 : if (soft_error || io_error) {
388 0 : msg_status("\n");
389 0 : msg_status("%8u soft errors\n", soft_error);
390 0 : msg_status("%8u io errors\n", io_error);
391 : } else {
392 3 : msg_status("Everything OK\n");
393 : }
394 :
395 3 : if (soft_error)
396 0 : log_fatal(ESOFT, "DANGER! Unexpected errors!\n");
397 3 : if (io_error)
398 0 : log_fatal(EIO, "DANGER! Unexpected input/output errors!\n");
399 :
400 3 : log_tag("summary:error_soft:%u\n", soft_error);
401 3 : log_tag("summary:error_io:%u\n", io_error);
402 3 : if (soft_error + io_error == 0)
403 3 : log_tag("summary:exit:ok\n");
404 : else
405 0 : log_tag("summary:exit:error\n");
406 3 : log_flush();
407 :
408 3 : free(handle);
409 3 : free(waiting_map);
410 3 : io_done(&io);
411 :
412 3 : if (soft_error + io_error != 0)
413 0 : return -1;
414 :
415 3 : if (alert < 0)
416 0 : return -1;
417 :
418 3 : return 0;
419 : }
420 :
421 3 : int state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount)
422 : {
423 : block_off_t blockmax;
424 : int ret;
425 : struct snapraid_parity_handle parity_handle[LEV_MAX];
426 : unsigned process_error;
427 : unsigned l;
428 :
429 3 : msg_progress("Drying...\n");
430 :
431 3 : blockmax = parity_allocated_size(state);
432 :
433 3 : if (blockstart > blockmax) {
434 : /* LCOV_EXCL_START */
435 : log_fatal(EUSER, "Error in the specified starting block %u. It's larger than the parity size %u.\n", blockstart, blockmax);
436 : exit(EXIT_FAILURE);
437 : /* LCOV_EXCL_STOP */
438 : }
439 :
440 : /* adjust the number of block to process */
441 3 : if (blockcount != 0 && blockstart + blockcount < blockmax) {
442 0 : blockmax = blockstart + blockcount;
443 : }
444 :
445 : /*
446 : * Open the file for reading
447 : * it may fail if the file doesn't exist, in this case we continue to dry the files
448 : */
449 21 : for (l = 0; l < state->level; ++l) {
450 18 : ret = parity_open(&parity_handle[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
451 18 : if (ret == -1) {
452 : /* LCOV_EXCL_START */
453 : log_tag("parity_%s:%u:%s: Open error. %s.\n", es(errno), blockmax, lev_config_name(l), strerror(errno));
454 : log_fatal_errno(errno, lev_config_name(l));
455 : exit(EXIT_FAILURE);
456 : /* LCOV_EXCL_STOP */
457 : }
458 : }
459 :
460 3 : process_error = 0;
461 :
462 : /* skip degenerated cases of empty parity, or skipping all */
463 3 : if (blockstart < blockmax) {
464 3 : ret = state_dry_process(state, parity_handle, blockstart, blockmax);
465 3 : if (ret == -1) {
466 : /* LCOV_EXCL_START */
467 : ++process_error;
468 : /* continue, as we are already exiting */
469 : /* LCOV_EXCL_STOP */
470 : }
471 : }
472 :
473 : /* try to close only if opened */
474 21 : for (l = 0; l < state->level; ++l) {
475 18 : ret = parity_close(&parity_handle[l]);
476 18 : if (ret == -1) {
477 : /* LCOV_EXCL_START */
478 : log_tag("parity_%s:%u:%s: Close error. %s.\n", es(errno), blockmax, lev_config_name(l), strerror(errno));
479 : log_fatal_errno(errno, lev_config_name(l));
480 :
481 : ++process_error;
482 : /* continue, as we are already exiting */
483 : /* LCOV_EXCL_STOP */
484 : }
485 : }
486 :
487 3 : if (process_error != 0)
488 0 : return -1;
489 3 : return 0;
490 : }
491 :
|