View Incident: http://co-op.engr.sgi.com/BugWorks/query.cgi/903582

Status: open                          Priority: 1                           
Assigned Engineer: mort               Submitter: steiner                    
Assigned Group: linux-kernel          Project: communitylinux               
Opened Date: 11/01/03                 Description:

fs/ext2/super.c wont compile if NR_CPUS is 512. There is a kmalloc in
ext2_fill_super() that  tries to allocate a structure that is larger than 
the max kmalloc size supported for IA64. (The code actually compiles but
generates a reference to a non-existent function. This is how the error 
is reported). The structure being allocated has several arrays that
have NR_CPUS as a dimension.

One solution would be to use vmalloc to allocate the structure. vmalloc
does not have a restriction on the size allocated. Before taking this 

.....

==========================
ADDITIONAL INFORMATION (ADD)
From: mort@sgi.com (BugWorks)
Date: Nov 10 2003 06:05:21AM
==========================

So here is the patch that ended up pleasing people.  Andrew Morton
has put this in his queue to integrate for 2.6.1 (yay!)

The patch fixes the underlying problem in the percpu_counters structure
by using alloc_percpu() to create the memory for the counters instead
of using a big-ass array.

Any comments?

I encourage some people to try this out.  I tried it on my system and
it seemed to work.  I had some printks in there to make sure that reasonable
numbers were being stored in the counter and everything looked fine to me.

Filesystem changes make me nervous :-)

The patch is at:  tomahawk:~mort/patches/percpu_counters-update.diff



# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.1357  -> 1.1358 
#	lib/percpu_counter.c	1.3     -> 1.4    
#	include/linux/percpu_counter.h	1.2     -> 1.3    
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/11/07	mort@green.i.bork.org	1.1358
# percpu_counters-update.diff
# --------------------------------------------
#
diff -Nru a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h
--- a/include/linux/percpu_counter.h	Fri Nov  7 13:30:04 2003
+++ b/include/linux/percpu_counter.h	Fri Nov  7 13:30:04 2003
@@ -8,6 +8,7 @@
 #include <linux/spinlock.h>
 #include <linux/smp.h>
 #include <linux/threads.h>
+#include <linux/percpu.h>
 
 #ifdef CONFIG_SMP
 
@@ -18,7 +19,7 @@
 struct percpu_counter {
 	spinlock_t lock;
 	long count;
-	struct __percpu_counter counters[NR_CPUS];
+	struct __percpu_counter *counters;
 };
 
 #if NR_CPUS >= 16
@@ -29,12 +30,14 @@
 
 static inline void percpu_counter_init(struct percpu_counter *fbc)
 {
-	int i;
-
 	spin_lock_init(&fbc->lock);
 	fbc->count = 0;
-	for (i = 0; i < NR_CPUS; i++)
-		fbc->counters[i].count = 0;
+	fbc->counters = alloc_percpu(struct __percpu_counter);
+}
+
+static inline void percpu_counter_destroy(struct percpu_counter *fbc)
+{
+	free_percpu(fbc->counters);
 }
 
 void percpu_counter_mod(struct percpu_counter *fbc, long amount);
@@ -67,6 +70,10 @@
 static inline void percpu_counter_init(struct percpu_counter *fbc)
 {
 	fbc->count = 0;
+}
+
+static inline void percpu_counter_destroy(struct percpu_counter *fbc)
+{
 }
 
 static inline void
diff -Nru a/lib/percpu_counter.c b/lib/percpu_counter.c
--- a/lib/percpu_counter.c	Fri Nov  7 13:30:04 2003
+++ b/lib/percpu_counter.c	Fri Nov  7 13:30:04 2003
@@ -4,9 +4,10 @@
 
 void percpu_counter_mod(struct percpu_counter *fbc, long amount)
 {
-	int cpu = get_cpu();
-	long count = fbc->counters[cpu].count;
+	long count;
 
+	count = *(long *)get_cpu_ptr(fbc->counters);
+	put_cpu_ptr();
 	count += amount;
 	if (count >= FBC_BATCH || count <= -FBC_BATCH) {
 		spin_lock(&fbc->lock);
@@ -14,8 +15,8 @@
 		spin_unlock(&fbc->lock);
 		count = 0;
 	}
-	fbc->counters[cpu].count = count;
-	put_cpu();
+	*(long *)get_cpu_ptr(fbc->counters) = count;
+	put_cpu_ptr();
 }
 
 EXPORT_SYMBOL(percpu_counter_mod);
