Skip to content

Commit

Permalink
dm stats: fix possible counter corruption on 32-bit systems
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mikulas Patocka authored and Mike Snitzer committed Sep 18, 2013
1 parent cc9d3c3 commit bbf3f8c
Showing 1 changed file with 17 additions and 6 deletions.
23 changes: 17 additions & 6 deletions drivers/md/dm-stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,19 +451,26 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
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.
*
* This is racy if the driver finishes bios from non-interrupt
* context as well as from interrupt context or from more different
* interrupts.
* preempt_disable/enable is racy if the driver finishes bios
* from non-interrupt context as well as from interrupt context
* or from more different interrupts.
*
* However, the race only results in not counting some events,
* so it is acceptable.
* On 64-bit architectures the race only results in not counting some
* 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.
*/
#if BITS_PER_LONG == 32
unsigned long flags;
local_irq_save(flags);
#else
preempt_disable();
#endif
p = &s->stat_percpu[smp_processor_id()][entry];

if (!end) {
Expand All @@ -478,7 +485,11 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
p->ticks[idx] += duration;
}

#if BITS_PER_LONG == 32
local_irq_restore(flags);
#else
preempt_enable();
#endif
}

static void __dm_stat_bio(struct dm_stat *s, unsigned long bi_rw,
Expand Down

0 comments on commit bbf3f8c

Please sign in to comment.