2026-04-02 16:00:27 +02:00
// SPDX-License-Identifier: GPL-3.0-or-later
2026-04-02 16:25:49 +02:00
// Copyright (C) 2016 Andrea Mazzoleni
2015-12-30 11:08:20 +01:00
# include "portable.h"
# include "io.h"
2020-05-18 15:21:42 +02:00
void ( * io_start ) ( struct snapraid_io * io ,
block_off_t blockstart , block_off_t blockmax ,
2021-05-22 11:37:36 +02:00
bit_vect_t * block_enabled ) = 0 ;
2020-05-18 15:21:42 +02:00
void ( * io_stop ) ( struct snapraid_io * io ) = 0 ;
block_off_t ( * io_read_next ) ( struct snapraid_io * io , void * * * buffer ) = 0 ;
struct snapraid_task * ( * io_data_read ) ( struct snapraid_io * io , unsigned * diskcur , unsigned * waiting_map , unsigned * waiting_mac ) = 0 ;
struct snapraid_task * ( * io_parity_read ) ( struct snapraid_io * io , unsigned * levcur , unsigned * waiting_map , unsigned * waiting_mac ) = 0 ;
void ( * io_parity_write ) ( struct snapraid_io * io , unsigned * levcur , unsigned * waiting_map , unsigned * waiting_mac ) = 0 ;
void ( * io_write_preset ) ( struct snapraid_io * io , block_off_t blockcur , int skip ) = 0 ;
void ( * io_write_next ) ( struct snapraid_io * io , block_off_t blockcur , int skip , int * writer_error ) = 0 ;
void ( * io_refresh ) ( struct snapraid_io * io ) = 0 ;
2025-09-25 12:46:15 +02:00
void ( * io_flush ) ( struct snapraid_io * io ) = 0 ;
2020-05-18 15:21:42 +02:00
2015-12-30 11:08:20 +01:00
/**
* Get the next block position to process .
*/
static block_off_t io_position_next ( struct snapraid_io * io )
{
block_off_t blockcur ;
/* get the next position */
2021-05-22 11:37:36 +02:00
if ( io - > block_enabled ) {
while ( io - > block_next < io - > block_max & & ! bit_vect_test ( io - > block_enabled , io - > block_next ) )
+ + io - > block_next ;
}
2015-12-30 11:08:20 +01:00
blockcur = io - > block_next ;
/* next block for the next call */
+ + io - > block_next ;
return blockcur ;
}
/**
2016-01-04 11:24:04 +01:00
* Setup the next pending task for all readers .
2015-12-30 11:08:20 +01:00
*/
2016-01-18 22:58:00 +01:00
static void io_reader_sched ( struct snapraid_io * io , int task_index , block_off_t blockcur )
2015-12-30 11:08:20 +01:00
{
unsigned i ;
2016-01-04 11:24:04 +01:00
for ( i = 0 ; i < io - > reader_max ; + + i ) {
struct snapraid_worker * worker = & io - > reader_map [ i ] ;
2016-01-18 22:58:00 +01:00
struct snapraid_task * task = & worker - > task_map [ task_index ] ;
2015-12-30 11:08:20 +01:00
/* setup the new pending task */
if ( blockcur < io - > block_max )
task - > state = TASK_STATE_READY ;
else
task - > state = TASK_STATE_EMPTY ;
task - > path [ 0 ] = 0 ;
if ( worker - > handle )
task - > disk = worker - > handle - > disk ;
else
task - > disk = 0 ;
2025-10-27 16:22:52 +01:00
assert ( worker - > buffer_skew + i < io - > buffer_max ) ;
2016-01-18 22:58:00 +01:00
task - > buffer = io - > buffer_map [ task_index ] [ worker - > buffer_skew + i ] ;
2015-12-30 11:08:20 +01:00
task - > position = blockcur ;
task - > block = 0 ;
task - > file = 0 ;
task - > file_pos = 0 ;
task - > read_size = 0 ;
task - > is_timestamp_different = 0 ;
}
}
2016-01-04 11:28:34 +01:00
/**
* Setup the next pending task for all writers .
*/
2016-01-18 22:58:00 +01:00
static void io_writer_sched ( struct snapraid_io * io , int task_index , block_off_t blockcur )
2016-01-04 11:28:34 +01:00
{
unsigned i ;
for ( i = 0 ; i < io - > writer_max ; + + i ) {
struct snapraid_worker * worker = & io - > writer_map [ i ] ;
2016-01-18 22:58:00 +01:00
struct snapraid_task * task = & worker - > task_map [ task_index ] ;
2016-01-04 11:28:34 +01:00
/* setup the new pending task */
task - > state = TASK_STATE_READY ;
task - > path [ 0 ] = 0 ;
task - > disk = 0 ;
2016-01-18 22:58:00 +01:00
task - > buffer = io - > buffer_map [ task_index ] [ worker - > buffer_skew + i ] ;
2016-01-04 11:28:34 +01:00
task - > position = blockcur ;
task - > block = 0 ;
task - > file = 0 ;
task - > file_pos = 0 ;
task - > read_size = 0 ;
task - > is_timestamp_different = 0 ;
}
}
/**
* Setup an empty next pending task for all writers .
*/
2016-01-18 22:58:00 +01:00
static void io_writer_sched_empty ( struct snapraid_io * io , int task_index , block_off_t blockcur )
2016-01-04 11:28:34 +01:00
{
unsigned i ;
for ( i = 0 ; i < io - > writer_max ; + + i ) {
struct snapraid_worker * worker = & io - > writer_map [ i ] ;
2016-01-18 22:58:00 +01:00
struct snapraid_task * task = & worker - > task_map [ task_index ] ;
2016-01-04 11:28:34 +01:00
/* setup the new pending task */
task - > state = TASK_STATE_EMPTY ;
task - > path [ 0 ] = 0 ;
task - > disk = 0 ;
task - > buffer = 0 ;
task - > position = blockcur ;
task - > block = 0 ;
task - > file = 0 ;
task - > file_pos = 0 ;
task - > read_size = 0 ;
task - > is_timestamp_different = 0 ;
}
}
2016-03-15 22:40:13 +01:00
/*****************************************************************************/
/* mono thread */
static block_off_t io_read_next_mono ( struct snapraid_io * io , void * * * buffer )
{
block_off_t blockcur_schedule ;
/* reset the index */
io - > reader_index = 0 ;
blockcur_schedule = io_position_next ( io ) ;
/* schedule the next read */
io_reader_sched ( io , 0 , blockcur_schedule ) ;
/* set the buffer to use */
* buffer = io - > buffer_map [ 0 ] ;
return blockcur_schedule ;
}
static void io_write_preset_mono ( struct snapraid_io * io , block_off_t blockcur , int skip )
{
unsigned i ;
/* reset the index */
io - > writer_index = 0 ;
/* clear errors */
for ( i = 0 ; i < IO_WRITER_ERROR_MAX ; + + i )
io - > writer_error [ i ] = 0 ;
if ( skip ) {
/* skip the next write */
io_writer_sched_empty ( io , 0 , blockcur ) ;
} else {
/* schedule the next write */
io_writer_sched ( io , 0 , blockcur ) ;
}
}
static void io_write_next_mono ( struct snapraid_io * io , block_off_t blockcur , int skip , int * writer_error )
{
unsigned i ;
( void ) blockcur ;
( void ) skip ;
/* report errors */
for ( i = 0 ; i < IO_WRITER_ERROR_MAX ; + + i )
writer_error [ i ] = io - > writer_error [ i ] ;
}
static void io_refresh_mono ( struct snapraid_io * io )
{
( void ) io ;
}
2025-09-25 12:46:15 +02:00
static void io_flush_mono ( struct snapraid_io * io )
{
( void ) io ;
}
2016-03-15 22:40:13 +01:00
static struct snapraid_task * io_task_read_mono ( struct snapraid_io * io , unsigned base , unsigned count , unsigned * pos , unsigned * waiting_map , unsigned * waiting_mac )
{
struct snapraid_worker * worker ;
struct snapraid_task * task ;
unsigned i ;
/* get the next task */
i = io - > reader_index + + ;
assert ( base < = i & & i < base + count ) ;
worker = & io - > reader_map [ i ] ;
task = & worker - > task_map [ 0 ] ;
/* do the work */
if ( task - > state ! = TASK_STATE_EMPTY )
worker - > func ( worker , task ) ;
/* return the position */
* pos = i - base ;
/* store the waiting index */
waiting_map [ 0 ] = i - base ;
* waiting_mac = 1 ;
return task ;
}
static struct snapraid_task * io_data_read_mono ( struct snapraid_io * io , unsigned * pos , unsigned * waiting_map , unsigned * waiting_mac )
{
return io_task_read_mono ( io , io - > data_base , io - > data_count , pos , waiting_map , waiting_mac ) ;
}
static struct snapraid_task * io_parity_read_mono ( struct snapraid_io * io , unsigned * pos , unsigned * waiting_map , unsigned * waiting_mac )
{
return io_task_read_mono ( io , io - > parity_base , io - > parity_count , pos , waiting_map , waiting_mac ) ;
}
static void io_parity_write_mono ( struct snapraid_io * io , unsigned * pos , unsigned * waiting_map , unsigned * waiting_mac )
{
struct snapraid_worker * worker ;
struct snapraid_task * task ;
unsigned i ;
/* get the next task */
i = io - > writer_index + + ;
worker = & io - > writer_map [ i ] ;
task = & worker - > task_map [ 0 ] ;
io - > writer_error [ i ] = 0 ;
/* do the work */
if ( task - > state ! = TASK_STATE_EMPTY )
worker - > func ( worker , task ) ;
/* return the position */
* pos = i ;
/* store the waiting index */
waiting_map [ 0 ] = i ;
* waiting_mac = 1 ;
}
static void io_start_mono ( struct snapraid_io * io ,
block_off_t blockstart , block_off_t blockmax ,
2021-05-22 11:37:36 +02:00
bit_vect_t * block_enabled )
2016-03-15 22:40:13 +01:00
{
io - > block_start = blockstart ;
io - > block_max = blockmax ;
2021-05-22 11:37:36 +02:00
io - > block_enabled = block_enabled ;
2016-03-15 22:40:13 +01:00
io - > block_next = blockstart ;
}
static void io_stop_mono ( struct snapraid_io * io )
{
( void ) io ;
}
/*****************************************************************************/
/* multi thread */
2016-03-18 18:45:35 +01:00
/* disable multithread if pthread is not present */
2021-05-25 22:08:12 +02:00
# if HAVE_THREAD
2016-03-18 18:45:35 +01:00
2015-12-30 11:08:20 +01:00
/**
2016-01-04 11:24:04 +01:00
* Get the next task to work on for a reader .
2015-12-30 11:08:20 +01:00
*
* This is the synchronization point for workers with the io .
*/
2016-01-04 11:24:04 +01:00
static struct snapraid_task * io_reader_step ( struct snapraid_worker * worker )
2015-12-30 11:08:20 +01:00
{
struct snapraid_io * io = worker - > io ;
/* the synchronization is protected by the io mutex */
2016-02-13 15:37:28 +01:00
thread_mutex_lock ( & io - > io_mutex ) ;
2015-12-30 11:08:20 +01:00
while ( 1 ) {
2016-01-06 10:10:25 +01:00
unsigned next_index ;
2015-12-30 11:08:20 +01:00
2026-04-27 15:43:06 +02:00
/*
* Check if the worker has to exit
* even if there is work to do
*/
2016-01-06 10:10:25 +01:00
if ( io - > done ) {
2016-02-13 15:37:28 +01:00
thread_mutex_unlock ( & io - > io_mutex ) ;
2016-01-06 10:10:25 +01:00
return 0 ;
}
2015-12-30 11:08:20 +01:00
/* get the next pending task */
2016-01-10 19:39:43 +01:00
next_index = ( worker - > index + 1 ) % io - > io_max ;
2015-12-30 11:08:20 +01:00
/* if the queue of pending tasks is not empty */
2016-01-06 10:10:25 +01:00
if ( next_index ! = io - > reader_index ) {
struct snapraid_task * task ;
/* the index that the IO may be waiting for */
unsigned waiting_index = io - > reader_index ;
/* the index that worker just completed */
unsigned done_index = worker - > index ;
2015-12-30 11:08:20 +01:00
/* get the new working task */
2016-01-06 10:10:25 +01:00
worker - > index = next_index ;
2015-12-30 11:08:20 +01:00
task = & worker - > task_map [ worker - > index ] ;
2016-01-06 10:10:25 +01:00
/* if the just completed task is at this index */
2016-02-06 15:08:11 +01:00
if ( done_index = = waiting_index ) {
2016-01-06 10:10:25 +01:00
/* notify the IO that a new read is complete */
2016-02-13 15:37:28 +01:00
thread_cond_signal_and_unlock ( & io - > read_done , & io - > io_mutex ) ;
2016-02-06 15:08:11 +01:00
} else {
2016-02-13 15:37:28 +01:00
thread_mutex_unlock ( & io - > io_mutex ) ;
2016-02-06 15:08:11 +01:00
}
2016-01-06 10:10:25 +01:00
2016-01-06 10:27:07 +01:00
/* return the new task */
2016-01-06 10:10:25 +01:00
return task ;
2015-12-30 11:08:20 +01:00
}
2016-01-04 11:24:04 +01:00
/* otherwise wait for a read_sched event */
2016-02-13 15:37:28 +01:00
thread_cond_wait ( & io - > read_sched , & io - > io_mutex ) ;
2015-12-30 11:08:20 +01:00
}
}
2016-01-04 11:28:34 +01:00
/**
* Get the next task to work on for a writer .
*
* This is the synchronization point for workers with the io .
*/
2016-01-05 13:20:25 +01:00
static struct snapraid_task * io_writer_step ( struct snapraid_worker * worker , int state )
2016-01-04 11:28:34 +01:00
{
struct snapraid_io * io = worker - > io ;
2016-01-05 13:20:25 +01:00
int error_index ;
2016-01-04 11:28:34 +01:00
/* the synchronization is protected by the io mutex */
2016-02-13 15:37:28 +01:00
thread_mutex_lock ( & io - > io_mutex ) ;
2016-01-04 11:28:34 +01:00
2016-01-05 13:20:25 +01:00
/* counts the number of errors in the global state */
error_index = state - IO_WRITER_ERROR_BASE ;
if ( error_index > = 0 & & error_index < IO_WRITER_ERROR_MAX )
+ + io - > writer_error [ error_index ] ;
2016-01-04 11:28:34 +01:00
while ( 1 ) {
2016-01-06 10:10:25 +01:00
unsigned next_index ;
2016-01-04 11:28:34 +01:00
/* get the next pending task */
2016-01-10 19:39:43 +01:00
next_index = ( worker - > index + 1 ) % io - > io_max ;
2016-01-04 11:28:34 +01:00
/* if the queue of pending tasks is not empty */
2016-01-06 10:10:25 +01:00
if ( next_index ! = io - > writer_index ) {
struct snapraid_task * task ;
2016-03-15 22:40:13 +01:00
2016-01-06 10:10:25 +01:00
/* the index that the IO may be waiting for */
2016-01-10 19:39:43 +01:00
unsigned waiting_index = ( io - > writer_index + 1 ) % io - > io_max ;
2016-01-06 10:10:25 +01:00
/* the index that worker just completed */
unsigned done_index = worker - > index ;
2016-01-04 11:28:34 +01:00
/* get the new working task */
2016-01-06 10:10:25 +01:00
worker - > index = next_index ;
2016-01-04 11:28:34 +01:00
task = & worker - > task_map [ worker - > index ] ;
2016-01-06 10:10:25 +01:00
/* if the just completed task is at this index */
2016-02-06 15:08:11 +01:00
if ( done_index = = waiting_index ) {
2016-01-06 10:10:25 +01:00
/* notify the IO that a new write is complete */
2016-02-13 15:37:28 +01:00
thread_cond_signal_and_unlock ( & io - > write_done , & io - > io_mutex ) ;
2016-02-06 15:08:11 +01:00
} else {
2016-02-13 15:37:28 +01:00
thread_mutex_unlock ( & io - > io_mutex ) ;
2016-02-06 15:08:11 +01:00
}
2016-01-06 10:10:25 +01:00
2016-01-06 10:27:07 +01:00
/* return the new task */
2016-01-06 10:10:25 +01:00
return task ;
2016-01-04 11:28:34 +01:00
}
2026-04-27 15:43:06 +02:00
/*
* Check if the worker has to exit
* but only if there is no work to do
*/
2016-01-06 10:10:25 +01:00
if ( io - > done ) {
2016-02-13 15:37:28 +01:00
thread_mutex_unlock ( & io - > io_mutex ) ;
2016-01-06 10:10:25 +01:00
return 0 ;
}
2016-01-04 11:28:34 +01:00
/* otherwise wait for a write_sched event */
2016-02-13 15:37:28 +01:00
thread_cond_wait ( & io - > write_sched , & io - > io_mutex ) ;
2016-01-04 11:28:34 +01:00
}
}
2015-12-30 11:08:20 +01:00
/**
* Get the next block position to operate on .
*
* This is the synchronization point for workers with the io .
*/
2016-03-15 22:40:13 +01:00
static block_off_t io_read_next_thread ( struct snapraid_io * io , void * * * buffer )
2015-12-30 11:08:20 +01:00
{
block_off_t blockcur_schedule ;
block_off_t blockcur_caller ;
unsigned i ;
2016-01-04 11:24:04 +01:00
/* get the next parity position to process */
2015-12-30 11:08:20 +01:00
blockcur_schedule = io_position_next ( io ) ;
2016-01-04 11:24:04 +01:00
/* ensure that all data/parity was read */
assert ( io - > reader_list [ 0 ] = = io - > reader_max ) ;
2015-12-30 11:08:20 +01:00
/* setup the list of workers to process */
2016-01-04 11:24:04 +01:00
for ( i = 0 ; i < = io - > reader_max ; + + i )
io - > reader_list [ i ] = i ;
2015-12-30 11:08:20 +01:00
/* the synchronization is protected by the io mutex */
2016-02-13 15:37:28 +01:00
thread_mutex_lock ( & io - > io_mutex ) ;
2015-12-30 11:08:20 +01:00
2016-01-04 11:24:04 +01:00
/* schedule the next read */
io_reader_sched ( io , io - > reader_index , blockcur_schedule ) ;
2015-12-30 11:08:20 +01:00
2016-01-05 17:29:26 +01:00
/* set the index for the tasks to return to the caller */
2016-01-10 19:39:43 +01:00
io - > reader_index = ( io - > reader_index + 1 ) % io - > io_max ;
2015-12-30 11:08:20 +01:00
/* get the position to operate at high level from one task */
2016-01-04 11:24:04 +01:00
blockcur_caller = io - > reader_map [ 0 ] . task_map [ io - > reader_index ] . position ;
2015-12-30 11:08:20 +01:00
/* set the buffer to use */
2016-01-04 11:24:04 +01:00
* buffer = io - > buffer_map [ io - > reader_index ] ;
2015-12-30 11:08:20 +01:00
/* signal all the workers that there is a new pending task */
2016-02-13 15:37:28 +01:00
thread_cond_broadcast_and_unlock ( & io - > read_sched , & io - > io_mutex ) ;
2015-12-30 11:08:20 +01:00
return blockcur_caller ;
}
2016-03-15 22:40:13 +01:00
static void io_write_preset_thread ( struct snapraid_io * io , block_off_t blockcur , int skip )
{
( void ) io ;
( void ) blockcur ;
( void ) skip ;
}
static void io_write_next_thread ( struct snapraid_io * io , block_off_t blockcur , int skip , int * writer_error )
2016-01-04 11:28:34 +01:00
{
unsigned i ;
/* ensure that all parity was written */
assert ( io - > writer_list [ 0 ] = = io - > writer_max ) ;
/* setup the list of workers to process */
for ( i = 0 ; i < = io - > writer_max ; + + i )
io - > writer_list [ i ] = i ;
2016-01-05 17:29:26 +01:00
2016-01-04 11:28:34 +01:00
/* the synchronization is protected by the io mutex */
2016-02-13 15:37:28 +01:00
thread_mutex_lock ( & io - > io_mutex ) ;
2016-01-04 11:28:34 +01:00
2016-01-05 13:20:25 +01:00
/* report errors */
for ( i = 0 ; i < IO_WRITER_ERROR_MAX ; + + i ) {
writer_error [ i ] = io - > writer_error [ i ] ;
io - > writer_error [ i ] = 0 ;
}
2016-01-04 11:28:34 +01:00
if ( skip ) {
/* skip the next write */
io_writer_sched_empty ( io , io - > writer_index , blockcur ) ;
} else {
/* schedule the next write */
io_writer_sched ( io , io - > writer_index , blockcur ) ;
}
2016-01-05 17:29:26 +01:00
/* at this point the writers must be in sync with the readers */
2016-01-04 11:28:34 +01:00
assert ( io - > writer_index = = io - > reader_index ) ;
2016-01-05 17:29:26 +01:00
/* set the index to be used for the next write */
2016-01-10 19:39:43 +01:00
io - > writer_index = ( io - > writer_index + 1 ) % io - > io_max ;
2016-01-04 11:28:34 +01:00
/* signal all the workers that there is a new pending task */
2016-02-13 15:37:28 +01:00
thread_cond_broadcast_and_unlock ( & io - > write_sched , & io - > io_mutex ) ;
2016-01-04 11:28:34 +01:00
}
2016-03-15 22:40:13 +01:00
static void io_refresh_thread ( struct snapraid_io * io )
2016-01-27 20:37:31 +01:00
{
unsigned i ;
/* the synchronization is protected by the io mutex */
2016-02-13 15:37:28 +01:00
thread_mutex_lock ( & io - > io_mutex ) ;
2016-01-27 20:37:31 +01:00
/* for all readers, count the number of read blocks */
for ( i = 0 ; i < io - > reader_max ; + + i ) {
unsigned begin , end , cached ;
struct snapraid_worker * worker = & io - > reader_map [ i ] ;
/* the first block read */
begin = io - > reader_index + 1 ;
/* the block in reading */
end = worker - > index ;
if ( begin > end )
end + = io - > io_max ;
cached = end - begin ;
if ( worker - > parity_handle )
2017-11-19 19:33:47 +01:00
io - > state - > parity [ worker - > parity_handle - > level ] . cached_blocks = cached ;
2016-01-27 20:37:31 +01:00
else
2017-11-19 19:33:47 +01:00
worker - > handle - > disk - > cached_blocks = cached ;
2016-01-27 20:37:31 +01:00
}
2026-04-27 15:43:06 +02:00
/*
* For all writers , count the number of written blocks
* note that this is a kind of " opposite " of cached blocks
*/
2016-01-27 20:37:31 +01:00
for ( i = 0 ; i < io - > writer_max ; + + i ) {
unsigned begin , end , cached ;
struct snapraid_worker * worker = & io - > writer_map [ i ] ;
/* the first block written */
begin = io - > writer_index + 1 ;
/* the block in writing */
end = worker - > index ;
if ( begin > end )
end + = io - > io_max ;
cached = end - begin ;
2017-11-19 19:33:47 +01:00
io - > state - > parity [ worker - > parity_handle - > level ] . cached_blocks = cached ;
2016-01-27 20:37:31 +01:00
}
2016-02-13 15:37:28 +01:00
thread_mutex_unlock ( & io - > io_mutex ) ;
2016-01-27 20:37:31 +01:00
}
2025-09-25 12:46:15 +02:00
static void io_flush_thread ( struct snapraid_io * io )
{
unsigned i ;
while ( 1 ) {
int all_done = 1 ;
/* the synchronization is protected by the io mutex */
thread_mutex_lock ( & io - > io_mutex ) ;
for ( i = 0 ; i < io - > writer_max ; + + i ) {
struct snapraid_worker * worker = & io - > writer_map [ i ] ;
/* get the next pending task */
unsigned next_index = ( worker - > index + 1 ) % io - > io_max ;
/* if the queue of pending tasks is not empty */
if ( next_index ! = io - > writer_index ) {
all_done = 0 ;
break ;
}
}
thread_mutex_unlock ( & io - > io_mutex ) ;
if ( all_done )
break ;
/* wait for something to complete */
thread_yield ( ) ;
}
}
2016-03-15 22:40:13 +01:00
static struct snapraid_task * io_task_read_thread ( struct snapraid_io * io , unsigned base , unsigned count , unsigned * pos , unsigned * waiting_map , unsigned * waiting_mac )
2015-12-30 11:08:20 +01:00
{
2016-01-20 21:30:00 +01:00
unsigned waiting_cycle ;
/* count the waiting cycle */
waiting_cycle = 0 ;
/* clear the waiting indexes */
* waiting_mac = 0 ;
2015-12-30 11:08:20 +01:00
/* the synchronization is protected by the io mutex */
2016-02-13 15:37:28 +01:00
thread_mutex_lock ( & io - > io_mutex ) ;
2015-12-30 11:08:20 +01:00
while ( 1 ) {
2026-05-21 15:12:57 +02:00
unsigned * let ;
2016-01-06 10:10:25 +01:00
unsigned busy_index ;
2016-01-04 11:24:04 +01:00
2026-04-27 15:43:06 +02:00
/*
* Get the index the IO is using
* we must ensure that this index has not a read in progress
* to avoid a concurrent access
*/
2016-01-06 10:10:25 +01:00
busy_index = io - > reader_index ;
2015-12-30 11:08:20 +01:00
/* search for a worker that has already finished */
2016-01-04 11:24:04 +01:00
let = & io - > reader_list [ 0 ] ;
2015-12-30 11:08:20 +01:00
while ( 1 ) {
unsigned i = * let ;
/* if we are at the end */
2016-01-04 11:24:04 +01:00
if ( i = = io - > reader_max )
2015-12-30 11:08:20 +01:00
break ;
/* if it's in range */
if ( base < = i & & i < base + count ) {
struct snapraid_worker * worker ;
2016-01-20 21:30:00 +01:00
/* if it's the first cycle */
if ( waiting_cycle = = 0 ) {
/* store the waiting indexes */
waiting_map [ ( * waiting_mac ) + + ] = i - base ;
}
2016-01-04 11:24:04 +01:00
worker = & io - > reader_map [ i ] ;
2015-12-30 11:08:20 +01:00
/* if the worker has finished this index */
2016-01-06 10:10:25 +01:00
if ( busy_index ! = worker - > index ) {
2015-12-30 11:08:20 +01:00
struct snapraid_task * task ;
2016-01-04 11:24:04 +01:00
task = & worker - > task_map [ io - > reader_index ] ;
2015-12-30 11:08:20 +01:00
2026-04-27 15:43:06 +02:00
/*
* Mark the worker as processed
* setting the previous one to point at the next one
*/
2016-01-04 11:24:04 +01:00
* let = io - > reader_list [ i + 1 ] ;
2015-12-30 11:08:20 +01:00
2026-07-06 16:30:03 +02:00
thread_mutex_unlock ( & io - > io_mutex ) ;
2015-12-30 11:08:20 +01:00
/* return the position */
* pos = i - base ;
2016-01-20 21:30:00 +01:00
/* on the first cycle, no one is waiting */
if ( waiting_cycle = = 0 )
* waiting_mac = 0 ;
2015-12-30 11:08:20 +01:00
return task ;
}
}
/* next position to check */
2016-01-04 11:24:04 +01:00
let = & io - > reader_list [ i + 1 ] ;
2015-12-30 11:08:20 +01:00
}
2016-01-20 21:30:00 +01:00
/* if no worker is ready, wait for an event */
2016-02-13 15:37:28 +01:00
thread_cond_wait ( & io - > read_done , & io - > io_mutex ) ;
2016-01-20 21:30:00 +01:00
/* count the cycles */
+ + waiting_cycle ;
2015-12-30 11:08:20 +01:00
}
}
2016-03-15 22:40:13 +01:00
static struct snapraid_task * io_data_read_thread ( struct snapraid_io * io , unsigned * pos , unsigned * waiting_map , unsigned * waiting_mac )
2015-12-30 11:08:20 +01:00
{
2016-03-15 22:40:13 +01:00
return io_task_read_thread ( io , io - > data_base , io - > data_count , pos , waiting_map , waiting_mac ) ;
2015-12-30 11:08:20 +01:00
}
2016-03-15 22:40:13 +01:00
static struct snapraid_task * io_parity_read_thread ( struct snapraid_io * io , unsigned * pos , unsigned * waiting_map , unsigned * waiting_mac )
2015-12-30 11:08:20 +01:00
{
2016-03-15 22:40:13 +01:00
return io_task_read_thread ( io , io - > parity_base , io - > parity_count , pos , waiting_map , waiting_mac ) ;
2015-12-30 11:08:20 +01:00
}
2016-03-15 22:40:13 +01:00
static void io_parity_write_thread ( struct snapraid_io * io , unsigned * pos , unsigned * waiting_map , unsigned * waiting_mac )
2016-01-04 11:28:34 +01:00
{
2016-01-20 21:30:00 +01:00
unsigned waiting_cycle ;
/* count the waiting cycle */
waiting_cycle = 0 ;
/* clear the waiting indexes */
* waiting_mac = 0 ;
2016-01-04 11:28:34 +01:00
/* the synchronization is protected by the io mutex */
2016-02-13 15:37:28 +01:00
thread_mutex_lock ( & io - > io_mutex ) ;
2016-01-04 11:28:34 +01:00
while ( 1 ) {
2026-05-21 15:12:57 +02:00
unsigned * let ;
2016-01-06 10:10:25 +01:00
unsigned busy_index ;
2016-01-04 11:28:34 +01:00
2026-04-27 15:43:06 +02:00
/*
* Get the next index the IO is going to use
* we must ensure that this index has not a write in progress
* to avoid a concurrent access
* note that we are already sure that a write is not in progress
* at the index the IO is using at now
*/
2016-01-10 19:39:43 +01:00
busy_index = ( io - > writer_index + 1 ) % io - > io_max ;
2016-01-04 11:28:34 +01:00
/* search for a worker that has already finished */
let = & io - > writer_list [ 0 ] ;
while ( 1 ) {
unsigned i = * let ;
struct snapraid_worker * worker ;
/* if we are at the end */
if ( i = = io - > writer_max )
break ;
2016-01-20 21:30:00 +01:00
/* if it's the first cycle */
if ( waiting_cycle = = 0 ) {
/* store the waiting indexes */
waiting_map [ ( * waiting_mac ) + + ] = i ;
}
2016-01-04 11:28:34 +01:00
worker = & io - > writer_map [ i ] ;
/* the two indexes cannot be equal */
assert ( io - > writer_index ! = worker - > index ) ;
/* if the worker has finished this index */
2016-01-06 10:10:25 +01:00
if ( busy_index ! = worker - > index ) {
2026-04-27 15:43:06 +02:00
/*
* Mark the worker as processed
* setting the previous one to point at the next one
*/
2016-01-04 11:28:34 +01:00
* let = io - > writer_list [ i + 1 ] ;
2026-07-06 16:30:03 +02:00
thread_mutex_unlock ( & io - > io_mutex ) ;
2016-01-04 11:28:34 +01:00
/* return the position */
* pos = i ;
2016-01-20 21:30:00 +01:00
/* on the first cycle, no one is waiting */
if ( waiting_cycle = = 0 )
* waiting_mac = 0 ;
2016-01-04 11:28:34 +01:00
return ;
}
/* next position to check */
let = & io - > writer_list [ i + 1 ] ;
}
2016-01-20 21:30:00 +01:00
/* if no worker is ready, wait for an event */
2016-02-13 15:37:28 +01:00
thread_cond_wait ( & io - > write_done , & io - > io_mutex ) ;
2016-01-20 21:30:00 +01:00
/* count the cycles */
+ + waiting_cycle ;
2016-01-04 11:28:34 +01:00
}
}
2016-01-04 11:24:04 +01:00
static void io_reader_worker ( struct snapraid_worker * worker , struct snapraid_task * task )
2015-12-30 11:08:20 +01:00
{
/* if we reached the end */
if ( task - > position > = worker - > io - > block_max ) {
2016-01-04 11:24:04 +01:00
/* complete a dummy task */
2015-12-30 11:08:20 +01:00
task - > state = TASK_STATE_EMPTY ;
} else {
worker - > func ( worker , task ) ;
}
}
2016-01-04 11:24:04 +01:00
static void * io_reader_thread ( void * arg )
2015-12-30 11:08:20 +01:00
{
struct snapraid_worker * worker = arg ;
/* force completion of the first task */
2016-01-04 11:24:04 +01:00
io_reader_worker ( worker , & worker - > task_map [ 0 ] ) ;
2015-12-30 11:08:20 +01:00
while ( 1 ) {
struct snapraid_task * task ;
/* get the new task */
2016-01-04 11:24:04 +01:00
task = io_reader_step ( worker ) ;
2015-12-30 11:08:20 +01:00
/* if no task, it means to exit */
if ( ! task )
break ;
/* nothing more to do */
if ( task - > state = = TASK_STATE_EMPTY )
continue ;
2026-06-15 11:16:19 +02:00
if ( task - > state ! = TASK_STATE_READY ) {
/* LCOV_EXCL_START */
log_fatal ( EINTERNAL , " Internal inconsistency: Unexpected state %d in reader for task at position %u in worker %u \n " , task - > state , task - > position , worker - > index ) ;
os_abort ( ) ;
/* LCOV_EXCL_STOP */
}
2015-12-30 11:08:20 +01:00
/* work on the assigned task */
2016-01-04 11:24:04 +01:00
io_reader_worker ( worker , task ) ;
2015-12-30 11:08:20 +01:00
}
return 0 ;
}
2016-01-04 11:28:34 +01:00
static void * io_writer_thread ( void * arg )
{
struct snapraid_worker * worker = arg ;
2016-01-05 13:20:25 +01:00
int latest_state = TASK_STATE_DONE ;
2016-01-04 11:28:34 +01:00
while ( 1 ) {
struct snapraid_task * task ;
/* get the new task */
2016-01-05 13:20:25 +01:00
task = io_writer_step ( worker , latest_state ) ;
2016-01-04 11:28:34 +01:00
/* if no task, it means to exit */
if ( ! task )
break ;
/* nothing more to do */
2016-01-05 13:20:25 +01:00
if ( task - > state = = TASK_STATE_EMPTY ) {
latest_state = TASK_STATE_DONE ;
2016-01-04 11:28:34 +01:00
continue ;
2016-01-05 13:20:25 +01:00
}
2016-01-04 11:28:34 +01:00
2026-06-15 11:16:19 +02:00
if ( task - > state ! = TASK_STATE_READY ) {
/* LCOV_EXCL_START */
log_fatal ( EINTERNAL , " Internal inconsistency: Unexpected state %d in writer for task at position %u in worker %u \n " , task - > state , task - > position , worker - > index ) ;
os_abort ( ) ;
/* LCOV_EXCL_STOP */
}
2016-01-04 11:28:34 +01:00
/* work on the assigned task */
worker - > func ( worker , task ) ;
2016-01-05 13:20:25 +01:00
/* save the resulting state */
latest_state = task - > state ;
2016-01-04 11:28:34 +01:00
}
return 0 ;
}
2016-03-15 22:40:13 +01:00
static void io_start_thread ( struct snapraid_io * io ,
2015-12-30 11:08:20 +01:00
block_off_t blockstart , block_off_t blockmax ,
2021-05-22 11:37:36 +02:00
bit_vect_t * block_enabled )
2015-12-30 11:08:20 +01:00
{
unsigned i ;
io - > block_start = blockstart ;
io - > block_max = blockmax ;
2021-05-22 11:37:36 +02:00
io - > block_enabled = block_enabled ;
2015-12-30 11:08:20 +01:00
io - > block_next = blockstart ;
io - > done = 0 ;
2025-10-27 15:55:13 +01:00
io - > reader_index = io - > io_max - 1 ; /* the first io_read_next() is going to set it to 0 */
2016-01-04 11:28:34 +01:00
io - > writer_index = 0 ;
2015-12-30 11:08:20 +01:00
2016-01-05 13:20:25 +01:00
/* clear writer errors */
for ( i = 0 ; i < IO_WRITER_ERROR_MAX ; + + i )
io - > writer_error [ i ] = 0 ;
2026-04-27 15:43:06 +02:00
/*
* Setup the initial read pending tasks , except the latest one ,
* the latest will be initialized at the fist io_read_next ( ) call
*/
2016-01-10 19:39:43 +01:00
for ( i = 0 ; i < io - > io_max - 1 ; + + i ) {
2015-12-30 11:08:20 +01:00
block_off_t blockcur = io_position_next ( io ) ;
2016-01-04 11:24:04 +01:00
io_reader_sched ( io , i , blockcur ) ;
2015-12-30 11:08:20 +01:00
}
2016-01-04 11:24:04 +01:00
/* setup the lists of workers to process */
io - > reader_list [ 0 ] = io - > reader_max ;
2016-01-04 11:28:34 +01:00
for ( i = 0 ; i < = io - > writer_max ; + + i )
io - > writer_list [ i ] = i ;
2016-01-04 11:24:04 +01:00
/* start the reader threads */
for ( i = 0 ; i < io - > reader_max ; + + i ) {
struct snapraid_worker * worker = & io - > reader_map [ i ] ;
2015-12-30 11:08:20 +01:00
worker - > index = 0 ;
2021-05-25 22:08:12 +02:00
thread_create ( & worker - > thread , io_reader_thread , worker ) ;
2015-12-30 11:08:20 +01:00
}
2016-01-04 11:28:34 +01:00
/* start the writer threads */
for ( i = 0 ; i < io - > writer_max ; + + i ) {
struct snapraid_worker * worker = & io - > writer_map [ i ] ;
2016-01-10 19:39:43 +01:00
worker - > index = io - > io_max - 1 ;
2016-01-04 11:28:34 +01:00
2021-05-25 22:08:12 +02:00
thread_create ( & worker - > thread , io_writer_thread , worker ) ;
2016-01-04 11:28:34 +01:00
}
2015-12-30 11:08:20 +01:00
}
2016-03-15 22:40:13 +01:00
static void io_stop_thread ( struct snapraid_io * io )
2015-12-30 11:08:20 +01:00
{
unsigned i ;
2016-02-13 15:37:28 +01:00
thread_mutex_lock ( & io - > io_mutex ) ;
2015-12-30 11:08:20 +01:00
/* mark that we are stopping */
io - > done = 1 ;
/* signal all the threads to recognize the new state */
2016-02-13 15:35:23 +01:00
thread_cond_broadcast ( & io - > read_sched ) ;
thread_cond_broadcast ( & io - > write_sched ) ;
2015-12-30 11:08:20 +01:00
2016-02-13 15:37:28 +01:00
thread_mutex_unlock ( & io - > io_mutex ) ;
2015-12-30 11:08:20 +01:00
2016-01-04 11:24:04 +01:00
/* wait for all readers to terminate */
for ( i = 0 ; i < io - > reader_max ; + + i ) {
struct snapraid_worker * worker = & io - > reader_map [ i ] ;
2015-12-30 11:08:20 +01:00
void * retval ;
/* wait for thread termination */
2016-02-13 15:35:23 +01:00
thread_join ( worker - > thread , & retval ) ;
2015-12-30 11:08:20 +01:00
}
2016-01-04 11:28:34 +01:00
/* wait for all writers to terminate */
for ( i = 0 ; i < io - > writer_max ; + + i ) {
struct snapraid_worker * worker = & io - > writer_map [ i ] ;
void * retval ;
/* wait for thread termination */
2016-02-13 15:35:23 +01:00
thread_join ( worker - > thread , & retval ) ;
2016-01-04 11:28:34 +01:00
}
2015-12-30 11:08:20 +01:00
}
2016-03-18 18:45:35 +01:00
# endif
2016-03-15 22:40:13 +01:00
/*****************************************************************************/
/* global */
void io_init ( struct snapraid_io * io , struct snapraid_state * state ,
unsigned io_cache , unsigned buffer_max ,
void ( * data_reader ) ( struct snapraid_worker * , struct snapraid_task * ) ,
struct snapraid_handle * handle_map , unsigned handle_max ,
void ( * parity_reader ) ( struct snapraid_worker * , struct snapraid_task * ) ,
void ( * parity_writer ) ( struct snapraid_worker * , struct snapraid_task * ) ,
struct snapraid_parity_handle * parity_handle_map , unsigned parity_handle_max )
{
unsigned i ;
2021-05-30 15:50:13 +02:00
size_t allocated_size ;
size_t block_size = state - > block_size ;
2016-03-15 22:40:13 +01:00
io - > state = state ;
2016-03-18 18:45:35 +01:00
2025-10-27 16:22:52 +01:00
assert ( buffer_max > = handle_max + parity_handle_max ) ;
2025-03-27 12:57:41 -07:00
/* initialize bandwidth limiting */
bw_init ( & io - > bw , state - > opt . bwlimit ) ;
/* set IO context in handles */
for ( i = 0 ; i < handle_max ; + + i )
handle_map [ i ] . bw = & io - > bw ;
for ( i = 0 ; i < parity_handle_max ; + + i )
parity_handle_map [ i ] . bw = & io - > bw ;
2021-05-25 22:08:12 +02:00
# if HAVE_THREAD
2016-03-15 22:40:13 +01:00
if ( io_cache = = 0 ) {
2026-04-27 15:43:06 +02:00
/*
* Default is 16 MiB of cache
* this seems to be a good tradeoff between speed and memory usage
*/
2022-01-09 17:43:18 +01:00
io - > io_max = 16 * 1024 * 1024 / state - > block_size ;
2016-03-15 22:40:13 +01:00
if ( io - > io_max < IO_MIN )
io - > io_max = IO_MIN ;
if ( io - > io_max > IO_MAX )
io - > io_max = IO_MAX ;
} else {
io - > io_max = io_cache ;
}
2016-03-18 18:45:35 +01:00
# else
( void ) io_cache ;
/* without pthread force the mono thread mode */
io - > io_max = 1 ;
# endif
2016-03-15 22:40:13 +01:00
assert ( io - > io_max = = 1 | | ( io - > io_max > = IO_MIN & & io - > io_max < = IO_MAX ) ) ;
io - > buffer_max = buffer_max ;
2021-05-30 15:50:13 +02:00
allocated_size = 0 ;
2016-03-15 22:40:13 +01:00
for ( i = 0 ; i < io - > io_max ; + + i ) {
2016-05-15 14:18:44 +02:00
if ( state - > file_mode ! = ADVISE_DIRECT )
2021-05-30 15:50:13 +02:00
io - > buffer_map [ i ] = malloc_nofail_vector_align ( handle_max , buffer_max , block_size , & io - > buffer_alloc_map [ i ] ) ;
2016-05-15 14:18:44 +02:00
else
2021-05-30 15:50:13 +02:00
io - > buffer_map [ i ] = malloc_nofail_vector_direct ( handle_max , buffer_max , block_size , & io - > buffer_alloc_map [ i ] ) ;
2016-03-15 22:40:13 +01:00
if ( ! state - > opt . skip_self )
mtest_vector ( io - > buffer_max , state - > block_size , io - > buffer_map [ i ] ) ;
2021-05-30 15:50:13 +02:00
allocated_size + = block_size * buffer_max ;
2016-03-15 22:40:13 +01:00
}
2021-05-30 15:50:13 +02:00
msg_progress ( " Using %u MiB of memory for %u cached blocks. \n " , ( unsigned ) ( allocated_size / MEBI ) , io - > io_max ) ;
2016-03-15 22:40:13 +01:00
if ( parity_writer ) {
io - > reader_max = handle_max ;
io - > writer_max = parity_handle_max ;
} else {
io - > reader_max = handle_max + parity_handle_max ;
io - > writer_max = 0 ;
}
io - > reader_map = malloc_nofail ( sizeof ( struct snapraid_worker ) * io - > reader_max ) ;
2026-05-21 15:12:57 +02:00
io - > reader_list = malloc_nofail ( sizeof ( unsigned ) * ( io - > reader_max + 1 ) ) ;
2016-03-15 22:40:13 +01:00
io - > writer_map = malloc_nofail ( sizeof ( struct snapraid_worker ) * io - > writer_max ) ;
2026-05-21 15:12:57 +02:00
io - > writer_list = malloc_nofail ( sizeof ( unsigned ) * ( io - > writer_max + 1 ) ) ;
2016-03-15 22:40:13 +01:00
io - > data_base = 0 ;
io - > data_count = handle_max ;
io - > parity_base = handle_max ;
io - > parity_count = parity_handle_max ;
for ( i = 0 ; i < io - > reader_max ; + + i ) {
struct snapraid_worker * worker = & io - > reader_map [ i ] ;
worker - > io = io ;
if ( i < handle_max ) {
/* it's a data read */
worker - > handle = & handle_map [ i ] ;
worker - > parity_handle = 0 ;
worker - > func = data_reader ;
/* data read is put in lower buffer index */
worker - > buffer_skew = 0 ;
} else {
/* it's a parity read */
worker - > handle = 0 ;
worker - > parity_handle = & parity_handle_map [ i - handle_max ] ;
worker - > func = parity_reader ;
/* parity read is put after data and computed parity */
worker - > buffer_skew = parity_handle_max ;
}
}
for ( i = 0 ; i < io - > writer_max ; + + i ) {
struct snapraid_worker * worker = & io - > writer_map [ i ] ;
worker - > io = io ;
/* it's a parity write */
worker - > handle = 0 ;
worker - > parity_handle = & parity_handle_map [ i ] ;
worker - > func = parity_writer ;
/* parity to write is put after data */
worker - > buffer_skew = handle_max ;
}
2021-05-25 22:08:12 +02:00
# if HAVE_THREAD
2016-03-18 18:45:35 +01:00
if ( io - > io_max > 1 ) {
2016-03-15 22:40:13 +01:00
io_read_next = io_read_next_thread ;
io_write_preset = io_write_preset_thread ;
io_write_next = io_write_next_thread ;
io_refresh = io_refresh_thread ;
2025-09-25 12:46:15 +02:00
io_flush = io_flush_thread ;
2016-03-15 22:40:13 +01:00
io_data_read = io_data_read_thread ;
io_parity_read = io_parity_read_thread ;
io_parity_write = io_parity_write_thread ;
io_start = io_start_thread ;
io_stop = io_stop_thread ;
2021-05-25 22:08:12 +02:00
thread_mutex_init ( & io - > io_mutex ) ;
thread_cond_init ( & io - > read_done ) ;
thread_cond_init ( & io - > read_sched ) ;
thread_cond_init ( & io - > write_done ) ;
thread_cond_init ( & io - > write_sched ) ;
2016-03-18 18:45:35 +01:00
} else
# endif
{
2016-03-15 22:40:13 +01:00
io_read_next = io_read_next_mono ;
io_write_preset = io_write_preset_mono ;
io_write_next = io_write_next_mono ;
io_refresh = io_refresh_mono ;
2025-09-25 12:46:15 +02:00
io_flush = io_flush_mono ;
2016-03-15 22:40:13 +01:00
io_data_read = io_data_read_mono ;
io_parity_read = io_parity_read_mono ;
io_parity_write = io_parity_write_mono ;
io_start = io_start_mono ;
io_stop = io_stop_mono ;
}
}
void io_done ( struct snapraid_io * io )
{
unsigned i ;
for ( i = 0 ; i < io - > io_max ; + + i ) {
free ( io - > buffer_map [ i ] ) ;
free ( io - > buffer_alloc_map [ i ] ) ;
}
free ( io - > reader_map ) ;
free ( io - > reader_list ) ;
free ( io - > writer_map ) ;
free ( io - > writer_list ) ;
2021-05-25 22:08:12 +02:00
# if HAVE_THREAD
2016-03-18 18:45:35 +01:00
if ( io - > io_max > 1 ) {
2016-03-15 22:40:13 +01:00
thread_mutex_destroy ( & io - > io_mutex ) ;
thread_cond_destroy ( & io - > read_done ) ;
thread_cond_destroy ( & io - > read_sched ) ;
thread_cond_destroy ( & io - > write_done ) ;
thread_cond_destroy ( & io - > write_sched ) ;
}
2016-03-18 18:45:35 +01:00
# endif
2016-03-15 22:40:13 +01:00
}