LCOV - code coverage report
Current view: top level - cmdline - dry.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 120 157 76.4 %
Date: 2017-11-06 22:14:04 Functions: 5 5 100.0 %

          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             : 

Generated by: LCOV version 1.13