Commit 83bfc5dd authored by Andrew Morton's avatar Andrew Morton Committed by James Bottomley

[PATCH] Make diskstats per-cpu using kmalloc_percpu

Patch from Ravikiran G Thirumalai <kiran@in.ibm.com>

Makes the disk stats on struct gendisk per-cpu.
parent bc858911
...@@ -450,7 +450,7 @@ static inline unsigned jiffies_to_msec(unsigned jif) ...@@ -450,7 +450,7 @@ static inline unsigned jiffies_to_msec(unsigned jif)
return (jif / HZ) * 1000 + (jif % HZ) * 1000 / HZ; return (jif / HZ) * 1000 + (jif % HZ) * 1000 / HZ;
#endif #endif
} }
static ssize_t disk_stat_read(struct gendisk * disk, char *page) static ssize_t disk_stats_read(struct gendisk * disk, char *page)
{ {
disk_round_stats(disk); disk_round_stats(disk);
return sprintf(page, return sprintf(page,
...@@ -458,14 +458,16 @@ static ssize_t disk_stat_read(struct gendisk * disk, char *page) ...@@ -458,14 +458,16 @@ static ssize_t disk_stat_read(struct gendisk * disk, char *page)
"%8u %8u %8llu %8u " "%8u %8u %8llu %8u "
"%8u %8u %8u" "%8u %8u %8u"
"\n", "\n",
disk->reads, disk->read_merges, disk_stat_read(disk, reads), disk_stat_read(disk, read_merges),
(unsigned long long)disk->read_sectors, (unsigned long long)disk_stat_read(disk, read_sectors),
jiffies_to_msec(disk->read_ticks), jiffies_to_msec(disk_stat_read(disk, read_ticks)),
disk->writes, disk->write_merges, disk_stat_read(disk, writes),
(unsigned long long)disk->write_sectors, disk_stat_read(disk, write_merges),
jiffies_to_msec(disk->write_ticks), (unsigned long long)disk_stat_read(disk, write_sectors),
disk->in_flight, jiffies_to_msec(disk->io_ticks), jiffies_to_msec(disk_stat_read(disk, write_ticks)),
jiffies_to_msec(disk->time_in_queue)); disk_stat_read(disk, in_flight),
jiffies_to_msec(disk_stat_read(disk, io_ticks)),
jiffies_to_msec(disk_stat_read(disk, time_in_queue)));
} }
static struct disk_attribute disk_attr_dev = { static struct disk_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO }, .attr = {.name = "dev", .mode = S_IRUGO },
...@@ -481,7 +483,7 @@ static struct disk_attribute disk_attr_size = { ...@@ -481,7 +483,7 @@ static struct disk_attribute disk_attr_size = {
}; };
static struct disk_attribute disk_attr_stat = { static struct disk_attribute disk_attr_stat = {
.attr = {.name = "stat", .mode = S_IRUGO }, .attr = {.name = "stat", .mode = S_IRUGO },
.show = disk_stat_read .show = disk_stats_read
}; };
static struct attribute * default_attrs[] = { static struct attribute * default_attrs[] = {
...@@ -497,6 +499,7 @@ static void disk_release(struct kobject * kobj) ...@@ -497,6 +499,7 @@ static void disk_release(struct kobject * kobj)
struct gendisk *disk = to_disk(kobj); struct gendisk *disk = to_disk(kobj);
kfree(disk->random); kfree(disk->random);
kfree(disk->part); kfree(disk->part);
free_disk_stats(disk);
kfree(disk); kfree(disk);
} }
...@@ -516,6 +519,10 @@ struct gendisk *alloc_disk(int minors) ...@@ -516,6 +519,10 @@ struct gendisk *alloc_disk(int minors)
struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL); struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL);
if (disk) { if (disk) {
memset(disk, 0, sizeof(struct gendisk)); memset(disk, 0, sizeof(struct gendisk));
if (!init_disk_stats(disk)) {
kfree(disk);
return NULL;
}
if (minors > 1) { if (minors > 1) {
int size = (minors - 1) * sizeof(struct hd_struct); int size = (minors - 1) * sizeof(struct hd_struct);
disk->part = kmalloc(size, GFP_KERNEL); disk->part = kmalloc(size, GFP_KERNEL);
......
...@@ -1475,17 +1475,17 @@ void drive_stat_acct(struct request *rq, int nr_sectors, int new_io) ...@@ -1475,17 +1475,17 @@ void drive_stat_acct(struct request *rq, int nr_sectors, int new_io)
return; return;
if (rw == READ) { if (rw == READ) {
rq->rq_disk->read_sectors += nr_sectors; disk_stat_add(rq->rq_disk, read_sectors, nr_sectors);
if (!new_io) if (!new_io)
rq->rq_disk->read_merges++; disk_stat_inc(rq->rq_disk, read_merges);
} else if (rw == WRITE) { } else if (rw == WRITE) {
rq->rq_disk->write_sectors += nr_sectors; disk_stat_add(rq->rq_disk, write_sectors, nr_sectors);
if (!new_io) if (!new_io)
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);
rq->rq_disk->in_flight++; disk_stat_inc(rq->rq_disk, in_flight);
} }
} }
...@@ -1525,11 +1525,12 @@ void disk_round_stats(struct gendisk *disk) ...@@ -1525,11 +1525,12 @@ void disk_round_stats(struct gendisk *disk)
{ {
unsigned long now = jiffies; unsigned long now = jiffies;
disk->time_in_queue += disk->in_flight * (now - disk->stamp); disk_stat_add(disk, time_in_queue,
disk_stat_read(disk, in_flight) * (now - disk->stamp));
disk->stamp = now; disk->stamp = now;
if (disk->in_flight) if (disk_stat_read(disk, in_flight))
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;
} }
...@@ -1647,7 +1648,7 @@ static int attempt_merge(request_queue_t *q, struct request *req, ...@@ -1647,7 +1648,7 @@ static int attempt_merge(request_queue_t *q, struct request *req,
if (req->rq_disk) { if (req->rq_disk) {
disk_round_stats(req->rq_disk); disk_round_stats(req->rq_disk);
req->rq_disk->in_flight--; disk_stat_dec(req->rq_disk, in_flight);
} }
__blk_put_request(q, next); __blk_put_request(q, next);
...@@ -2199,16 +2200,16 @@ void end_that_request_last(struct request *req) ...@@ -2199,16 +2200,16 @@ 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->writes++; disk_stat_inc(disk, writes);
disk->write_ticks += duration; disk_stat_add(disk, write_ticks, duration);
break; break;
case READ: case READ:
disk->reads++; disk_stat_inc(disk, reads);
disk->read_ticks += duration; disk_stat_add(disk, read_ticks, duration);
break; break;
} }
disk_round_stats(disk); disk_round_stats(disk);
disk->in_flight--; disk_stat_dec(disk, in_flight);
} }
__blk_put_request(req->q, req); __blk_put_request(req->q, req);
} }
......
...@@ -2803,7 +2803,9 @@ static int is_mddev_idle(mddev_t *mddev) ...@@ -2803,7 +2803,9 @@ static int is_mddev_idle(mddev_t *mddev)
idle = 1; idle = 1;
ITERATE_RDEV(mddev,rdev,tmp) { ITERATE_RDEV(mddev,rdev,tmp) {
struct gendisk *disk = rdev->bdev->bd_contains->bd_disk; struct gendisk *disk = rdev->bdev->bd_contains->bd_disk;
curr_events = disk->read_sectors + disk->write_sectors - disk->sync_io; curr_events = disk_stat_read(disk, read_sectors) +
disk_stat_read(disk, write_sectors) -
disk->sync_io;
if ((curr_events - rdev->last_events) > 32) { if ((curr_events - rdev->last_events) > 32) {
rdev->last_events = curr_events; rdev->last_events = curr_events;
idle = 0; idle = 0;
......
...@@ -503,13 +503,7 @@ void del_gendisk(struct gendisk *disk) ...@@ -503,13 +503,7 @@ void del_gendisk(struct gendisk *disk)
disk->capacity = 0; disk->capacity = 0;
disk->flags &= ~GENHD_FL_UP; disk->flags &= ~GENHD_FL_UP;
unlink_gendisk(disk); unlink_gendisk(disk);
disk->reads = disk->writes = 0; disk_stat_set_all(disk, 0);
disk->read_sectors = disk->write_sectors = 0;
disk->read_merges = disk->write_merges = 0;
disk->read_ticks = disk->write_ticks = 0;
disk->in_flight = 0;
disk->io_ticks = 0;
disk->time_in_queue = 0;
disk->stamp = disk->stamp_idle = 0; disk->stamp = disk->stamp_idle = 0;
devfs_remove_partitions(disk); devfs_remove_partitions(disk);
if (disk->driverfs_dev) { if (disk->driverfs_dev) {
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/major.h> #include <linux/major.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/smp.h>
#include <linux/string.h>
enum { enum {
/* These three have identical behaviour; use the second one if DOS FDISK gets /* These three have identical behaviour; use the second one if DOS FDISK gets
...@@ -70,6 +72,16 @@ struct hd_struct { ...@@ -70,6 +72,16 @@ struct hd_struct {
#define GENHD_FL_CD 8 #define GENHD_FL_CD 8
#define GENHD_FL_UP 16 #define GENHD_FL_UP 16
struct disk_stats {
unsigned read_sectors, write_sectors;
unsigned reads, writes;
unsigned read_merges, write_merges;
unsigned read_ticks, write_ticks;
unsigned io_ticks;
int in_flight;
unsigned time_in_queue;
};
struct gendisk { struct gendisk {
int major; /* major number of driver */ int major; /* major number of driver */
int first_minor; int first_minor;
...@@ -94,16 +106,86 @@ struct gendisk { ...@@ -94,16 +106,86 @@ struct gendisk {
int policy; int policy;
unsigned sync_io; /* RAID */ unsigned sync_io; /* RAID */
unsigned read_sectors, write_sectors;
unsigned reads, writes;
unsigned read_merges, write_merges;
unsigned read_ticks, write_ticks;
unsigned io_ticks;
int in_flight;
unsigned long stamp, stamp_idle; unsigned long stamp, stamp_idle;
unsigned time_in_queue; #ifdef CONFIG_SMP
struct disk_stats *dkstats;
#else
struct disk_stats dkstats;
#endif
}; };
/*
* 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
* of statistics counters
*/
#ifdef CONFIG_SMP
#define disk_stat_add(gendiskp, field, addnd) \
(per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd)
#define disk_stat_read(gendiskp, field) \
({ \
typeof(gendiskp->dkstats->field) res = 0; \
int i; \
for (i=0; i < NR_CPUS; i++) { \
if (!cpu_possible(i)) \
continue; \
res += per_cpu_ptr(gendiskp->dkstats, i)->field; \
} \
res; \
})
static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) {
int i;
for (i=0; i < NR_CPUS; i++) {
if (cpu_possible(i)) {
memset(per_cpu_ptr(gendiskp->dkstats, i), value,
sizeof (struct disk_stats));
}
}
}
#else
#define disk_stat_add(gendiskp, field, addnd) (gendiskp->dkstats.field += addnd)
#define disk_stat_read(gendiskp, field) (gendiskp->dkstats.field)
static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) {
memset(&gendiskp->dkstats, value, sizeof (struct disk_stats));
}
#endif
#define disk_stat_inc(gendiskp, field) disk_stat_add(gendiskp, field, 1)
#define disk_stat_dec(gendiskp, field) disk_stat_add(gendiskp, field, -1)
#define disk_stat_sub(gendiskp, field, subnd) \
disk_stat_add(gendiskp, field, -subnd)
/* Inlines to alloc and free disk stats in struct gendisk */
#ifdef CONFIG_SMP
static inline int init_disk_stats(struct gendisk *disk)
{
disk->dkstats = kmalloc_percpu(sizeof (struct disk_stats), GFP_KERNEL);
if (!disk->dkstats)
return 0;
disk_stat_set_all(disk, 0);
return 1;
}
static inline void free_disk_stats(struct gendisk *disk)
{
kfree_percpu(disk->dkstats);
}
#else /* CONFIG_SMP */
static inline int init_disk_stats(struct gendisk *disk)
{
return 1;
}
static inline void free_disk_stats(struct gendisk *disk)
{
}
#endif /* CONFIG_SMP */
/* drivers/block/ll_rw_blk.c */ /* drivers/block/ll_rw_blk.c */
extern void disk_round_stats(struct gendisk *disk); extern void disk_round_stats(struct gendisk *disk);
......
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