Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 381

kernel-2.6.18-238.el5.src.rpm

From: Jerome Marchand <jmarchand@redhat.com>
Date: Tue, 15 Jul 2008 15:55:42 +0200
Subject: [block] Enhanced Partition Statistics: core statistics
Message-id: 20080715135909.902901206@redhat.com
O-Subject: [Patch RHEL5.3 1/9] Enhanced Partition Statistics: core statistics
Bugzilla: 224322
RH-Acked-by: Anton Arapov <aarapov@redhat.com>
RH-Acked-by: Peter Zijlstra <pzijlstr@redhat.com>

bz224322

This patch contain the core infrastructure of enhanced partition
statistics. It adds to struct hd_struct the same stats data as struct
gendisk and define basics function to manipulate them. The data are
stored in an auxilliary struture to avoid kABI breakage. That
structure is accessed through an hash table.

commit: ea5c48ab2a76559d4af39e1f7de137c0851ac0a5

diff --git a/block/genhd.c b/block/genhd.c
index 1b7e6db..762eb46 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -15,6 +15,7 @@
 #include <linux/kobj_map.h>
 #include <linux/buffer_head.h>
 #include <linux/mutex.h>
+#include <linux/hash.h>
 
 struct subsystem block_subsys;
 static DEFINE_MUTEX(block_subsys_lock);
@@ -709,3 +710,67 @@ int invalidate_partition(struct gendisk *disk, int index)
 }
 
 EXPORT_SYMBOL(invalidate_partition);
+
+#define PARTSTATS_HASH_BITS 10
+#define PARTSTATS_HASH_SIZE (1 << PARTSTATS_HASH_BITS)
+struct hlist_head partstats_hash[PARTSTATS_HASH_SIZE] =
+	{ [ 0 ... PARTSTATS_HASH_SIZE - 1 ] = HLIST_HEAD_INIT };
+DEFINE_SPINLOCK(partstats_lock);
+#define partstats_hash_fn(part) \
+	hash_long((unsigned long)(part), PARTSTATS_HASH_BITS)
+
+struct partstats *get_partstats(struct hd_struct *part)
+{
+	struct hlist_head *head;
+	struct hlist_node *node;
+	struct partstats *p;
+
+	head = &partstats_hash[partstats_hash_fn(part)];
+	hlist_for_each_entry_rcu(p, node, head, hlist) {
+		if (p->addr == part)
+			return p;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(get_partstats);
+
+int init_partstats(struct hd_struct *part)
+{
+	struct partstats *ps = kzalloc(sizeof(struct partstats), GFP_KERNEL);
+	struct hlist_head *head;
+
+	if (!ps)
+		return 0;
+	if (!init_part_stats(ps)) {
+		kfree(ps);
+		return 0;
+	}
+
+	spin_lock(&partstats_lock);
+	head = &partstats_hash[partstats_hash_fn(part)];
+	ps->addr = part;
+	hlist_add_head_rcu(&ps->hlist, head);
+	spin_unlock(&partstats_lock);
+
+	return 1;
+}
+
+void free_partstats_rcu(struct rcu_head *head)
+{
+	struct partstats *ps = container_of(head, struct partstats, rcu);
+	free_part_stats(ps);
+	kfree(ps);
+}
+
+void free_partstats(struct hd_struct *part)
+{
+	struct partstats *ps;
+
+	spin_lock(&partstats_lock);
+	ps = get_partstats(part);
+	if(ps) {
+		hlist_del_rcu(&ps->hlist);
+		call_rcu(&ps->rcu, free_partstats_rcu);
+	}
+	spin_unlock(&partstats_lock);
+}
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index e4af57e..845e9ab 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -74,6 +74,33 @@ struct partition {
 	__le32 nr_sects;		/* nr of sectors in partition */
 } __attribute__((packed));
 
+struct disk_stats {
+	unsigned long sectors[2];	/* READs and WRITEs */
+	unsigned long ios[2];
+	unsigned long merges[2];
+	unsigned long ticks[2];
+	unsigned long io_ticks;
+	unsigned long time_in_queue;
+};
+
+/*
+ * Auxiliary struct to avoid kABI breakage
+ */
+struct partstats {
+	struct hlist_node hlist;
+	struct rcu_head rcu;
+	void *addr;
+	unsigned long stamp, stamp_idle;
+	int in_flight;
+#ifdef	CONFIG_SMP
+	struct disk_stats *dkstats;
+#else
+	struct disk_stats dkstats;
+#endif
+};
+
+extern struct partstats *get_partstats(struct hd_struct *);
+
 struct hd_struct {
 	sector_t start_sect;
 	sector_t nr_sects;
@@ -88,15 +115,6 @@ struct hd_struct {
 #define GENHD_FL_CD				8
 #define GENHD_FL_UP				16
 #define GENHD_FL_SUPPRESS_PARTITION_INFO	32
-
-struct disk_stats {
-	unsigned long sectors[2];	/* READs and WRITEs */
-	unsigned long ios[2];
-	unsigned long merges[2];
-	unsigned long ticks[2];
-	unsigned long io_ticks;
-	unsigned long time_in_queue;
-};
 	
 struct gendisk {
 	int major;			/* major number of driver */
@@ -143,6 +161,20 @@ struct disk_attribute {
  * The __ variants should only be called in critical sections. The full
  * variants disable/enable preemption.
  */
+static inline struct hd_struct *get_part(struct gendisk *gendiskp,
+					 sector_t sector)
+{
+	struct hd_struct *part;
+	int i;
+	for (i = 0; i < gendiskp->minors - 1; i++) {
+		part = gendiskp->part[i];
+		if (part && part->start_sect <= sector
+		    && sector < part->start_sect + part->nr_sects)
+			return part;
+	}
+	return NULL;
+}
+
 #ifdef	CONFIG_SMP
 #define __disk_stat_add(gendiskp, field, addnd) 	\
 	(per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd)
@@ -162,6 +194,56 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value)	{
 		memset(per_cpu_ptr(gendiskp->dkstats, i), value,
 				sizeof (struct disk_stats));
 }		
+
+#define part_stat_add(part, field, addnd)				\
+({									\
+	struct partstats *ps;						\
+	rcu_read_lock();						\
+	ps = get_partstats(part);					\
+	per_cpu_ptr(ps->dkstats, smp_processor_id())->field += addnd;	\
+	rcu_read_unlock();						\
+})
+
+#define __all_stat_add(gendiskp, field, addnd, sector)		\
+({								\
+	struct hd_struct *part = get_part(gendiskp, sector);	\
+	if (part)						\
+		part_stat_add(part, field, addnd);		\
+	__disk_stat_add(gendiskp, field, addnd);		\
+})
+
+#define all_stat_add(gendiskp, field, addnd, sector)		\
+({								\
+	struct hd_struct *part = get_part(gendiskp, sector);	\
+	if (part)						\
+		part_stat_add(part, field, addnd);		\
+	disk_stat_add(gendiskp, field, addnd);			\
+})
+
+#define part_stat_read(part, field)				\
+({								\
+	struct partstats *ps;					\
+	typeof(ps->dkstats->field) res = 0;			\
+	int i;							\
+	rcu_read_lock();					\
+	ps = get_partstats(part);				\
+	for_each_possible_cpu(i)				\
+		res += per_cpu_ptr(ps->dkstats, i)->field;	\
+	rcu_read_unlock();					\
+	res;							\
+})
+
+static inline void part_stat_reset(struct hd_struct *part)
+{
+	struct partstats *ps;
+	int i;
+	rcu_read_lock();
+	ps = get_partstats(part);
+	for_each_possible_cpu(i)
+		memset(per_cpu_ptr(ps->dkstats, i), 0,
+		       sizeof(struct disk_stats));
+	rcu_read_unlock();
+}
 				
 #else
 #define __disk_stat_add(gendiskp, field, addnd) \
@@ -171,6 +253,50 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value)	{
 static inline void disk_stat_set_all(struct gendisk *gendiskp, int value)	{
 	memset(&gendiskp->dkstats, value, sizeof (struct disk_stats));
 }
+#define part_stat_add(part, field, addnd) 		\
+({							\
+	rcu_read_lock();				\
+	get_partstats(part)->dkstats.field += addnd;	\
+	rcu_read_unlock();				\
+})
+
+#define __all_stat_add(gendiskp, field, addnd, sector)		\
+({								\
+	struct hd_struct *part = get_part(gendiskp, sector);	\
+	if (part) {						\
+		rcu_read_lock();				\
+		get_partstats(part)->dkstats.field += addnd;	\
+		rcu_read_unlock();				\
+	}							\
+	__disk_stat_add(gendiskp, field, addnd);		\
+})
+
+#define all_stat_add(gendiskp, field, addnd, sector)		\
+({								\
+	struct hd_struct *part = get_part(gendiskp, sector);	\
+	if (part) {						\
+		rcu_read_lock();				\
+		get_partstats(part)->dkstats.field += addnd;	\
+		rcu_read_unlock();				\
+	}							\
+	disk_stat_add(gendiskp, field, addnd);			\
+})
+
+#define part_stat_read(part, field)	 		\
+({							\
+	typeof(((struct disk_stats *)0)->field) res;	\
+	rcu_read_lock();				\
+	res = get_partstats(part)->dkstats.field;	\
+	rcu_read_unlock();				\
+	res;						\
+})
+
+static inline void part_stat_reset(struct hd_struct *part)
+{
+	rcu_read_lock();
+	memset(&get_partstats(part)->dkstats, 0, sizeof(struct disk_stats));
+	rcu_read_unlock();
+}
 #endif
 
 #define disk_stat_add(gendiskp, field, addnd)			\
@@ -191,6 +317,23 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value)	{
 #define disk_stat_sub(gendiskp, field, subnd) \
 		disk_stat_add(gendiskp, field, -subnd)
 
+#define part_stat_inc(gendiskp, field) part_stat_add(gendiskp, field, 1)
+#define part_stat_dec(gendiskp, field) part_stat_add(gendiskp, field, -1)
+#define part_stat_sub(gendiskp, field, subnd) \
+		part_stat_add(gendiskp, field, -subnd)
+
+#define __all_stat_inc(gendiskp, field, sector) \
+		__all_stat_add(gendiskp, field, 1, sector)
+#define all_stat_inc(gendiskp, field, sector) \
+		all_stat_add(gendiskp, field, 1, sector)
+#define __all_stat_dec(gendiskp, field, sector) \
+		__all_stat_add(gendiskp, field, -1, sector)
+#define all_stat_dec(gendiskp, field, sector) \
+		all_stat_add(gendiskp, field, -1, sector)
+#define __all_stat_sub(gendiskp, field, subnd, sector) \
+		__all_stat_add(gendiskp, field, -subnd, sector)
+#define all_stat_sub(gendiskp, field, subnd, sector) \
+		all_stat_add(gendiskp, field, -subnd, sector)
 
 /* Inlines to alloc and free disk stats in struct gendisk */
 #ifdef  CONFIG_SMP
@@ -206,6 +349,20 @@ static inline void free_disk_stats(struct gendisk *disk)
 {
 	free_percpu(disk->dkstats);
 }
+
+static inline int init_part_stats(struct partstats *ps)
+{
+	ps->dkstats = alloc_percpu(struct disk_stats);
+	if (!ps->dkstats)
+		return 0;
+	return 1;
+}
+
+static inline void free_part_stats(struct partstats *ps)
+{
+	free_percpu(ps->dkstats);
+}
+
 #else	/* CONFIG_SMP */
 static inline int init_disk_stats(struct gendisk *disk)
 {
@@ -215,6 +372,15 @@ static inline int init_disk_stats(struct gendisk *disk)
 static inline void free_disk_stats(struct gendisk *disk)
 {
 }
+
+static inline int init_part_stats(struct partstats *ps)
+{
+	return 1;
+}
+
+static inline void free_part_stats(struct partstats *ps)
+{
+}
 #endif	/* CONFIG_SMP */
 
 /* drivers/block/ll_rw_blk.c */
@@ -226,6 +392,8 @@ extern void add_disk(struct gendisk *disk);
 extern void del_gendisk(struct gendisk *gp);
 extern void unlink_gendisk(struct gendisk *gp);
 extern struct gendisk *get_gendisk(dev_t dev, int *part);
+int init_partstats(struct hd_struct *part);
+void free_partstats(struct hd_struct *part);
 
 extern void set_device_ro(struct block_device *bdev, int flag);
 extern void set_disk_ro(struct gendisk *disk, int flag);