[PATCH 7/7] lockdep: scalable statistics

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Rework the lock stat counters to be scalable.

Signed-off-by: Peter Zijlstra <[email protected]>
---
 include/linux/lockdep.h |   34 +++++-----
 kernel/lockdep.c        |  162 ++++++++++++++++++++++++++++++++++++------------
 kernel/lockdep_proc.c   |  136 ++++++++++------------------------------
 3 files changed, 178 insertions(+), 154 deletions(-)

Index: linux-2.6/include/linux/lockdep.h
===================================================================
--- linux-2.6.orig/include/linux/lockdep.h	2007-05-23 10:23:16.000000000 +0200
+++ linux-2.6/include/linux/lockdep.h	2007-05-23 10:36:45.000000000 +0200
@@ -72,18 +72,6 @@ struct lock_class_key {
 	struct lockdep_subclass_key	subkeys[MAX_LOCKDEP_SUBCLASSES];
 };
 
-struct lock_contention_point {
-	unsigned long ip;
-	atomic_t count;
-};
-
-struct lock_time {
-	raw_spinlock_t lock;
-	unsigned long long min, max;
-	unsigned long long total;
-	unsigned long nr;
-};
-
 /*
  * The lock-class itself:
  */
@@ -128,17 +116,31 @@ struct lock_class {
 	const char			*name;
 	int				name_version;
 
+	unsigned long			contention_point[4];
+};
+
 #ifdef CONFIG_LOCK_STAT
-	atomic_t			read_contentions;
-	atomic_t			write_contentions;
-	struct lock_contention_point	contention_point[4];
+struct lock_time {
+	unsigned long long		min;
+	unsigned long long		max;
+	unsigned long long		total;
+	unsigned long			nr;
+};
+
+struct lock_class_stats {
+	unsigned long			read_contentions;
+	unsigned long			write_contentions;
+	unsigned long			contention_point[4];
 	struct lock_time		read_waittime;
 	struct lock_time		write_waittime;
 	struct lock_time		read_holdtime;
 	struct lock_time		write_holdtime;
-#endif
 };
 
