Commit d38948f6 authored by Ingo Molnar's avatar Ingo Molnar Committed by Linus Torvalds

[PATCH] disk stats preempt safety

The per-cpu disk stats are being updated in a non-preempt-safe manner in a
couple of places.

The patch introduces introduces preempt and non-preempt versions of the
statistics code and updates the block code to use the appropriate ones.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 29c6097f
...@@ -2058,13 +2058,13 @@ void drive_stat_acct(struct request *rq, int nr_sectors, int new_io) ...@@ -2058,13 +2058,13 @@ void drive_stat_acct(struct request *rq, int nr_sectors, int new_io)
return; return;
if (rw == READ) { if (rw == READ) {
disk_stat_add(rq->rq_disk, read_sectors, nr_sectors); __disk_stat_add(rq->rq_disk, read_sectors, nr_sectors);
if (!new_io) if (!new_io)
disk_stat_inc(rq->rq_disk, read_merges); __disk_stat_inc(rq->rq_disk, read_merges);
} else if (rw == WRITE) { } else if (rw == WRITE) {
disk_stat_add(rq->rq_disk, write_sectors, nr_sectors); __disk_stat_add(rq->rq_disk, write_sectors, nr_sectors);
if (!new_io) if (!new_io)
disk_stat_inc(rq->rq_disk, write_merges); __disk_stat_inc(rq->rq_disk, write_merges);
} }
if (new_io) { if (new_io) {
disk_round_stats(rq->rq_disk); disk_round_stats(rq->rq_disk);
...@@ -2110,12 +2110,12 @@ void disk_round_stats(struct gendisk *disk) ...@@ -2110,12 +2110,12 @@ void disk_round_stats(struct gendisk *disk)
{ {
unsigned long now = jiffies; unsigned long now = jiffies;
disk_stat_add(disk, time_in_queue, __disk_stat_add(disk, time_in_queue,
disk->in_flight * (now - disk->stamp)); disk->in_flight * (now - disk->stamp));
disk->stamp = now; disk->stamp = now;
if (disk->in_flight) if (disk->in_flight)
disk_stat_add(disk, io_ticks, (now - disk->stamp_idle)); __disk_stat_add(disk, io_ticks, (now - disk->stamp_idle));
disk->stamp_idle = now; disk->stamp_idle = now;
} }
...@@ -2982,12 +2982,12 @@ void end_that_request_last(struct request *req) ...@@ -2982,12 +2982,12 @@ void end_that_request_last(struct request *req)
unsigned long duration = jiffies - req->start_time; unsigned long duration = jiffies - req->start_time;
switch (rq_data_dir(req)) { switch (rq_data_dir(req)) {
case WRITE: case WRITE:
disk_stat_inc(disk, writes); __disk_stat_inc(disk, writes);
disk_stat_add(disk, write_ticks, duration); __disk_stat_add(disk, write_ticks, duration);
break; break;
case READ: case READ:
disk_stat_inc(disk, reads); __disk_stat_inc(disk, reads);
disk_stat_add(disk, read_ticks, duration); __disk_stat_add(disk, read_ticks, duration);
break; break;
} }
disk_round_stats(disk); disk_round_stats(disk);
......
...@@ -129,13 +129,14 @@ struct gendisk { ...@@ -129,13 +129,14 @@ struct gendisk {
/* /*
* Macros to operate on percpu disk statistics: * Macros to operate on percpu disk statistics:
* Since writes to disk_stats are serialised through the queue_lock, *
* smp_processor_id() should be enough to get to the per_cpu versions * The __ variants should only be called in critical sections. The full
* of statistics counters * variants disable/enable preemption.
*/ */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
#define disk_stat_add(gendiskp, field, addnd) \ #define __disk_stat_add(gendiskp, field, addnd) \
(per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd) (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd)
#define disk_stat_read(gendiskp, field) \ #define disk_stat_read(gendiskp, field) \
({ \ ({ \
typeof(gendiskp->dkstats->field) res = 0; \ typeof(gendiskp->dkstats->field) res = 0; \
...@@ -159,7 +160,8 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { ...@@ -159,7 +160,8 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) {
} }
#else #else
#define disk_stat_add(gendiskp, field, addnd) (gendiskp->dkstats.field += addnd) #define __disk_stat_add(gendiskp, field, addnd) \
(gendiskp->dkstats.field += addnd)
#define disk_stat_read(gendiskp, field) (gendiskp->dkstats.field) #define disk_stat_read(gendiskp, field) (gendiskp->dkstats.field)
static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) {
...@@ -167,8 +169,21 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { ...@@ -167,8 +169,21 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) {
} }
#endif #endif
#define disk_stat_inc(gendiskp, field) disk_stat_add(gendiskp, field, 1) #define disk_stat_add(gendiskp, field, addnd) \
do { \
preempt_disable(); \
__disk_stat_add(gendiskp, field, addnd); \
preempt_enable(); \
} while (0)
#define __disk_stat_dec(gendiskp, field) __disk_stat_add(gendiskp, field, -1)
#define disk_stat_dec(gendiskp, field) disk_stat_add(gendiskp, field, -1) #define disk_stat_dec(gendiskp, field) disk_stat_add(gendiskp, field, -1)
#define __disk_stat_inc(gendiskp, field) __disk_stat_add(gendiskp, field, 1)
#define disk_stat_inc(gendiskp, field) disk_stat_add(gendiskp, field, 1)
#define __disk_stat_sub(gendiskp, field, subnd) \
__disk_stat_add(gendiskp, field, -subnd)
#define disk_stat_sub(gendiskp, field, subnd) \ #define disk_stat_sub(gendiskp, field, subnd) \
disk_stat_add(gendiskp, field, -subnd) disk_stat_add(gendiskp, field, -subnd)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment