copy: reduce clash overhead

This commit is contained in:
Thomas Schoebel-Theuer 2022-02-21 17:39:58 +01:00
parent 8e1e2f81a3
commit d41ea13d37
2 changed files with 50 additions and 30 deletions

View File

@ -87,29 +87,32 @@ atomic_t global_copy_write_flight;
///////////////////////// own helper functions //////////////////////// ///////////////////////// own helper functions ////////////////////////
/* TODO: /* Historic. Now in production for years. Here my old ideas:
*
* The clash logic is untested / alpha stage (Feb. 2011). * The clash logic is untested / alpha stage (Feb. 2011).
* *
* For now, the output is never used, so this cannot do harm. * For now, the output is never used, so this cannot do harm.
*
* In order to get the output really working / enterprise grade,
* some larger test effort should be invested.
*/ */
static inline static inline
void _clash(struct copy_brick *brick) void notify_clash(struct copy_brick *brick, bool do_wake)
{ {
set_bit(0, &brick->clash); WRITE_ONCE(brick->clash, true);
smp_mb();
atomic_inc(&brick->total_clash_count); atomic_inc(&brick->total_clash_count);
if (!do_wake)
return;
WRITE_ONCE(brick->trigger, true); WRITE_ONCE(brick->trigger, true);
brick_wake_smp(&brick->event); brick_wake_smp(&brick->event);
} }
static inline static inline
int _clear_clash(struct copy_brick *brick) bool clear_clash(struct copy_brick *brick)
{ {
int old; bool cleared;
old = test_and_clear_bit(0, &brick->clash);
return old; cleared = cmpxchg(&brick->clash, true, false);
smp_mb();
return cleared;
} }
/* Current semantics (NOT REALLY IMPLEMENTED because OUTPUT IS NOT IN USE) /* Current semantics (NOT REALLY IMPLEMENTED because OUTPUT IS NOT IN USE)
@ -315,7 +318,7 @@ void copy_endio(struct generic_callback *cb)
exit: exit:
if (unlikely(error < 0)) { if (unlikely(error < 0)) {
WRITE_ONCE(st->error, error); WRITE_ONCE(st->error, error);
_clash(brick); notify_clash(brick, false);
} }
WRITE_ONCE(st->active[queue], false); WRITE_ONCE(st->active[queue], false);
if (mref->ref_flags & MREF_WRITE) { if (mref->ref_flags & MREF_WRITE) {
@ -357,7 +360,7 @@ int _make_mref(struct copy_brick *brick,
int status = -EAGAIN; int status = -EAGAIN;
/* Does it make sense to create a new mref right here? */ /* Does it make sense to create a new mref right here? */
if (brick->clash) if (READ_ONCE(brick->clash))
goto done; goto done;
status = -EINVAL; status = -EINVAL;
if (current_pos < 0 || end_pos <= 0) if (current_pos < 0 || end_pos <= 0)
@ -834,7 +837,6 @@ restart:
default: default:
MARS_ERR("illegal state %d at index %u\n", MARS_ERR("illegal state %d at index %u\n",
state, index); state, index);
_clash(brick);
progress = -EILSEQ; progress = -EILSEQ;
} }
@ -846,7 +848,7 @@ idle:
WRITE_ONCE(st->error, progress); WRITE_ONCE(st->error, progress);
MARS_DBG("progress = %d\n", progress); MARS_DBG("progress = %d\n", progress);
progress = 0; progress = 0;
_clash(brick); notify_clash(brick, false);
} else if (do_restart) { } else if (do_restart) {
goto restart; goto restart;
} else if (st->state != next_state) { } else if (st->state != next_state) {
@ -871,6 +873,27 @@ idle:
return progress; return progress;
} }
static
bool wait_reset_clash(struct copy_brick *brick, bool do_reset)
{
if (!READ_ONCE(brick->clash))
return false;
if (atomic_read(&brick->copy_read_flight) + atomic_read(&brick->copy_write_flight) > 0) {
/* wait until all pending copy IO has finished
*/
return true;
}
if (do_reset) {
MARS_DBG("clash\n");
_clear_all_mref(brick);
_clear_state_table(brick);
clear_clash(brick);
}
return false;
}
static static
int _run_copy(struct copy_brick *brick, loff_t this_start) int _run_copy(struct copy_brick *brick, loff_t this_start)
{ {
@ -880,19 +903,9 @@ int _run_copy(struct copy_brick *brick, loff_t this_start)
int progress; int progress;
bool is_first; bool is_first;
if (unlikely(_clear_clash(brick))) { if (READ_ONCE(brick->clash) &&
MARS_DBG("clash\n"); wait_reset_clash(brick, false))
if (atomic_read(&brick->copy_read_flight) + atomic_read(&brick->copy_write_flight) > 0) {
/* wait until all pending copy IO has finished
*/
_clash(brick);
MARS_DBG("re-clash\n");
brick_msleep(100);
return 0; return 0;
}
_clear_all_mref(brick);
_clear_state_table(brick);
}
if (this_start < brick->copy_last) if (this_start < brick->copy_last)
this_start = brick->copy_last; this_start = brick->copy_last;
@ -941,7 +954,8 @@ int _run_copy(struct copy_brick *brick, loff_t this_start)
} }
// check the resulting state: can we advance the copy_last pointer? // check the resulting state: can we advance the copy_last pointer?
if (this_start == brick->copy_last && progress && !brick->clash) { if (progress &&
this_start == brick->copy_last) {
int count = 0; int count = 0;
int error; int error;
@ -1018,6 +1032,12 @@ int _run_copy(struct copy_brick *brick, loff_t this_start)
_update_percent(brick, false); _update_percent(brick, false);
} }
} }
/* when necessary, reset and allow restart */
if (READ_ONCE(brick->clash) &&
wait_reset_clash(brick, true)) {
brick_msleep(100);
notify_clash(brick, true);
}
return progress; return progress;
} }
@ -1257,7 +1277,7 @@ char *copy_statistics(struct copy_brick *brick, int verbose)
"verify_error_count = %d " "verify_error_count = %d "
"low_dirty = %d " "low_dirty = %d "
"is_aborting = %d " "is_aborting = %d "
"clash = %lu | " "clash = %d | "
"total clash_count = %d | " "total clash_count = %d | "
"io_flight = %d " "io_flight = %d "
"copy_read_flight = %d " "copy_read_flight = %d "

View File

@ -105,7 +105,7 @@ struct copy_brick {
bool terminated; bool terminated;
loff_t stable_copy_start; loff_t stable_copy_start;
loff_t stable_copy_end; loff_t stable_copy_end;
unsigned long clash; bool clash;
atomic_t total_clash_count; atomic_t total_clash_count;
atomic_t io_flight; atomic_t io_flight;
atomic_t copy_read_flight; atomic_t copy_read_flight;