Protect rate limiter state updates with a mutex to prevent data races

This commit is contained in:
Andrea Mazzoleni 2026-08-12 00:22:27 +02:00
parent b4a694bb10
commit eebb3943e5
4 changed files with 34 additions and 1 deletions

View file

@ -10,6 +10,18 @@ void bw_init(struct snapraid_bw* bw, uint64_t limit)
bw->limit = limit;
bw->total = 0;
bw->start = os_tick_ms();
#if HAVE_THREAD
thread_mutex_init(&bw->lock);
#endif
}
void bw_done(struct snapraid_bw* bw)
{
#if HAVE_THREAD
thread_mutex_destroy(&bw->lock);
#else
(void)bw;
#endif
}
void bw_limit(struct snapraid_bw* bw, uint64_t bytes)
@ -17,13 +29,18 @@ void bw_limit(struct snapraid_bw* bw, uint64_t bytes)
if (!bw || bw->limit == 0)
return;
#if HAVE_THREAD
thread_mutex_lock(&bw->lock);
#endif
uint64_t start = bw->start;
uint64_t now = os_tick_ms();
uint64_t elapsed = now - start;
uint64_t done;
uint64_t eta;
done = __atomic_fetch_add(&bw->total, bytes, __ATOMIC_SEQ_CST);
bw->total += bytes;
done = bw->total;
eta = done * 1000 / bw->limit;
@ -39,6 +56,10 @@ void bw_limit(struct snapraid_bw* bw, uint64_t bytes)
elapsed = eta + 10000;
}
#if HAVE_THREAD
thread_mutex_unlock(&bw->lock);
#endif
if (eta > elapsed) {
eta -= elapsed;
if (eta > 1000)

View file

@ -14,6 +14,9 @@ struct snapraid_bw {
uint64_t limit; /**< Bandwidth limit in bytes per second */
uint64_t total; /**< Remaining bytes allowed in current second */
uint64_t start; /**< Time when to reset the bandwidth counter */
#if HAVE_THREAD
thread_mutex_t lock; /**< Mutex protecting bw fields */
#endif
};
/**
@ -21,6 +24,11 @@ struct snapraid_bw {
*/
void bw_init(struct snapraid_bw* bw, uint64_t limit);
/**
* Destroy the bandwidth limit
*/
void bw_done(struct snapraid_bw* bw);
/**
* Limit IO bandwidth to stay within the configured limit.
* If no limit is set, returns immediately.

View file

@ -2147,6 +2147,8 @@ bail:
free(buffer_alloc);
free(buffer);
bw_done(&bw);
/* fail if some error are present after the run */
if (fix) {
if (state->opt.expect_unrecoverable) {

View file

@ -1138,6 +1138,8 @@ void io_done(struct snapraid_io* io)
free(io->writer_map);
free(io->writer_list);
bw_done(&io->bw);
#if HAVE_THREAD
if (io->io_max > 1) {
thread_mutex_destroy(&io->io_mutex);