mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Fix thread operations using thread-safe functions
We have a common disk cache, that we cannot share during multithread operations. Intead of using a mutex to access the cache, we use a local thread cache.
This commit is contained in:
parent
fcf7c44197
commit
8ff39bb9bf
3 changed files with 76 additions and 39 deletions
|
|
@ -1029,16 +1029,16 @@ static int chunk_parity_inside_compare(const void* void_a, const void* void_b)
|
|||
* The search is optimized for sequential accesses.
|
||||
* \return If not found return 0
|
||||
*/
|
||||
static struct snapraid_chunk* fs_par2chunk_get(struct snapraid_disk* disk, block_off_t parity_pos)
|
||||
static struct snapraid_chunk* fs_par2chunk_get_ts(struct snapraid_disk* disk, struct snapraid_chunk** fs_last, block_off_t parity_pos)
|
||||
{
|
||||
struct snapraid_chunk* chunk;
|
||||
|
||||
/* check if the last accessed chunk matches */
|
||||
if (disk->fs_last
|
||||
&& parity_pos >= disk->fs_last->parity_pos
|
||||
&& parity_pos < disk->fs_last->parity_pos + disk->fs_last->count
|
||||
if (*fs_last
|
||||
&& parity_pos >= (*fs_last)->parity_pos
|
||||
&& parity_pos < (*fs_last)->parity_pos + (*fs_last)->count
|
||||
) {
|
||||
chunk = disk->fs_last;
|
||||
chunk = *fs_last;
|
||||
} else {
|
||||
struct chunk_parity_inside arg = { parity_pos };
|
||||
chunk = tommy_tree_search_compare(&disk->fs_parity, chunk_parity_inside_compare, &arg);
|
||||
|
|
@ -1048,7 +1048,7 @@ static struct snapraid_chunk* fs_par2chunk_get(struct snapraid_disk* disk, block
|
|||
return 0;
|
||||
|
||||
/* store the last accessed chunk */
|
||||
disk->fs_last = chunk;
|
||||
*fs_last = chunk;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
|
@ -1084,17 +1084,17 @@ static int chunk_file_inside_compare(const void* void_a, const void* void_b)
|
|||
* The search is optimized for sequential accesses.
|
||||
* \return If not found return 0
|
||||
*/
|
||||
static struct snapraid_chunk* fs_file2chunk_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
|
||||
static struct snapraid_chunk* fs_file2chunk_get_ts(struct snapraid_disk* disk, struct snapraid_chunk** fs_last, struct snapraid_file* file, block_off_t file_pos)
|
||||
{
|
||||
struct snapraid_chunk* chunk;
|
||||
|
||||
/* check if the last accessed chunk matches */
|
||||
if (disk->fs_last
|
||||
&& file == disk->fs_last->file
|
||||
&& file_pos >= disk->fs_last->file_pos
|
||||
&& file_pos < disk->fs_last->file_pos + disk->fs_last->count
|
||||
if (*fs_last
|
||||
&& file == (*fs_last)->file
|
||||
&& file_pos >= (*fs_last)->file_pos
|
||||
&& file_pos < (*fs_last)->file_pos + (*fs_last)->count
|
||||
) {
|
||||
chunk = disk->fs_last;
|
||||
chunk = *fs_last;
|
||||
} else {
|
||||
struct chunk_file_inside arg = { file, file_pos };
|
||||
chunk = tommy_tree_search_compare(&disk->fs_file, chunk_file_inside_compare, &arg);
|
||||
|
|
@ -1104,16 +1104,16 @@ static struct snapraid_chunk* fs_file2chunk_get(struct snapraid_disk* disk, stru
|
|||
return 0;
|
||||
|
||||
/* store the last accessed chunk */
|
||||
disk->fs_last = chunk;
|
||||
*fs_last = chunk;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos)
|
||||
struct snapraid_file* fs_par2file_get_ts(struct snapraid_disk* disk, struct snapraid_chunk** fs_last, block_off_t parity_pos, block_off_t* file_pos)
|
||||
{
|
||||
struct snapraid_chunk* chunk;
|
||||
|
||||
chunk = fs_par2chunk_get(disk, parity_pos);
|
||||
chunk = fs_par2chunk_get_ts(disk, fs_last, parity_pos);
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
|
|
@ -1123,11 +1123,11 @@ struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t pa
|
|||
return chunk->file;
|
||||
}
|
||||
|
||||
block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
|
||||
block_off_t fs_file2par_get_ts(struct snapraid_disk* disk, struct snapraid_chunk** fs_last, struct snapraid_file* file, block_off_t file_pos)
|
||||
{
|
||||
struct snapraid_chunk* chunk;
|
||||
|
||||
chunk = fs_file2chunk_get(disk, file, file_pos);
|
||||
chunk = fs_file2chunk_get_ts(disk, fs_last, file, file_pos);
|
||||
if (!chunk) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency when resolving file '%s' at position '%u' in disk '%s'\n", file->sub, file_pos, disk->name);
|
||||
|
|
@ -1146,7 +1146,7 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap
|
|||
|
||||
if (file_pos > 0) {
|
||||
/* search an existing chunk for the previous file_pos */
|
||||
chunk = fs_file2chunk_get(disk, file, file_pos - 1);
|
||||
chunk = fs_file2chunk_get_ts(disk, &disk->fs_last, file, file_pos - 1);
|
||||
|
||||
if (chunk != 0 && parity_pos == chunk->parity_pos + chunk->count) {
|
||||
/* ensure that we are extending the chunk at the end */
|
||||
|
|
@ -1190,7 +1190,7 @@ void fs_deallocate(struct snapraid_disk* disk, block_off_t parity_pos)
|
|||
struct snapraid_chunk* file_chunk;
|
||||
block_off_t first_count, second_count;
|
||||
|
||||
chunk = fs_par2chunk_get(disk, parity_pos);
|
||||
chunk = fs_par2chunk_get_ts(disk, &disk->fs_last, parity_pos);
|
||||
if (!chunk) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Internal inconsistency when deallocating parity position '%u' for not existing chunk in disk '%s'\n", parity_pos, disk->name);
|
||||
|
|
@ -1251,12 +1251,12 @@ void fs_deallocate(struct snapraid_disk* disk, block_off_t parity_pos)
|
|||
disk->fs_last = second_chunk;
|
||||
}
|
||||
|
||||
struct snapraid_block* fs_par2block_get(struct snapraid_disk* disk, block_off_t parity_pos)
|
||||
struct snapraid_block* fs_par2block_get_ts(struct snapraid_disk* disk, struct snapraid_chunk** fs_last, block_off_t parity_pos)
|
||||
{
|
||||
struct snapraid_file* file;
|
||||
block_off_t file_pos;
|
||||
|
||||
file = fs_par2file_get(disk, parity_pos, &file_pos);
|
||||
file = fs_par2file_get_ts(disk, fs_last, parity_pos, &file_pos);
|
||||
|
||||
if (!file)
|
||||
return BLOCK_EMPTY;
|
||||
|
|
|
|||
|
|
@ -321,9 +321,7 @@ struct snapraid_disk {
|
|||
char dir[PATH_MAX]; /**< Mount point of the disk. It always terminates with /. */
|
||||
char smartctl[PATH_MAX]; /**< Custom command for smartctl. Empty means auto. */
|
||||
uint64_t device; /**< Device identifier. */
|
||||
|
||||
uint64_t tick; /**< Usage time of the disk. */
|
||||
|
||||
block_off_t total_blocks; /**< Number of total blocks. */
|
||||
block_off_t free_blocks; /**< Number of free blocks at the last sync. */
|
||||
|
||||
|
|
@ -899,6 +897,8 @@ int fs_check(struct snapraid_disk* disk);
|
|||
*
|
||||
* After this call you can use the par2file/par2block operations
|
||||
* to query the relation.
|
||||
*
|
||||
* \note This function is NOT thread-safe as it uses the the disk cache. +
|
||||
*/
|
||||
void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos);
|
||||
|
||||
|
|
@ -907,6 +907,8 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap
|
|||
*
|
||||
* After this call the par2file/par2block operations
|
||||
* won't find anymore the parity association.
|
||||
*
|
||||
* \note This function is NOT thread-safe as it uses the the disk cache.
|
||||
*/
|
||||
void fs_deallocate(struct snapraid_disk* disk, block_off_t pos);
|
||||
|
||||
|
|
@ -920,22 +922,49 @@ static inline struct snapraid_block* fs_file2block_get(struct snapraid_file* fil
|
|||
return &file->blockvec[file_pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* Like fs_file2par_get() but thread-safe.
|
||||
*/
|
||||
struct snapraid_file* fs_par2file_get_ts(struct snapraid_disk* disk, struct snapraid_chunk** fs_last, block_off_t parity_pos, block_off_t* file_pos);
|
||||
|
||||
/**
|
||||
* Get the file position from the parity position.
|
||||
* Return 0 if no file is using it.
|
||||
* \note This function is NOT thread-safe as it uses the the disk cache.
|
||||
*/
|
||||
struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos);
|
||||
static inline struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos)
|
||||
{
|
||||
return fs_par2file_get_ts(disk, &disk->fs_last, parity_pos, file_pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like fs_file2par_get() but thread-safe.
|
||||
*/
|
||||
block_off_t fs_file2par_get_ts(struct snapraid_disk* disk, struct snapraid_chunk** fs_last, struct snapraid_file* file, block_off_t file_pos);
|
||||
|
||||
/**
|
||||
* Get the parity position from the file position.
|
||||
* \note This function is NOT thread-safe as it uses the the disk cache.
|
||||
*/
|
||||
block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos);
|
||||
static inline block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
|
||||
{
|
||||
return fs_file2par_get_ts(disk, &disk->fs_last, file, file_pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like fs_par2block_get() but thread-safe.
|
||||
*/
|
||||
struct snapraid_block* fs_par2block_get_ts(struct snapraid_disk* disk, struct snapraid_chunk** fs_last, block_off_t parity_pos);
|
||||
|
||||
/**
|
||||
* Get the block from the parity position.
|
||||
* Return BLOCK_EMPTY==0 if the block is over the end of the disk or not used.
|
||||
* \note This function is NOT thread-safe as it uses the the disk cache.
|
||||
*/
|
||||
struct snapraid_block* fs_par2block_get(struct snapraid_disk* disk, block_off_t parity_pos);
|
||||
static inline struct snapraid_block* fs_par2block_get(struct snapraid_disk* disk, block_off_t parity_pos)
|
||||
{
|
||||
return fs_par2block_get_ts(disk, &disk->fs_last, parity_pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a disk mapping.
|
||||
|
|
|
|||
|
|
@ -1464,7 +1464,7 @@ static void state_content_check(struct snapraid_state* state, const char* path)
|
|||
*
|
||||
* Note that position with only DELETED blocks are discarged.
|
||||
*/
|
||||
static int position_is_required(struct snapraid_state* state, block_off_t pos)
|
||||
static int fs_position_is_required(struct snapraid_state* state, block_off_t pos)
|
||||
{
|
||||
tommy_node* i;
|
||||
|
||||
|
|
@ -1493,7 +1493,7 @@ static int position_is_required(struct snapraid_state* state, block_off_t pos)
|
|||
* Note also that not requiring an info block, doesn't mean that if present it
|
||||
* can be discarded.
|
||||
*/
|
||||
static int info_is_required(struct snapraid_state* state, block_off_t pos)
|
||||
static int fs_info_is_required(struct snapraid_state* state, block_off_t pos)
|
||||
{
|
||||
tommy_node* i;
|
||||
|
||||
|
|
@ -1510,7 +1510,7 @@ static int info_is_required(struct snapraid_state* state, block_off_t pos)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void position_clear_deleted(struct snapraid_state* state, block_off_t pos)
|
||||
static void fs_position_clear_deleted(struct snapraid_state* state, block_off_t pos)
|
||||
{
|
||||
tommy_node* i;
|
||||
|
||||
|
|
@ -1530,9 +1530,9 @@ static void position_clear_deleted(struct snapraid_state* state, block_off_t pos
|
|||
/**
|
||||
* Check if a block position in a disk is deleted.
|
||||
*/
|
||||
static int is_block_deleted(struct snapraid_disk* disk, block_off_t pos)
|
||||
static int fs_is_block_deleted_ts(struct snapraid_disk* disk, struct snapraid_chunk** fs_last, block_off_t pos)
|
||||
{
|
||||
struct snapraid_block* block = fs_par2block_get(disk, pos);
|
||||
struct snapraid_block* block = fs_par2block_get_ts(disk, fs_last, pos);
|
||||
|
||||
return block_state_get(block) == BLOCK_STATE_DELETED;
|
||||
}
|
||||
|
|
@ -1948,7 +1948,7 @@ static void state_read_content(struct snapraid_state* state, const char* path, S
|
|||
info_set(&state->infoarr, v_pos, info);
|
||||
|
||||
/* ensure that an info is present only for used positions */
|
||||
if (info_is_required(state, v_pos)) {
|
||||
if (fs_info_is_required(state, v_pos)) {
|
||||
if (!info) {
|
||||
/* LCOV_EXCL_START */
|
||||
decoding_error(path, f);
|
||||
|
|
@ -2653,6 +2653,14 @@ static void* state_write_thread(void* arg)
|
|||
for (i = state->disklist; i != 0; i = i->next) {
|
||||
tommy_node* j;
|
||||
struct snapraid_disk* disk = i->data;
|
||||
struct snapraid_chunk* fs_last;
|
||||
|
||||
/**
|
||||
* This is the last accessed chunk in the tree operations.
|
||||
* Here we are inside a thread, and we cannot use the
|
||||
* ::disk member to store this information.
|
||||
*/
|
||||
fs_last = 0;
|
||||
|
||||
/* if the disk is not mapped, skip it */
|
||||
if (disk->mapping_idx < 0)
|
||||
|
|
@ -2693,7 +2701,7 @@ static void* state_write_thread(void* arg)
|
|||
begin = 0;
|
||||
while (begin < file->blockmax) {
|
||||
unsigned v_state = block_state_get(fs_file2block_get(file, begin));
|
||||
block_off_t v_pos = fs_file2par_get(disk, file, begin);
|
||||
block_off_t v_pos = fs_file2par_get_ts(disk, &fs_last, file, begin);
|
||||
uint32_t v_count;
|
||||
|
||||
block_off_t end;
|
||||
|
|
@ -2703,7 +2711,7 @@ static void* state_write_thread(void* arg)
|
|||
while (end < file->blockmax) {
|
||||
if (v_state != block_state_get(fs_file2block_get(file, end)))
|
||||
break;
|
||||
if (v_pos + (end - begin) != fs_file2par_get(disk, file, end))
|
||||
if (v_pos + (end - begin) != fs_file2par_get_ts(disk, &fs_last, file, end))
|
||||
break;
|
||||
++end;
|
||||
}
|
||||
|
|
@ -2808,12 +2816,12 @@ static void* state_write_thread(void* arg)
|
|||
int is_deleted;
|
||||
block_off_t end;
|
||||
|
||||
is_deleted = is_block_deleted(disk, begin);
|
||||
is_deleted = fs_is_block_deleted_ts(disk, &fs_last, begin);
|
||||
|
||||
/* find the end of run of blocks */
|
||||
end = begin + 1;
|
||||
while (end < blockmax
|
||||
&& is_deleted == is_block_deleted(disk, end)
|
||||
&& is_deleted == fs_is_block_deleted_ts(disk, &fs_last, end)
|
||||
) {
|
||||
++end;
|
||||
}
|
||||
|
|
@ -2826,7 +2834,7 @@ static void* state_write_thread(void* arg)
|
|||
|
||||
/* write all the hash */
|
||||
while (begin < end) {
|
||||
struct snapraid_block* block = fs_par2block_get(disk, begin);
|
||||
struct snapraid_block* block = fs_par2block_get_ts(disk, &fs_last, begin);
|
||||
|
||||
swrite(block->hash, HASH_SIZE, f);
|
||||
|
||||
|
|
@ -2997,7 +3005,7 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
info_has_rehash = 0; /* if there is a rehash info */
|
||||
for (idx = 0; idx < blockmax; ++idx) {
|
||||
/* if the position is used */
|
||||
if (position_is_required(state, idx)) {
|
||||
if (fs_position_is_required(state, idx)) {
|
||||
snapraid_info info = info_get(&state->infoarr, idx);
|
||||
|
||||
/* only if there is some info to store */
|
||||
|
|
@ -3015,7 +3023,7 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
info_set(&state->infoarr, idx, 0);
|
||||
|
||||
/* and clear any deleted blocks */
|
||||
position_clear_deleted(state, idx);
|
||||
fs_position_clear_deleted(state, idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue