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