+struct lock_class_stats lock_stats(struct lock_class *class);
+void clear_lock_stats(struct lock_class *class);
+#endif
+
 /*
  * Map the lock object (the lock instance) to the lock-class object.
  * This is embedded into specific lock instances:
Index: linux-2.6/kernel/lockdep.c
===================================================================
--- linux-2.6.orig/kernel/lockdep.c	2007-05-23 10:23:16.000000000 +0200
+++ linux-2.6/kernel/lockdep.c	2007-05-23 11:50:13.000000000 +0200
@@ -133,6 +133,105 @@ static struct lock_list *alloc_list_entr
 unsigned long nr_lock_classes;
 static struct lock_class lock_classes[MAX_LOCKDEP_KEYS];
 
+#ifdef CONFIG_LOCK_STAT
+static DEFINE_PER_CPU(struct lock_class_stats[MAX_LOCKDEP_KEYS], lock_stats);
+
+static int lock_contention_point(struct lock_class *class, unsigned long ip)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(class->contention_point); i++) {
+		if (class->contention_point[i] == 0) {
+			class->contention_point[i] = ip;
+			break;
+		}
+		if (class->contention_point[i] == ip)
+			break;
+	}
+
+	return i;
+}
+
+static inline void lock_contention_add(struct lock_class_stats *src,
+		struct lock_class_stats *dst)
+{
+	int i;
+
+	dst->read_contentions += src->read_contentions;
+	dst->write_contentions += src->write_contentions;
+	for (i = 0; i < ARRAY_SIZE(dst->contention_point); i++)
+		dst->contention_point[i] += src->contention_point[i];
+}
+
+static inline void lock_time_add(struct lock_time *src, struct lock_time *dst)
+{
+	dst->min += src->min;
+	dst->max += src->max;
+	dst->total += src->total;
+	dst->nr += src->nr;
+}
+
+static void lock_time_inc(struct lock_time *lt, unsigned long long time)
+{
+	if (time > lt->max)
+		lt->max = time;
+
+	if (time < lt->min || !lt->min)
+		lt->min = time;
+
+	lt->total += time;
+	lt->nr++;
+}
+
+struct lock_class_stats lock_stats(struct lock_class *class)
+{
+	struct lock_class_stats stats;
+	int cpu, i;
+
+	memset(&stats, 0, sizeof(struct lock_class_stats));
+	for_each_possible_cpu(cpu) {
+		struct lock_class_stats *pcs =
+			&per_cpu(lock_stats, cpu)[class - lock_classes];
+
+		stats.read_contentions += pcs->read_contentions;
+		stats.write_contentions += pcs->write_contentions;
+		for (i = 0; i < ARRAY_SIZE(stats.contention_point); i++)
+			stats.contention_point[i] += pcs->contention_point[i];
+
+		lock_time_add(&pcs->read_waittime, &stats.read_waittime);
+		lock_time_add(&pcs->write_waittime, &stats.write_waittime);
+
+		lock_time_add(&pcs->read_holdtime, &stats.read_holdtime);
+		lock_time_add(&pcs->write_holdtime, &stats.write_holdtime);
+	}
+
+	return stats;
+}
+
+void clear_lock_stats(struct lock_class *class)
+{
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+		struct lock_class_stats *cpu_stats =
+			&per_cpu(lock_stats, cpu)[class - lock_classes];
+
+		memset(cpu_stats, 0, sizeof(struct lock_class_stats));
+	}
+	memset(class->contention_point, 0, sizeof(class->contention_point));
+}
+
+static struct lock_class_stats *get_lock_stats(struct lock_class *class)
+{
+	return &get_cpu_var(lock_stats)[class - lock_classes];
+}
+
+static void put_lock_stats(struct lock_class_stats *stats)
+{
+	put_cpu_var(lock_stats);
+}
+#endif
+
 /*
  * We keep a global list of all lock classes. The list only grows,
  * never shrinks. The list is only accessed with the lockdep
@@ -1290,12 +1389,6 @@ register_lock_class(struct lockdep_map *
 	INIT_LIST_HEAD(&class->lock_entry);
 	INIT_LIST_HEAD(&class->locks_before);
 	INIT_LIST_HEAD(&class->locks_after);
-#ifdef CONFIG_LOCK_STAT
-	class->read_waittime.lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
-	class->write_waittime.lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
-	class->read_holdtime.lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
-	class->write_holdtime.lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
-#endif
 	class->name_version = count_matching_names(class);
 	/*
 	 * We use RCU's safe list-add method to make
@@ -2349,22 +2442,9 @@ static int check_unlock(struct task_stru
 }
 
 #ifdef CONFIG_LOCK_STAT
-static void lock_time_add(struct lock_time *lt, unsigned long long wt)
-{
-	__raw_spin_lock(&lt->lock);
-	if (wt > lt->max)
-		lt->max = wt;
-
-	if (wt < lt->min || !lt->min)
-		lt->min = wt;
-
-	lt->total += wt;
-	lt->nr++;
-	__raw_spin_unlock(&lt->lock);
-}
-
 static void lock_release_holdtime(struct held_lock *hlock)
 {
+	struct lock_class_stats *stats;
 	unsigned long long holdtime;
 
 	if (!lock_stat)
@@ -2372,10 +2452,14 @@ static void lock_release_holdtime(struct
 
 	holdtime = sched_clock() - hlock->holdtime_stamp;
 
+	stats = get_lock_stats(hlock->class);
+
 	if (hlock->read)
-		lock_time_add(&hlock->class->read_holdtime, holdtime);
+		lock_time_inc(&stats->read_holdtime, holdtime);
 	else
-		lock_time_add(&hlock->class->write_holdtime, holdtime);
+		lock_time_inc(&stats->write_holdtime, holdtime);
+
+	put_lock_stats(stats);
 }
 #else
 static void lock_release_holdtime(struct held_lock *hlock)
@@ -2516,8 +2600,9 @@ __lock_contended(struct lockdep_map *loc
 {
 	struct task_struct *curr = current;
 	struct held_lock *hlock, *prev_hlock;
+	struct lock_class_stats *stats;
 	unsigned int depth;
-	int i;
+	int i, point;
 
 	depth = curr->lockdep_depth;
 	if (DEBUG_LOCKS_WARN_ON(!depth))
@@ -2541,22 +2626,18 @@ __lock_contended(struct lockdep_map *loc
 found_it:
 	hlock->waittime_stamp = sched_clock();
 
+	point = lock_contention_point(hlock->class, ip);
+
+	stats = get_lock_stats(hlock->class);
+	if (point < ARRAY_SIZE(stats->contention_point))
+		stats->contention_point[i]++;
+
 	if (hlock->read)
-		atomic_inc(&hlock->class->read_contentions);
+		stats->read_contentions++;
 	else
-		atomic_inc(&hlock->class->write_contentions);
+		stats->write_contentions++;
 
-	for (i = 0; i < ARRAY_SIZE(hlock->class->contention_point); i++) {
-		if (hlock->class->contention_point[i].ip == 0) {
-			hlock->class->contention_point[i].ip = ip;
-			atomic_set(&hlock->class->contention_point[i].count, 1);
-			break;
-		}
-		if (hlock->class->contention_point[i].ip == ip) {
-			atomic_inc(&hlock->class->contention_point[i].count);
-			break;
-		}
-	}
+	put_lock_stats(stats);
 }
 
 static void
@@ -2564,6 +2645,7 @@ __lock_acquired(struct lockdep_map *lock
 {
 	struct task_struct *curr = current;
 	struct held_lock *hlock, *prev_hlock;
+	struct lock_class_stats *stats;
 	unsigned int depth;
 	unsigned long long now, waittime;
 	int i;
@@ -2596,10 +2678,14 @@ found_it:
 
 	hlock->holdtime_stamp = now;
 
+	stats = get_lock_stats(hlock->class);
+
 	if (hlock->read)
-		lock_time_add(&hlock->class->read_waittime, waittime);
+		lock_time_inc(&stats->read_waittime, waittime);
 	else
-		lock_time_add(&hlock->class->write_waittime, waittime);
+		lock_time_inc(&stats->write_waittime, waittime);
+
+	put_lock_stats(stats);
 }
 #endif
 
Index: linux-2.6/kernel/lockdep_proc.c
===================================================================
--- linux-2.6.orig/kernel/lockdep_proc.c	2007-05-23 10:23:16.000000000 +0200
+++ linux-2.6/kernel/lockdep_proc.c	2007-05-23 10:36:45.000000000 +0200
@@ -347,27 +347,28 @@ static const struct file_operations proc
 #ifdef CONFIG_LOCK_STAT
 static int lock_contentions_show(struct seq_file *m, void *v)
 {
-	struct lock_contention_point *cp;
 	char sym[KSYM_SYMBOL_LEN];
 	struct lock_class *class;
-	int r, w, i;
+	struct lock_class_stats stats;
+	int i;
 
 	list_for_each_entry(class, &all_lock_classes, lock_entry) {
-		r = atomic_read(&class->read_contentions);
-		w = atomic_read(&class->write_contentions);
+		stats = lock_stats(class);
+
+		if (stats.read_contentions || stats.write_contentions) {
+			seq_printf(m, "%s: %lu %lu", class->name,
+					stats.write_contentions,
+					stats.read_contentions);
 
-		if (r || w) {
-			seq_printf(m, "%s: %d %d", class->name, w, r);
 			for (i = 0; i < ARRAY_SIZE(class->contention_point);
 					i++) {
-				cp = &class->contention_point[i];
-
-				if (cp->ip == 0)
+				if (class->contention_point[i] == 0)
 					break;
-				sprint_symbol(sym, cp->ip);
-				seq_printf(m, " [%d] [<%p>] %s",
-						atomic_read(&cp->count),
-						(void *)cp->ip, sym);
+				sprint_symbol(sym, class->contention_point[i]);
+				seq_printf(m, " %lu [<%p>] %s",
+					stats.contention_point[i],
+					(void *)class->contention_point[i],
+					sym);
 			}
 			seq_printf(m, "\n");
 		}
@@ -395,14 +396,8 @@ ssize_t lock_contentions_write(struct fi
 		if (c != '0')
 			return count;
 
-		list_for_each_entry(class, &all_lock_classes, lock_entry) {
-			atomic_set(&class->write_contentions, 0);
-			atomic_set(&class->read_contentions, 0);
-			for (i = 0; i < ARRAY_SIZE(class->contention_point);
-					i++) {
-				class->contention_point[i].ip = 0;
-			}
-		}
+		list_for_each_entry(class, &all_lock_classes, lock_entry)
+			clear_lock_stats(class);
 	}
 	return count;
 }
@@ -417,42 +412,24 @@ static const struct file_operations proc
 
 static void print_time(struct seq_file *m, struct lock_time *lt)
 {
-	unsigned long long min, total, max;
-	unsigned long nr;
-
-	__raw_spin_lock(&lt->lock);
-	min = lt->min;
-	total = lt->total;
-	max = lt->max;
-	nr = lt->nr;
-	__raw_spin_unlock(&lt->lock);
-
-	seq_printf(m, " %lu %llu %llu %llu", nr, min, max, total);
-}
-
-static void clear_time(struct lock_time *lt)
-{
-	__raw_spin_lock(&lt->lock);
-	lt->min = 0;
-	lt->total = 0;
-	lt->max = 0;
-	lt->nr = 0;
-	__raw_spin_unlock(&lt->lock);
+	seq_printf(m, " %lu %llu %llu %llu",
+			lt->nr, lt->min, lt->max, lt->total);
 }
 
 static int lock_waittime_show(struct seq_file *m, void *v)
 {
 	struct lock_class *class;
-	int r, w;
+	struct lock_class_stats stats;
 
 	list_for_each_entry(class, &all_lock_classes, lock_entry) {
-		r = atomic_read(&class->read_contentions);
-		w = atomic_read(&class->write_contentions);
+		stats = lock_stats(class);
 
-		if (r || w) {
-			seq_printf(m, "%s: %d %d", class->name, w, r);
-			print_time(m, &class->write_waittime);
-			print_time(m, &class->read_waittime);
+		if (stats.read_contentions || stats.write_contentions) {
+			seq_printf(m, "%s: %lu %lu", class->name,
+					stats.write_contentions,
+					stats.read_contentions);
+			print_time(m, &stats.write_waittime);
+			print_time(m, &stats.read_waittime);
 			seq_printf(m, "\n");
 		}
 	}
@@ -465,30 +442,9 @@ static int lock_waittime_open(struct ino
 	return single_open(file, lock_waittime_show, NULL);
 }
 
-ssize_t lock_waittime_write(struct file *file, const char __user *buf,
-		size_t count, loff_t *ppos)
-{
-	struct lock_class *class;
-	char c;
-
-	if (count) {
-		if (get_user(c, buf))
-			return -EFAULT;
-
-		if (c != '0')
-			return count;
-
-		list_for_each_entry(class, &all_lock_classes, lock_entry) {
-			clear_time(&class->read_waittime);
-			clear_time(&class->write_waittime);
-		}
-	}
-	return count;
-}
-
 static const struct file_operations proc_lock_waittime_operations = {
 	.open		= lock_waittime_open,
-	.write		= lock_waittime_write,
+	.write		= lock_contentions_write,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
 	.release	= seq_release,
@@ -497,16 +453,17 @@ static const struct file_operations proc
 static int lock_holdtime_show(struct seq_file *m, void *v)
 {
 	struct lock_class *class;
-	int r, w;
+	struct lock_class_stats stats;
 
 	list_for_each_entry(class, &all_lock_classes, lock_entry) {
-		r = atomic_read(&class->read_contentions);
-		w = atomic_read(&class->write_contentions);
+		stats = lock_stats(class);
 
-		if (r || w) {
-			seq_printf(m, "%s: %d %d", class->name, w, r);
-			print_time(m, &class->write_holdtime);
-			print_time(m, &class->read_holdtime);
+		if (stats.read_contentions || stats.write_contentions) {
+			seq_printf(m, "%s: %lu %lu", class->name,
+					stats.write_contentions,
+					stats.read_contentions);
+			print_time(m, &stats.write_holdtime);
+			print_time(m, &stats.read_holdtime);
 			seq_printf(m, "\n");
 		}
 	}
@@ -519,30 +476,9 @@ static int lock_holdtime_open(struct ino
 	return single_open(file, lock_holdtime_show, NULL);
 }
 
-ssize_t lock_holdtime_write(struct file *file, const char __user *buf,
-		size_t count, loff_t *ppos)
-{
-	struct lock_class *class;
-	char c;
-
-	if (count) {
-		if (get_user(c, buf))
-			return -EFAULT;
-
-		if (c != '0')
-			return count;
-
-		list_for_each_entry(class, &all_lock_classes, lock_entry) {
-			clear_time(&class->read_holdtime);
-			clear_time(&class->write_holdtime);
-		}
-	}
-	return count;
-}
-
 static const struct file_operations proc_lock_holdtime_operations = {
 	.open		= lock_holdtime_open,
-	.write		= lock_holdtime_write,
+	.write		= lock_contentions_write,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
 	.release	= seq_release,

-- 

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux