LCOV - code coverage report
Current view: top level - cmdline - dry.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 131 174 75.3 %
Date: 2026-04-29 15:04:44 Functions: 4 5 80.0 %

          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             : 

Generated by: LCOV version 1.0