dm stats: fix possible counter corruption on 32-bit systems
There was a deliberate race condition in dm_stat_for_entry() to avoid the overhead of disabling and enabling interrupts. The race could result in some events not being counted on 64-bit architectures. However, on 32-bit architectures, operations on long long variables are not atomic, so the race condition could cause the counter to jump by 2^32. Such jumps could be disruptive, so we need to do proper locking on 32-bit architectures. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Cc: Alasdair G. Kergon <agk@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
This commit is contained in:
parent
cc9d3c382b
commit
bbf3f8cbdc
|
@ -451,19 +451,26 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
|
||||||
struct dm_stat_percpu *p;
|
struct dm_stat_percpu *p;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For strict correctness we should use local_irq_disable/enable
|
* For strict correctness we should use local_irq_save/restore
|
||||||
* instead of preempt_disable/enable.
|
* instead of preempt_disable/enable.
|
||||||
*
|
*
|
||||||
* This is racy if the driver finishes bios from non-interrupt
|
* preempt_disable/enable is racy if the driver finishes bios
|
||||||
* context as well as from interrupt context or from more different
|
* from non-interrupt context as well as from interrupt context
|
||||||
* interrupts.
|
* or from more different interrupts.
|
||||||
*
|
*
|
||||||
* However, the race only results in not counting some events,
|
* On 64-bit architectures the race only results in not counting some
|
||||||
* so it is acceptable.
|
* events, so it is acceptable. On 32-bit architectures the race could
|
||||||
|
* cause the counter going off by 2^32, so we need to do proper locking
|
||||||
|
* there.
|
||||||
*
|
*
|
||||||
* part_stat_lock()/part_stat_unlock() have this race too.
|
* part_stat_lock()/part_stat_unlock() have this race too.
|
||||||
*/
|
*/
|
||||||
|
#if BITS_PER_LONG == 32
|
||||||
|
unsigned long flags;
|
||||||
|
local_irq_save(flags);
|
||||||
|
#else
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
|
#endif
|
||||||
p = &s->stat_percpu[smp_processor_id()][entry];
|
p = &s->stat_percpu[smp_processor_id()][entry];
|
||||||
|
|
||||||
if (!end) {
|
if (!end) {
|
||||||
|
@ -478,7 +485,11 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
|
||||||
p->ticks[idx] += duration;
|
p->ticks[idx] += duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if BITS_PER_LONG == 32
|
||||||
|
local_irq_restore(flags);
|
||||||
|
#else
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __dm_stat_bio(struct dm_stat *s, unsigned long bi_rw,
|
static void __dm_stat_bio(struct dm_stat *s, unsigned long bi_rw,
|
||||||
|
|
Loading…
Reference in New Issue