RE: Limit hash table size

Chen, Kenneth W
2004-02-17T22:30:55Z

[-- Attachment #1: Type: text/plain, Size: 959 bytes --]

OK, here is another revision on top of what has been discussed. It adds
4 boot time parameters so user can override default size as needed to
suite special needs. I will sent a separate patch for
kernel-parameters.txt if everyone is OK with this one.

- Ken

-----Original Message-----
From: Andrew Morton [mailto:akpm@osdl.org]
Sent: Thursday, February 05, 2004 3:58 PM
To: Chen, Kenneth W
Cc: linux-kernel@vger.kernel.org; linux-ia64@vger.kernel.org
Subject: Re: Limit hash table size

Ken, I remain unhappy with this patch. If a big box has 500 million
dentries or inodes in cache (is possible), those hash chains will be
more
than 200 entries long on average. It will be very slow.

We need to do something smarter. At least, for machines which do not
have
the ia64 proliferation-of-zones problem.

Maybe we should leave the sizing of these tables as-is, and add some
hook
which allows the architecture to scale them back.

[-- Attachment #2: hash4.patch --]
[-- Type: application/octet-stream, Size: 4596 bytes --]

diff -Nur linux-2.6.3-rc4/fs/dcache.c linux-2.6.3-rc4.hash/fs/dcache.c
--- linux-2.6.3-rc4/fs/dcache.c 2004-02-16 18:21:54.000000000 -0800
+++ linux-2.6.3-rc4.hash/fs/dcache.c 2004-02-17 14:03:21.000000000 -0800
@@ -49,6 +49,7 @@
*/
#define D_HASHBITS d_hash_shift
#define D_HASHMASK d_hash_mask
+#define D_HASHMAX (2*1024*1024UL) /* max number of entries */

static unsigned int d_hash_mask;
static unsigned int d_hash_shift;
@@ -1531,6 +1532,16 @@
return ino;
}

+static __initdata unsigned long dhash_entries;
+static int __init set_dhash_entries(char *str)
+{
+ if (!str)
+ return 0;
+ dhash_entries = simple_strtoul(str, &str, 0);
+ return 1;
+}
+__setup("dhash_entries=", set_dhash_entries);
+
static void __init dcache_init(unsigned long mempages)
{
struct hlist_head *d;
@@ -1556,11 +1567,14 @@

set_shrinker(DEFAULT_SEEKS, shrink_dcache_memory);

-#if PAGE_SHIFT < 13
- mempages >>= (13 - PAGE_SHIFT);
-#endif
- mempages *= sizeof(struct hlist_head);
- for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++)
+ if (!dhash_entries) {
+ dhash_entries = PAGE_SHIFT < 13 ?
+ mempages >> (13 - PAGE_SHIFT) :
+ mempages << (PAGE_SHIFT - 13);
+ dhash_entries = min(D_HASHMAX, dhash_entries);
+ }
+ dhash_entries *= sizeof(struct hlist_head);
+ for (order = 0; ((1UL << order) << PAGE_SHIFT) < dhash_entries; order++)
;

do {
diff -Nur linux-2.6.3-rc4/fs/inode.c linux-2.6.3-rc4.hash/fs/inode.c
--- linux-2.6.3-rc4/fs/inode.c 2004-02-16 18:23:36.000000000 -0800
+++ linux-2.6.3-rc4.hash/fs/inode.c 2004-02-17 14:03:21.000000000 -0800
@@ -53,6 +53,7 @@
*/
#define I_HASHBITS i_hash_shift
#define I_HASHMASK i_hash_mask
+#define I_HASHMAX (2*1024*1024UL) /* max number of entries */

static unsigned int i_hash_mask;
static unsigned int i_hash_shift;
@@ -1327,6 +1328,16 @@
wake_up_all(wq);
}

+static __initdata unsigned long ihash_entries;
+static int __init set_ihash_entries(char *str)
+{
+ if (!str)
+ return 0;
+ ihash_entries = simple_strtoul(str, &str, 0);
+ return 1;
+}
+__setup("ihash_entries=", set_ihash_entries);
+
/*
* Initialize the waitqueues and inode hash table.
*/
@@ -1340,9 +1351,14 @@
for (i = 0; i < ARRAY_SIZE(i_wait_queue_heads); i++)
init_waitqueue_head(&i_wait_queue_heads[i].wqh);

- mempages >>= (14 - PAGE_SHIFT);
- mempages *= sizeof(struct hlist_head);
- for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++)
+ if (!ihash_entries) {
+ ihash_entries = PAGE_SHIFT < 14 ?
+ mempages >> (14 - PAGE_SHIFT) :
+ mempages << (PAGE_SHIFT - 14);
+ ihash_entries = min(I_HASHMAX, ihash_entries);
+ }
+ ihash_entries *= sizeof(struct hlist_head);
+ for (order = 0; ((1UL << order) << PAGE_SHIFT) < ihash_entries; order++)
;

do {
diff -Nur linux-2.6.3-rc4/net/ipv4/route.c linux-2.6.3-rc4.hash/net/ipv4/route.c
--- linux-2.6.3-rc4/net/ipv4/route.c 2004-02-16 18:23:37.000000000 -0800
+++ linux-2.6.3-rc4.hash/net/ipv4/route.c 2004-02-17 14:03:21.000000000 -0800
@@ -2717,6 +2717,16 @@
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_NET_CLS_ROUTE */

+static __initdata unsigned long rhash_entries;
+static int __init set_rhash_entries(char *str)
+{
+ if (!str)
+ return 0;
+ rhash_entries = simple_strtoul(str, &str, 0);
+ return 1;
+}
+__setup("rhash_entries=", set_rhash_entries);
+
int __init ip_rt_init(void)
{
int i, order, goal, rc = 0;
@@ -2743,7 +2753,10 @@
panic("IP: failed to allocate ip_dst_cache\n");

goal = num_physpages >> (26 - PAGE_SHIFT);
-
+ if (!rhash_entries)
+ goal = min(10, goal);
+ else
+ goal = (rhash_entries * sizeof(struct rt_hash_bucket)) >> PAGE_SHIFT;
for (order = 0; (1UL << order) < goal; order++)
/* NOTHING */;

diff -Nur linux-2.6.3-rc4/net/ipv4/tcp.c linux-2.6.3-rc4.hash/net/ipv4/tcp.c
--- linux-2.6.3-rc4/net/ipv4/tcp.c 2004-02-16 18:22:05.000000000 -0800
+++ linux-2.6.3-rc4.hash/net/ipv4/tcp.c 2004-02-17 14:03:21.000000000 -0800
@@ -2570,6 +2570,16 @@
extern void __skb_cb_too_small_for_tcp(int, int);
extern void tcpdiag_init(void);

+static __initdata unsigned long thash_entries;
+static int __init set_thash_entries(char *str)
+{
+ if (!str)
+ return 0;
+ thash_entries = simple_strtoul(str, &str, 0);
+ return 1;
+}
+__setup("thash_entries=", set_thash_entries);
+
void __init tcp_init(void)
{
struct sk_buff *skb = NULL;
@@ -2611,6 +2621,10 @@
else
goal = num_physpages >> (23 - PAGE_SHIFT);

+ if (!thash_entries)
+ goal = min(10, goal);
+ else
+ goal = (thash_entries * sizeof(struct tcp_ehash_bucket)) >> PAGE_SHIFT;
for (order = 0; (1UL << order) < goal; order++)
;
do {