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