diff --git a/cmdline/io.c b/cmdline/io.c index aae66dc6..4064503f 100644 --- a/cmdline/io.c +++ b/cmdline/io.c @@ -489,7 +489,7 @@ static void io_write_next_outside(struct snapraid_io* io) * * It must be called inside the lock. */ -static void io_write_next_inside(struct snapraid_io* io, unsigned blockcur, int skip, int* writer_error) +static void io_write_next_inside(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error) { unsigned i; @@ -537,6 +537,38 @@ void io_write_next(struct snapraid_io* io, unsigned blockcur, int skip, int* wri #endif } +block_off_t io_write_and_read_next(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error, void*** buffer) +{ + block_off_t blockcur_schedule; + block_off_t blockcur_caller; + + io_write_next_outside(io); + blockcur_schedule = io_read_next_outside(io); + + /* the synchronization is protected by the io mutex */ + pthread_mutex_lock(&io->mutex); + + io_write_next_inside(io, blockcur, skip, writer_error); + + blockcur_caller = io_read_next_inside(io, blockcur_schedule, buffer); + +#ifndef CHECKER + /* without the thread checker unlock before signaling, */ + /* this reduces the number of context switches */ + pthread_mutex_unlock(&io->mutex); +#endif + + /* signal all the workers that there is a new pending task */ + pthread_cond_broadcast(&io->write_sched); + pthread_cond_broadcast(&io->read_sched); + +#ifdef CHECKER + pthread_mutex_unlock(&io->mutex); +#endif + + return blockcur_caller; +} + void io_refresh(struct snapraid_io* io) { unsigned i; diff --git a/cmdline/io.h b/cmdline/io.h index 0aeadfdf..c7938860 100644 --- a/cmdline/io.h +++ b/cmdline/io.h @@ -365,6 +365,14 @@ void io_parity_write(struct snapraid_io* io, unsigned* levcur, unsigned* waiting */ void io_write_next(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error); +/** + * Next write and read position. + * + * It's a combination of io_write_next() and io_read_next() that + * locks only one time the scheduling mutex. + */ +block_off_t io_write_and_read_next(struct snapraid_io* io, block_off_t blockcur, int skip, int* writer_error, void*** buffer); + /** * Refresh the number of cached blocks for all data and parity disks. */ diff --git a/cmdline/sync.c b/cmdline/sync.c index 43f474b4..ce868157 100644 --- a/cmdline/sync.c +++ b/cmdline/sync.c @@ -686,6 +686,8 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari unsigned l; unsigned* waiting_map; unsigned waiting_mac; + block_off_t blocknext; + void** buffernext; /* the sync process assumes that all the hashes are correct */ /* including the ones from CHG and DELETED blocks */ @@ -754,6 +756,10 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari if (!state_progress_begin(state, blockstart, blockmax, countmax)) goto end; + /* scheduling helper */ + buffernext = 0; + blocknext = 0; + while (1) { unsigned failed_count; int error_on_this_block; @@ -768,7 +774,14 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari int writer_error[IO_WRITER_ERROR_MAX]; /* go to the next block */ - blockcur = io_read_next(&io, &buffer); + if (buffernext != 0) { + /* if already computed, use it */ + buffer = buffernext; + blockcur = blocknext; + } else { + /* otherwise do it now */ + blockcur = io_read_next(&io, &buffer); + } if (blockcur >= blockmax) break; @@ -1227,7 +1240,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari } /* write finished */ - io_write_next(&io, blockcur, !parity_going_to_be_updated, writer_error); + blocknext = io_write_and_read_next(&io, blockcur, !parity_going_to_be_updated, writer_error, &buffernext); /* handle errors reported */ for (j = 0; j < IO_WRITER_ERROR_MAX; ++j) {