diff -rux Makefile -x config.log mysql-5.0.83-orig/client/mysql_priv.h mysql-5.0.83-fix/client/mysql_priv.h
--- mysql-5.0.83-orig/client/mysql_priv.h	2009-05-29 20:19:25.000000000 +0200
+++ mysql-5.0.83-fix/client/mysql_priv.h	2009-07-27 20:24:34.143544101 +0200
@@ -1350,6 +1350,7 @@
 extern ulong max_binlog_size, max_relay_log_size;
 extern ulong rpl_recovery_rank, thread_cache_size;
 extern ulong back_log;
+extern uint  keycache_segments;
 extern ulong specialflag, current_pid;
 extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter;
 extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size;
diff -rux Makefile -x config.log mysql-5.0.83-orig/include/keycache.h mysql-5.0.83-fix/include/keycache.h
--- mysql-5.0.83-orig/include/keycache.h	2009-05-29 20:15:56.000000000 +0200
+++ mysql-5.0.83-fix/include/keycache.h	2009-07-27 20:23:45.813544077 +0200
@@ -35,13 +35,14 @@
 } KEYCACHE_WQUEUE;
 
 #define CHANGED_BLOCKS_HASH 128             /* must be power of 2 */
+#define DEFAULT_KEYCACHE_SEGMENTS 1
 
 /*
   The key cache structure
   It also contains read-only statistics parameters.
 */   
 
-typedef struct st_key_cache
+typedef struct st_r_key_cache
 {
   my_bool key_cache_inited;
   my_bool resize_in_flush;       /* true during flush of resize operation    */
@@ -76,25 +77,47 @@
   BLOCK_LINK *changed_blocks[CHANGED_BLOCKS_HASH]; /* hash for dirty file bl.*/
   BLOCK_LINK *file_blocks[CHANGED_BLOCKS_HASH];    /* hash for other file bl.*/
 
+  /* Statistics variables. These are reset in reset_key_cache_counters(). */
+  ulong global_blocks_changed;	/* number of currently dirty blocks         */
+  ulonglong global_cache_w_requests;/* number of write requests (write hits) */
+  ulonglong global_cache_write;     /* number of writes from cache to files  */
+  ulonglong global_cache_r_requests;/* number of read requests (read hits)   */
+  ulonglong global_cache_read;      /* number of reads from files to cache   */
+
+  int blocks;                   /* max number of blocks in the cache        */
+  my_bool in_init;		/* Set to 1 in MySQL during init/resize     */
+} R_KEY_CACHE;
+
+typedef struct st_key_cache
+{
+  R_KEY_CACHE *key_cache; /* holds the cache segments */
+  pthread_mutex_t status_lock;                    /* lock for updating status */
+
+  uint key_cache_block_size;     /* size of the page buffer of a cache block */
   /*
     The following variables are and variables used to hold parameters for
     initializing the key cache.
   */
 
+  size_t key_cache_mem_size;     /* specified size of the cache memory       */
+
   ulonglong param_buff_size;    /* size the memory allocated for the cache  */
   ulong param_block_size;       /* size of the blocks in the key cache      */
   ulong param_division_limit;   /* min. percentage of warm blocks           */
   ulong param_age_threshold;    /* determines when hot block is downgraded  */
 
+  my_bool key_cache_inited;
+  my_bool in_init;		/* Set to 1 in MySQL during init/resize     */
+
+
+  ulong blocks_used; /* maximum number of concurrently used blocks */
+  ulong blocks_unused; /* number of currently unused blocks */
   /* Statistics variables. These are reset in reset_key_cache_counters(). */
   ulong global_blocks_changed;	/* number of currently dirty blocks         */
   ulonglong global_cache_w_requests;/* number of write requests (write hits) */
   ulonglong global_cache_write;     /* number of writes from cache to files  */
   ulonglong global_cache_r_requests;/* number of read requests (read hits)   */
   ulonglong global_cache_read;      /* number of reads from files to cache   */
-
-  int blocks;                   /* max number of blocks in the cache        */
-  my_bool in_init;		/* Set to 1 in MySQL during init/resize     */
 } KEY_CACHE;
 
 /* The default key cache */
@@ -102,7 +125,7 @@
 
 extern int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
                           size_t use_mem, uint division_limit,
-                          uint age_threshold);
+                          uint age_threshold, uint segments);
 extern int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
                             size_t use_mem, uint division_limit,
                             uint age_threshold);
@@ -122,6 +145,8 @@
 extern int flush_key_blocks(KEY_CACHE *keycache,
                             int file, enum flush_type type);
 extern void end_key_cache(KEY_CACHE *keycache, my_bool cleanup);
+extern void update_key_cache_status(KEY_CACHE *keycache);
+
 
 /* Functions to handle multiple key caches */
 extern my_bool multi_keycache_init(void);
diff -rux Makefile -x config.log mysql-5.0.83-orig/myisam/mi_check.c mysql-5.0.83-fix/myisam/mi_check.c
--- mysql-5.0.83-orig/myisam/mi_check.c	2009-05-29 20:16:19.000000000 +0200
+++ mysql-5.0.83-fix/myisam/mi_check.c	2009-07-03 02:21:47.983544129 +0200
@@ -1545,7 +1545,7 @@
 
   if (!param->using_global_keycache)
     VOID(init_key_cache(dflt_key_cache, param->key_cache_block_size,
-                        param->use_buffers, 0, 0));
+                        param->use_buffers, 0, 0, 1));
 
   if (init_io_cache(&param->read_cache,info->dfile,
 		    (uint) param->read_buffer_length,
diff -rux Makefile -x config.log mysql-5.0.83-orig/myisam/mi_test1.c mysql-5.0.83-fix/myisam/mi_test1.c
--- mysql-5.0.83-orig/myisam/mi_test1.c	2009-05-29 20:16:20.000000000 +0200
+++ mysql-5.0.83-fix/myisam/mi_test1.c	2009-07-27 17:32:09.253544090 +0200
@@ -49,7 +49,7 @@
   MY_INIT(argv[0]);
   my_init();
   if (key_cacheing)
-    init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,IO_SIZE*16,0,0);
+    init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,IO_SIZE*16,0,0,DEFAULT_KEYCACHE_SEGMENTS);
   get_options(argc,argv);
 
   exit(run_test("test1"));
diff -rux Makefile -x config.log mysql-5.0.83-orig/myisam/mi_test2.c mysql-5.0.83-fix/myisam/mi_test2.c
--- mysql-5.0.83-orig/myisam/mi_test2.c	2009-05-29 20:16:20.000000000 +0200
+++ mysql-5.0.83-fix/myisam/mi_test2.c	2009-07-27 17:31:53.923543935 +0200
@@ -214,7 +214,7 @@
   if (!silent)
     printf("- Writing key:s\n");
   if (key_cacheing)
-    init_key_cache(dflt_key_cache,key_cache_block_size,key_cache_size,0,0);
+    init_key_cache(dflt_key_cache,key_cache_block_size,key_cache_size,0,0,DEFAULT_KEYCACHE_SEGMENTS);
   if (locking)
     mi_lock_database(file,F_WRLCK);
   if (write_cacheing)
@@ -830,6 +830,7 @@
       puts("Locking used");
     if (use_blob)
       puts("blobs used");
+    update_key_cache_status(dflt_key_cache);
     printf("key cache status: \n\
 blocks used:%10lu\n\
 not flushed:%10lu\n\
diff -rux Makefile -x config.log mysql-5.0.83-orig/myisam/mi_test3.c mysql-5.0.83-fix/myisam/mi_test3.c
--- mysql-5.0.83-orig/myisam/mi_test3.c	2009-05-29 20:16:20.000000000 +0200
+++ mysql-5.0.83-fix/myisam/mi_test3.c	2009-07-27 17:31:34.123544245 +0200
@@ -175,7 +175,7 @@
     exit(1);
   }
   if (key_cacheing && rnd(2) == 0)
-    init_key_cache(dflt_key_cache, KEY_CACHE_BLOCK_SIZE, 65536L, 0, 0);
+    init_key_cache(dflt_key_cache, KEY_CACHE_BLOCK_SIZE, 65536L, 0, 0, DEFAULT_KEYCACHE_SEGMENTS);
   printf("Process %d, pid: %d\n",id,getpid()); fflush(stdout);
 
   for (error=i=0 ; i < tests && !error; i++)
diff -rux Makefile -x config.log mysql-5.0.83-orig/myisam/myisamchk.c mysql-5.0.83-fix/myisam/myisamchk.c
--- mysql-5.0.83-orig/myisam/myisamchk.c	2009-05-29 20:16:21.000000000 +0200
+++ mysql-5.0.83-fix/myisam/myisamchk.c	2009-07-27 17:32:30.303544121 +0200
@@ -1107,7 +1107,7 @@
       {
 	if (param->testflag & (T_EXTEND | T_MEDIUM))
 	  VOID(init_key_cache(dflt_key_cache,opt_key_cache_block_size,
-                              param->use_buffers, 0, 0));
+                              param->use_buffers, 0, 0, DEFAULT_KEYCACHE_SEGMENTS));
 	VOID(init_io_cache(&param->read_cache,datafile,
 			   (uint) param->read_buffer_length,
 			   READ_CACHE,
@@ -1531,7 +1531,7 @@
     DBUG_RETURN(0);				/* Nothing to do */
 
   init_key_cache(dflt_key_cache, opt_key_cache_block_size, param->use_buffers,
-                 0, 0);
+                 0, 0, 1);
   if (init_io_cache(&info->rec_cache,-1,(uint) param->write_buffer_length,
 		   WRITE_CACHE,share->pack.header_length,1,
 		   MYF(MY_WME | MY_WAIT_IF_FULL)))
diff -rux Makefile -x config.log mysql-5.0.83-orig/myisam/myisam_ftdump.c mysql-5.0.83-fix/myisam/myisam_ftdump.c
--- mysql-5.0.83-orig/myisam/myisam_ftdump.c	2009-05-29 20:16:21.000000000 +0200
+++ mysql-5.0.83-fix/myisam/myisam_ftdump.c	2009-07-27 17:32:51.233544084 +0200
@@ -83,7 +83,7 @@
       usage();
   }
 
-  init_key_cache(dflt_key_cache,MI_KEY_BLOCK_LENGTH,USE_BUFFER_INIT, 0, 0);
+  init_key_cache(dflt_key_cache,MI_KEY_BLOCK_LENGTH,USE_BUFFER_INIT, 0, 0, DEFAULT_KEYCACHE_SEGMENTS);
 
   if (!(info=mi_open(argv[0],2,HA_OPEN_ABORT_IF_LOCKED)))
   {
diff -rux Makefile -x config.log mysql-5.0.83-orig/myisam/myisamlog.c mysql-5.0.83-fix/myisam/myisamlog.c
--- mysql-5.0.83-orig/myisam/myisamlog.c	2009-05-29 20:16:21.000000000 +0200
+++ mysql-5.0.83-fix/myisam/myisamlog.c	2009-07-03 02:23:46.843544079 +0200
@@ -331,7 +331,7 @@
   init_tree(&tree,0,0,sizeof(file_info),(qsort_cmp2) file_info_compare,1,
 	    (tree_element_free) file_info_free, NULL);
   VOID(init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,KEY_CACHE_SIZE,
-                      0, 0));
+                      0, 0, 1));
 
   files_open=0; access_time=0;
   while (access_time++ != number_of_commands &&
diff -rux Makefile -x config.log mysql-5.0.83-orig/mysys/mf_keycache.c mysql-5.0.83-fix/mysys/mf_keycache.c
--- mysql-5.0.83-orig/mysys/mf_keycache.c	2009-05-29 20:18:17.000000000 +0200
+++ mysql-5.0.83-fix/mysys/mf_keycache.c	2009-08-10 14:04:02.843544129 +0200
@@ -159,19 +159,55 @@
 
 #define FLUSH_CACHE         2000            /* sort this many blocks at once */
 
-static int flush_all_key_blocks(KEY_CACHE *keycache);
+static int flush_all_key_blocks(R_KEY_CACHE *keycache);
 #ifdef THREAD
 static void link_into_queue(KEYCACHE_WQUEUE *wqueue,
                                    struct st_my_thread_var *thread);
 static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
                                      struct st_my_thread_var *thread);
 #endif
-static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block);
+static void free_block(R_KEY_CACHE *keycache, BLOCK_LINK *block);
 #ifndef DBUG_OFF
-static void test_key_cache(KEY_CACHE *keycache,
+static void test_key_cache(R_KEY_CACHE *keycache,
                            const char *where, my_bool lock);
 #endif
 
+static BLOCK_LINK *r_find_key_block(R_KEY_CACHE *keycache,
+                                  File file, my_off_t filepos,
+                                  int init_hits_left,
+                                  int wrmode, int *page_st);
+int r_reset_key_cache_counters(const char *name __attribute__((unused)),
+                             R_KEY_CACHE *key_cache);
+/* functions that operate on real caches*/
+int r_init_key_cache(R_KEY_CACHE *keycache, uint key_cache_block_size,
+			  size_t use_mem, uint division_limit,
+			  uint age_threshold);
+int r_resize_key_cache(R_KEY_CACHE *keycache, uint key_cache_block_size,
+			    size_t use_mem, uint division_limit,
+			    uint age_threshold);
+void r_change_key_cache_param(R_KEY_CACHE *keycache, uint division_limit,
+				   uint age_threshold);
+uint r_key_cache_read(R_KEY_CACHE *keycache,
+                            File file, my_off_t filepos, int level,
+                            byte *buff, uint length,
+			    uint block_length,int return_buffer);
+int r_key_cache_insert(R_KEY_CACHE *keycache,
+                            File file, my_off_t filepos, int level,
+                            byte *buff, uint length);
+int r_key_cache_write(R_KEY_CACHE *keycache,
+                           File file, my_off_t filepos, int level,
+                           byte *buff, uint length,
+			   uint block_length,int force_write);
+int r_flush_key_blocks(R_KEY_CACHE *keycache,
+                            int file, enum flush_type type);
+void r_end_key_cache(R_KEY_CACHE *keycache, my_bool cleanup);
+
+uint keycache_segments = 1;
+
+//XXX - peter, by limiting the block size this could be made a shift rather than an idiv
+#define MKC_HASH(f, pos)                                                      \
+(((ulong) ((pos) / keycache->key_cache_block_size) +                              \
+                              (ulong) (f)) & (keycache_segments-1))
 #define KEYCACHE_HASH(f, pos)                                                 \
 (((ulong) ((pos) / keycache->key_cache_block_size) +                          \
                                      (ulong) (f)) & (keycache->hash_entries-1))
@@ -274,6 +310,119 @@
   return (old_value << 1);
 }
 
+void update_key_cache_status(KEY_CACHE *keycache)
+{
+      int i, overflow;
+      R_KEY_CACHE *kc;
+
+      keycache_pthread_mutex_lock(&keycache->status_lock);
+
+      keycache->blocks_used = 0;
+      keycache->blocks_unused = 0;
+      keycache->global_blocks_changed = 0;
+      keycache->global_cache_w_requests = 0;
+      keycache->global_cache_write = 0;
+      keycache->global_cache_r_requests = 0;
+      keycache->global_cache_read = 0;
+
+      /* XXX - peter
+       * can overflow. not anymore, now it's probably the ugliest bit of code ever written instead.
+       */
+      for (i = 0; i < keycache_segments; i++) {
+              kc = &keycache->key_cache[i];
+              overflow = ULONG_MAX - keycache->blocks_used;
+              if (kc->blocks_used > overflow) {
+                  keycache->blocks_used = kc->blocks_used - overflow;
+              } else {
+	          keycache->blocks_used += kc->blocks_used;
+              }
+
+              overflow = ULONG_MAX - keycache->blocks_unused;
+              if (kc->blocks_unused > overflow) {
+                  keycache->blocks_unused = kc->blocks_unused - overflow;
+              } else {
+	          keycache->blocks_unused += kc->blocks_unused;
+              }
+
+              overflow = ULONGLONG_MAX - keycache->global_blocks_changed;
+              if (kc->global_blocks_changed > overflow) {
+                  keycache->global_blocks_changed = kc->global_blocks_changed - overflow;
+              } else {
+	          keycache->global_blocks_changed += kc->global_blocks_changed;
+              }
+
+              overflow = ULONGLONG_MAX - keycache->global_cache_w_requests;
+              if (kc->global_cache_w_requests > overflow) {
+                  keycache->global_cache_w_requests = kc->global_cache_w_requests - overflow;
+              } else {
+	          keycache->global_cache_w_requests += kc->global_cache_w_requests;
+              }
+
+              overflow = ULONGLONG_MAX - keycache->global_cache_write;
+              if (kc->global_cache_write > overflow) {
+                  keycache->global_cache_write = kc->global_cache_write - overflow;
+              } else {
+	          keycache->global_cache_write += kc->global_cache_write;
+              }
+
+              overflow = ULONGLONG_MAX - keycache->global_cache_r_requests;
+              if (kc->global_cache_r_requests > overflow) {
+                  keycache->global_cache_r_requests = kc->global_cache_r_requests - overflow;
+              } else {
+	          keycache->global_cache_r_requests += kc->global_cache_r_requests;
+              }
+
+              overflow = ULONGLONG_MAX - keycache->global_cache_read;
+              if (kc->global_cache_read > overflow) {
+                  keycache->global_cache_read = kc->global_cache_read - overflow;
+              } else {
+	          keycache->global_cache_read += kc->global_cache_read;
+              }
+      }
+
+      keycache_pthread_mutex_unlock(&keycache->status_lock);
+}
+
+int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
+                 size_t use_mem, uint division_limit,
+                 uint age_threshold, uint segments)
+{
+      size_t mem_per_cache = 0;
+      int ret = 0;
+      int i, j;
+
+      keycache->in_init = 1;
+      keycache->key_cache_block_size = key_cache_block_size;
+      keycache->key_cache_mem_size = use_mem;
+      keycache_segments = next_power(segments);
+      if (keycache_segments != segments) {
+          fprintf(stderr, "Changed specified key_cache_segments %d to %d in order to make it a power of 2\n", segments, keycache_segments);
+      }
+      mem_per_cache = use_mem / keycache_segments;
+
+      if (!keycache->key_cache_inited) {
+	      keycache->key_cache = (R_KEY_CACHE*) my_malloc(sizeof(R_KEY_CACHE) * keycache_segments, MY_ZEROFILL);
+	      pthread_mutex_init(&keycache->status_lock, MY_MUTEX_INIT_FAST);
+	      keycache->key_cache_inited = 1;
+      }
+      for (i = 0; i < keycache_segments; i++) {
+              int blocks = r_init_key_cache(&keycache->key_cache[i], 
+                      key_cache_block_size, mem_per_cache, 
+                      division_limit, age_threshold);
+              if (blocks == 0) {
+                      /* failure, cleanup already inited cache segments and bail */
+                      for (j = 0; j < i; j++) { 
+                              r_end_key_cache(&keycache->key_cache[j], 1); 
+                      }
+                      DBUG_RETURN(0);
+              } else {
+                      ret += blocks;
+              }
+      }
+      keycache->in_init = 0;
+
+     DBUG_RETURN(ret);
+}
 
 /*
   Initialize a key cache
@@ -300,7 +449,7 @@
 
 */
 
-int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
+int r_init_key_cache(R_KEY_CACHE *keycache, uint key_cache_block_size,
                    size_t use_mem, uint division_limit,
                    uint age_threshold)
 {
@@ -450,6 +599,30 @@
   DBUG_RETURN(0);
 }
 
+int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
+		     size_t use_mem, uint division_limit,
+		     uint age_threshold)
+{
+	int mem_per_cache = use_mem / keycache_segments;
+	int ret = 0;
+	int i,j;
+
+	keycache->key_cache_block_size = key_cache_block_size;
+
+	for (i = 0; i < keycache_segments; i++) {
+		int blocks = r_resize_key_cache(&keycache->key_cache[i],
+			key_cache_block_size, mem_per_cache,
+			division_limit, age_threshold);
+		if (blocks == 0) {
+                      for (j = 0; j < i; j++) { 
+                              r_end_key_cache(&keycache->key_cache[j], 1); 
+                      }
+                      DBUG_RETURN(0);
+		}
+		ret += blocks;
+	}
+	DBUG_RETURN(ret);
+}
 
 /*
   Resize a key cache
@@ -480,7 +653,7 @@
     (when cnt_for_resize=0).
 */
 
-int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
+int r_resize_key_cache(R_KEY_CACHE *keycache, uint key_cache_block_size,
                      size_t use_mem, uint division_limit,
                      uint age_threshold)
 {
@@ -495,7 +668,7 @@
   if(key_cache_block_size == keycache->key_cache_block_size &&
      use_mem == keycache->key_cache_mem_size)
   {
-    change_key_cache_param(keycache, division_limit, age_threshold);
+    r_change_key_cache_param(keycache, division_limit, age_threshold);
     DBUG_RETURN(keycache->disk_blocks);
   }
 
@@ -534,9 +707,9 @@
   KEYCACHE_DBUG_ASSERT(keycache->cnt_for_resize_op == 0);
 #endif
 
-  end_key_cache(keycache, 0);			/* Don't free mutex */
+  r_end_key_cache(keycache, 0);			/* Don't free mutex */
   /* The following will work even if use_mem is 0 */
-  blocks= init_key_cache(keycache, key_cache_block_size, use_mem,
+  blocks= r_init_key_cache(keycache, key_cache_block_size, use_mem,
 			 division_limit, age_threshold);
 
 finish:
@@ -558,7 +731,7 @@
 /*
   Increment counter blocking resize key cache operation
 */
-static inline void inc_counter_for_resize_op(KEY_CACHE *keycache)
+static inline void inc_counter_for_resize_op(R_KEY_CACHE *keycache)
 {
   keycache->cnt_for_resize_op++;
 }
@@ -568,7 +741,7 @@
   Decrement counter blocking resize key cache operation;
   Signal the operation to proceed when counter becomes equal zero
 */
-static inline void dec_counter_for_resize_op(KEY_CACHE *keycache)
+static inline void dec_counter_for_resize_op(R_KEY_CACHE *keycache)
 {
 #ifdef THREAD
   struct st_my_thread_var *last_thread;
@@ -584,6 +757,18 @@
 #endif
 }
 
+void change_key_cache_param(KEY_CACHE *keycache, uint division_limit,
+                          uint age_threshold)
+{
+      int i;
+      DBUG_ENTER("change_key_cache_param");
+      for (i = 0; i < keycache_segments; i++) {
+              r_change_key_cache_param(&keycache->key_cache[i], division_limit,
+                      age_threshold);
+      }
+      DBUG_VOID_RETURN;
+}
+
 /*
   Change the key cache parameters
 
@@ -602,10 +787,10 @@
     age_threshold.
 */
 
-void change_key_cache_param(KEY_CACHE *keycache, uint division_limit,
+void r_change_key_cache_param(R_KEY_CACHE *keycache, uint division_limit,
 			    uint age_threshold)
 {
-  DBUG_ENTER("change_key_cache_param");
+  DBUG_ENTER("r_change_key_cache_param");
 
   keycache_pthread_mutex_lock(&keycache->cache_lock);
   if (division_limit)
@@ -619,6 +804,23 @@
 }
 
 
+void end_key_cache(KEY_CACHE *keycache, my_bool cleanup)
+{
+  int i;
+  DBUG_ENTER("end_key_cache");
+  DBUG_PRINT("enter", ("key_cache: 0x%lx", (long) keycache));
+
+  for (i = 0; i < keycache_segments; i++) {
+  	r_end_key_cache(&keycache->key_cache[i], cleanup);
+  }
+  if (cleanup) {
+    pthread_mutex_destroy(&keycache->status_lock);
+    my_free((gptr) keycache->key_cache, MYF(0));
+    keycache->key_cache_inited = 0;
+  }
+  DBUG_VOID_RETURN;
+}
+
 /*
   Remove key_cache from memory
 
@@ -631,7 +833,7 @@
     none
 */
 
-void end_key_cache(KEY_CACHE *keycache, my_bool cleanup)
+void r_end_key_cache(R_KEY_CACHE *keycache, my_bool cleanup)
 {
   DBUG_ENTER("end_key_cache");
   DBUG_PRINT("enter", ("key_cache: 0x%lx", (long) keycache));
@@ -841,7 +1043,7 @@
   and link it to the chain of clean blocks for the specified file
 */
 
-static void link_to_file_list(KEY_CACHE *keycache,
+static void link_to_file_list(R_KEY_CACHE *keycache,
                               BLOCK_LINK *block, int file,
                               my_bool unlink_block)
 {
@@ -862,7 +1064,7 @@
   file and link it to the chain of dirty blocks for this file
 */
 
-static inline void link_to_changed_list(KEY_CACHE *keycache,
+static inline void link_to_changed_list(R_KEY_CACHE *keycache,
                                         BLOCK_LINK *block)
 {
   unlink_changed(block);
@@ -910,7 +1112,7 @@
                   first for eviction
 */
 
-static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
+static void link_block(R_KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
                        my_bool at_end)
 {
   BLOCK_LINK *ins;
@@ -1003,7 +1205,7 @@
     See NOTES for link_block
 */
 
-static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block)
+static void unlink_block(R_KEY_CACHE *keycache, BLOCK_LINK *block)
 {
   if (block->next_used == block)
     /* The list contains only one member */
@@ -1034,7 +1236,7 @@
 /*
   Register requests for a block
 */
-static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count)
+static void reg_requests(R_KEY_CACHE *keycache, BLOCK_LINK *block, int count)
 {
   if (! block->requests)
     /* First request for the block unlinks it */
@@ -1072,7 +1274,7 @@
     for a too long time (this time is determined by parameter age_threshold).
 */
 
-static void unreg_request(KEY_CACHE *keycache,
+static void unreg_request(R_KEY_CACHE *keycache,
                           BLOCK_LINK *block, int at_end)
 {
   if (! --block->requests)
@@ -1128,7 +1330,7 @@
   signals on its termination
 */
 
-static inline void wait_for_readers(KEY_CACHE *keycache, BLOCK_LINK *block)
+static inline void wait_for_readers(R_KEY_CACHE *keycache, BLOCK_LINK *block)
 {
 #ifdef THREAD
   struct st_my_thread_var *thread= my_thread_var;
@@ -1165,7 +1367,7 @@
   Remove a hash link from the hash table
 */
 
-static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link)
+static void unlink_hash(R_KEY_CACHE *keycache, HASH_LINK *hash_link)
 {
   KEYCACHE_DBUG_PRINT("unlink_hash", ("fd: %u  pos_ %lu  #requests=%u",
       (uint) hash_link->file,(ulong) hash_link->diskpos, hash_link->requests));
@@ -1221,7 +1423,7 @@
   Get the hash link for a page
 */
 
-static HASH_LINK *get_hash_link(KEY_CACHE *keycache,
+static HASH_LINK *get_hash_link(R_KEY_CACHE *keycache,
                                 int file, my_off_t filepos)
 {
   reg1 HASH_LINK *hash_link, **start;
@@ -1306,6 +1508,15 @@
 }
 
 
+static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
+                                  File file, my_off_t filepos,
+                                  int init_hits_left,
+                                  int wrmode, int *page_st)
+{
+	return r_find_key_block(&keycache->key_cache[MKC_HASH(file, filepos)], 
+		file, filepos, init_hits_left, wrmode, page_st);
+}
+
 /*
   Get a block for the file page requested by a keycache read/write operation;
   If the page is not in the cache return a free block, if there is none
@@ -1342,7 +1553,7 @@
     waits until first of this operations links any block back.
 */
 
-static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
+static BLOCK_LINK *r_find_key_block(R_KEY_CACHE *keycache,
                                   File file, my_off_t filepos,
                                   int init_hits_left,
                                   int wrmode, int *page_st)
@@ -1685,7 +1896,7 @@
     portion is less than read_length, but not less than min_length.
 */
 
-static void read_block(KEY_CACHE *keycache,
+static void read_block(R_KEY_CACHE *keycache,
                        BLOCK_LINK *block, uint read_length,
                        uint min_length, my_bool primary)
 {
@@ -1758,6 +1969,39 @@
 }
 
 
+byte *key_cache_read(KEY_CACHE *keycache,
+                      File file, my_off_t filepos, int level,
+                      byte *buff, uint length,
+                      uint block_length __attribute__((unused)),
+                      int return_buffer __attribute__((unused)))
+{
+   byte *start= buff;
+   uint offset= (uint) (filepos % keycache->key_cache_block_size);
+   uint r_length;
+   uint read_length = 0;
+
+    /* Read data in key_cache_block_size increments */
+    do
+    {
+        r_length = length;
+        if (r_length > keycache->key_cache_block_size - offset) {
+		r_length = keycache->key_cache_block_size - offset;
+	}
+	read_length = r_key_cache_read(&keycache->key_cache[MKC_HASH(file, filepos)], 
+		file, filepos, level, buff, r_length, block_length, return_buffer);
+
+        if (read_length == 0) {
+	    return 0;
+	}
+
+	filepos += read_length;
+	buff += read_length;
+        offset = 0;
+    } while ((length-= read_length));
+
+    return start;
+}
+
 /*
   Read a block of data from a cached file into a buffer;
 
@@ -1786,15 +2030,14 @@
     have to be a multiple of key_cache_block_size;
 */
 
-byte *key_cache_read(KEY_CACHE *keycache,
+uint r_key_cache_read(R_KEY_CACHE *keycache,
                      File file, my_off_t filepos, int level,
                      byte *buff, uint length,
 		     uint block_length __attribute__((unused)),
 		     int return_buffer __attribute__((unused)))
 {
-  int error=0;
   uint offset= 0;
-  byte *start= buff;
+  uint read_length;
   DBUG_ENTER("key_cache_read");
   DBUG_PRINT("enter", ("fd: %u  pos: %lu  length: %u",
                (uint) file, (ulong) filepos, length));
@@ -1803,14 +2046,11 @@
   {
     /* Key cache is used */
     reg1 BLOCK_LINK *block;
-    uint read_length;
     uint status;
     int page_st;
 
     offset= (uint) (filepos % keycache->key_cache_block_size);
-    /* Read data in key_cache_block_size increments */
-    do
-    {
+
       keycache_pthread_mutex_lock(&keycache->cache_lock);
       if (!keycache->can_be_used)
       {
@@ -1829,7 +2069,7 @@
 
       inc_counter_for_resize_op(keycache);
       keycache->global_cache_r_requests++;
-      block=find_key_block(keycache, file, filepos, level, 0, &page_st);
+      block=r_find_key_block(keycache, file, filepos, level, 0, &page_st);
       if (block->status != BLOCK_ERROR && page_st != PAGE_READ)
       {
         /* The requested page is to be read into the block buffer */
@@ -1883,19 +2123,15 @@
       keycache_pthread_mutex_unlock(&keycache->cache_lock);
 
       if (status & BLOCK_ERROR)
-        DBUG_RETURN((byte *) 0);
+        DBUG_RETURN(0);
 
 #ifndef THREAD
       /* This is only true if we where able to read everything in one block */
       if (return_buffer)
 	DBUG_RETURN(block->buffer);
 #endif
-      buff+= read_length;
-      filepos+= read_length+offset;
-      offset= 0;
 
-    } while ((length-= read_length));
-    DBUG_RETURN(start);
+    DBUG_RETURN(read_length);
   }
 
 no_key_cache:					/* Key cache is not used */
@@ -1903,12 +2139,38 @@
   /* We can't use mutex here as the key cache may not be initialized */
   keycache->global_cache_r_requests++;
   keycache->global_cache_read++;
-  if (my_pread(file, (byte*) buff, length, filepos+offset, MYF(MY_NABP)))
-    error= 1;
-  DBUG_RETURN(error ? (byte*) 0 : start);
+  read_length = my_pread(file, (byte*) buff, length, filepos+offset, MYF(MY_NABP));
+  DBUG_RETURN(read_length ? read_length : 0);
 }
 
 
+int key_cache_insert(KEY_CACHE *keycache,
+                     File file, my_off_t filepos, int level,
+                     byte *buff, uint length)
+{
+   uint offset= (uint) (filepos % keycache->key_cache_block_size);
+   uint w_length;
+
+    /* Write data in key_cache_block_size increments */
+    do
+    {
+        w_length = length;
+        if (w_length > keycache->key_cache_block_size - offset) {
+		w_length = keycache->key_cache_block_size - offset;
+        }
+	if (r_key_cache_insert(&keycache->key_cache[MKC_HASH(file, filepos)],
+		file, filepos, level, buff, w_length)) {
+            return 1;
+        }
+
+	filepos += w_length;
+	buff += w_length;
+        offset = 0;
+    } while ((length-= w_length));
+
+    return 0;
+}
+
 /*
   Insert a block of file data from a buffer into key cache
 
@@ -1929,7 +2191,7 @@
     0 if a success, 1 - otherwise.
 */
 
-int key_cache_insert(KEY_CACHE *keycache,
+int r_key_cache_insert(R_KEY_CACHE *keycache,
                      File file, my_off_t filepos, int level,
                      byte *buff, uint length)
 {
@@ -1947,8 +2209,6 @@
     uint offset;
 
     offset= (uint) (filepos % keycache->key_cache_block_size);
-    do
-    {
       keycache_pthread_mutex_lock(&keycache->cache_lock);
       if (!keycache->can_be_used)
       {
@@ -1963,7 +2223,7 @@
 
       inc_counter_for_resize_op(keycache);
       keycache->global_cache_r_requests++;
-      block= find_key_block(keycache, file, filepos, level, 0, &page_st);
+      block= r_find_key_block(keycache, file, filepos, level, 0, &page_st);
       if (block->status != BLOCK_ERROR && page_st != PAGE_READ)
       {
         /* The requested page is to be read into the block buffer */
@@ -2010,16 +2270,39 @@
       if (error)
         DBUG_RETURN(1);
 
-      buff+= read_length;
-      filepos+= read_length+offset;
-      offset= 0;
-
-    } while ((length-= read_length));
   }
   DBUG_RETURN(0);
 }
 
 
+int key_cache_write(KEY_CACHE *keycache,
+                    File file, my_off_t filepos, int level,
+                    byte *buff, uint length,
+                    uint block_length  __attribute__((unused)),
+                    int dont_write)
+{
+   uint offset= (uint) (filepos % keycache->key_cache_block_size);
+   uint w_length;
+
+    /* Write data in key_cache_block_size increments */
+    do
+    {
+        w_length = length;
+        if (w_length > keycache->key_cache_block_size - offset) {
+		w_length = keycache->key_cache_block_size - offset;
+        }
+	if (r_key_cache_write(&keycache->key_cache[MKC_HASH(file,filepos)],
+		file, filepos, level, buff, w_length, block_length, dont_write))
+            return 1;
+
+	filepos += w_length;
+	buff += w_length;
+        offset = 0;
+    } while ((length-= w_length));
+
+    return 0;
+}
+
 /*
   Write a buffer into a cached file.
 
@@ -2047,7 +2330,7 @@
     have to be a multiple of key_cache_block_size;
 */
 
-int key_cache_write(KEY_CACHE *keycache,
+int r_key_cache_write(R_KEY_CACHE *keycache,
                     File file, my_off_t filepos, int level,
                     byte *buff, uint length,
                     uint block_length  __attribute__((unused)),
@@ -2082,8 +2365,6 @@
     uint offset;
 
     offset= (uint) (filepos % keycache->key_cache_block_size);
-    do
-    {
       keycache_pthread_mutex_lock(&keycache->cache_lock);
       if (!keycache->can_be_used)
       {
@@ -2098,7 +2379,7 @@
 
       inc_counter_for_resize_op(keycache);
       keycache->global_cache_w_requests++;
-      block= find_key_block(keycache, file, filepos, level, 1, &page_st);
+      block= r_find_key_block(keycache, file, filepos, level, 1, &page_st);
       if (!block)
       {
         /* It happens only for requests submitted during resize operation */
@@ -2153,7 +2434,7 @@
       {
         keycache_pthread_mutex_unlock(&keycache->cache_lock);
         error= 1;
-        break;
+        goto end;
       }
 
       dec_counter_for_resize_op(keycache);
@@ -2165,7 +2446,6 @@
       filepos+= read_length+offset;
       offset= 0;
 
-    } while ((length-= read_length));
     goto end;
   }
 
@@ -2195,7 +2475,7 @@
   and add it to the free list.
 */
 
-static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block)
+static void free_block(R_KEY_CACHE *keycache, BLOCK_LINK *block)
 {
   KEYCACHE_THREAD_TRACE("free block");
   KEYCACHE_DBUG_PRINT("free_block",
@@ -2253,7 +2533,7 @@
   free used blocks if requested
 */
 
-static int flush_cached_blocks(KEY_CACHE *keycache,
+static int flush_cached_blocks(R_KEY_CACHE *keycache,
                                File file, BLOCK_LINK **cache,
                                BLOCK_LINK **end,
                                enum flush_type type)
@@ -2334,7 +2614,7 @@
     1  error
 */
 
-static int flush_key_blocks_int(KEY_CACHE *keycache,
+static int flush_key_blocks_int(R_KEY_CACHE *keycache,
 				File file, enum flush_type type)
 {
   BLOCK_LINK *cache_buff[FLUSH_CACHE],**cache;
@@ -2525,6 +2805,21 @@
 }
 
 
+int flush_key_blocks(KEY_CACHE *keycache,
+                     File file, enum flush_type type)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < keycache_segments; i++) {
+		ret += r_flush_key_blocks(&keycache->key_cache[i], file, type);
+	}
+
+	if (ret > 0) {
+		ret = 1;
+	}
+	DBUG_RETURN(ret);
+}
 /*
   Flush all blocks for a file to disk
 
@@ -2540,7 +2835,7 @@
     1  error
 */
 
-int flush_key_blocks(KEY_CACHE *keycache,
+int r_flush_key_blocks(R_KEY_CACHE *keycache,
                      File file, enum flush_type type)
 {
   int res;
@@ -2562,7 +2857,7 @@
   Flush all blocks in the key cache to disk
 */
 
-static int flush_all_key_blocks(KEY_CACHE *keycache)
+static int flush_all_key_blocks(R_KEY_CACHE *keycache)
 {
 #if defined(KEYCACHE_DEBUG)
   uint cnt=0;
@@ -2591,6 +2886,17 @@
 }
 
 
+int reset_key_cache_counters(const char *name __attribute__((unused)),
+                             KEY_CACHE *key_cache)
+{
+	int i;
+
+	for (i = 0; i < keycache_segments; i++) {
+		r_reset_key_cache_counters(name, &key_cache->key_cache[i]);
+	}
+	DBUG_RETURN(0);
+}
+
 /*
   Reset the counters of a key cache.
 
@@ -2607,8 +2913,8 @@
     0 on success (always because it can't fail)
 */
 
-int reset_key_cache_counters(const char *name __attribute__((unused)),
-                             KEY_CACHE *key_cache)
+int r_reset_key_cache_counters(const char *name __attribute__((unused)),
+                             R_KEY_CACHE *key_cache)
 {
   DBUG_ENTER("reset_key_cache_counters");
   if (!key_cache->key_cache_inited)
@@ -2631,7 +2937,7 @@
 /*
   Test if disk-cache is ok
 */
-static void test_key_cache(KEY_CACHE *keycache __attribute__((unused)),
+static void test_key_cache(R_KEY_CACHE *keycache __attribute__((unused)),
                            const char *where __attribute__((unused)),
                            my_bool lock __attribute__((unused)))
 {
@@ -2645,7 +2951,7 @@
 #define MAX_QUEUE_LEN  100
 
 
-static void keycache_dump(KEY_CACHE *keycache)
+static void keycache_dump(R_KEY_CACHE *keycache)
 {
   FILE *keycache_dump_file=fopen(KEYCACHE_DUMP_FILE, "w");
   struct st_my_thread_var *last;
diff -rux Makefile -x config.log mysql-5.0.83-orig/sql/handler.cc mysql-5.0.83-fix/sql/handler.cc
--- mysql-5.0.83-orig/sql/handler.cc	2009-05-29 20:19:23.000000000 +0200
+++ mysql-5.0.83-fix/sql/handler.cc	2009-08-03 11:48:00.883544076 +0200
@@ -2419,11 +2419,13 @@
     uint tmp_block_size= (uint) key_cache->param_block_size;
     uint division_limit= key_cache->param_division_limit;
     uint age_threshold=  key_cache->param_age_threshold;
+    uint segments=  keycache_segments;
     pthread_mutex_unlock(&LOCK_global_system_variables);
     DBUG_RETURN(!init_key_cache(key_cache,
 				tmp_block_size,
 				tmp_buff_size,
-				division_limit, age_threshold));
+				division_limit, age_threshold,
+				segments));
   }
   DBUG_RETURN(0);
 }
@@ -2445,7 +2447,7 @@
     pthread_mutex_unlock(&LOCK_global_system_variables);
     DBUG_RETURN(!resize_key_cache(key_cache, tmp_block_size,
 				  tmp_buff_size,
-				  division_limit, age_threshold));
+				  division_limit, age_threshold)); 
   }
   DBUG_RETURN(0);
 }
@@ -2460,8 +2462,10 @@
     pthread_mutex_lock(&LOCK_global_system_variables);
     uint division_limit= key_cache->param_division_limit;
     uint age_threshold=  key_cache->param_age_threshold;
+    uint segments=  keycache_segments;
     pthread_mutex_unlock(&LOCK_global_system_variables);
-    change_key_cache_param(key_cache, division_limit, age_threshold);
+    change_key_cache_param(key_cache, division_limit, 
+		           age_threshold);
   }
   return 0;
 }
diff -rux Makefile -x config.log mysql-5.0.83-orig/sql/mysqld.cc mysql-5.0.83-fix/sql/mysqld.cc
--- mysql-5.0.83-orig/sql/mysqld.cc	2009-05-29 20:19:25.000000000 +0200
+++ mysql-5.0.83-fix/sql/mysqld.cc	2009-08-03 11:45:48.093543975 +0200
@@ -451,6 +451,7 @@
 ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
 ulong max_connections, max_connect_errors;
 uint  max_user_connections= 0;
+extern uint  keycache_segments;
 /*
   Limit of the total number of prepared statements in the server.
   Is necessary to protect the server against out-of-memory attacks.
@@ -2290,6 +2291,7 @@
 We will try our best to scrape up some info that will hopefully help diagnose\n\
 the problem, but since we have already crashed, something is definitely wrong\n\
 and this may fail.\n\n");
+  update_key_cache_status(dflt_key_cache);
   fprintf(stderr, "key_buffer_size=%lu\n",
           (ulong) dflt_key_cache->key_cache_mem_size);
   fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size);
@@ -4947,6 +4949,7 @@
   OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE,
   OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE,
   OPT_KEY_CACHE_DIVISION_LIMIT, OPT_KEY_CACHE_AGE_THRESHOLD,
+  OPT_KEY_CACHE_SEGMENTS,
   OPT_LONG_QUERY_TIME,
   OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET,
   OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE,
@@ -6110,6 +6113,12 @@
    (gptr*) 0,
    0, (GET_ULONG | GET_ASK_ADDR) , REQUIRED_ARG, 100,
    1, 100, 0, 1, 0},
+  {"key_cache_segments", OPT_KEY_CACHE_SEGMENTS,
+   "The number of segments in the key cache",
+   (gptr*) &keycache_segments,
+   (gptr*) &keycache_segments,
+   0, GET_UINT, REQUIRED_ARG,
+   DEFAULT_KEYCACHE_SEGMENTS, 1, UINT_MAX, 0, 1, 0},
   {"long_query_time", OPT_LONG_QUERY_TIME,
    "Log all queries that have taken more than long_query_time seconds to execute to file.",
    (gptr*) &global_system_variables.long_query_time,
@@ -7639,6 +7648,7 @@
   case OPT_KEY_CACHE_BLOCK_SIZE:
   case OPT_KEY_CACHE_DIVISION_LIMIT:
   case OPT_KEY_CACHE_AGE_THRESHOLD:
+  case OPT_KEY_CACHE_SEGMENTS:
   {
     KEY_CACHE *key_cache;
     if (!(key_cache= get_or_create_key_cache(keyname, key_length)))
@@ -7652,6 +7662,8 @@
       return (gptr*) &key_cache->param_division_limit;
     case OPT_KEY_CACHE_AGE_THRESHOLD:
       return (gptr*) &key_cache->param_age_threshold;
+    case OPT_KEY_CACHE_SEGMENTS:
+      return (gptr*) &keycache_segments;
     }
   }
   }
diff -rux Makefile -x config.log mysql-5.0.83-orig/sql/mysql_priv.h mysql-5.0.83-fix/sql/mysql_priv.h
--- mysql-5.0.83-orig/sql/mysql_priv.h	2009-05-29 20:19:25.000000000 +0200
+++ mysql-5.0.83-fix/sql/mysql_priv.h	2009-07-27 20:24:34.143544101 +0200
@@ -1350,6 +1350,7 @@
 extern ulong max_binlog_size, max_relay_log_size;
 extern ulong rpl_recovery_rank, thread_cache_size;
 extern ulong back_log;
+extern uint  keycache_segments;
 extern ulong specialflag, current_pid;
 extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter;
 extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size;
diff -rux Makefile -x config.log mysql-5.0.83-orig/sql/set_var.cc mysql-5.0.83-fix/sql/set_var.cc
--- mysql-5.0.83-orig/sql/set_var.cc	2009-05-29 20:19:26.000000000 +0200
+++ mysql-5.0.83-fix/sql/set_var.cc	2009-07-27 20:26:50.283544108 +0200
@@ -974,6 +974,7 @@
                                                                     SHOW_SYS},
   {sys_key_cache_division_limit.name,   (char*) &sys_key_cache_division_limit,
                                                                     SHOW_SYS},
+  {"key_cache_segments",       (char*) &keycache_segments,          SHOW_INT},
   {"language",                language,                             SHOW_CHAR},
   {"large_files_support",     (char*) &opt_large_files,             SHOW_BOOL},
   {"large_page_size",         (char*) &opt_large_page_size,         SHOW_INT},
@@ -2675,7 +2676,6 @@
   return error;
 }
 
-
 /*****************************************************************************
   Functions to handle SET NAMES and SET CHARACTER SET
 *****************************************************************************/
diff -rux Makefile -x config.log mysql-5.0.83-orig/sql/set_var.h mysql-5.0.83-fix/sql/set_var.h
--- mysql-5.0.83-orig/sql/set_var.h	2009-05-29 20:19:26.000000000 +0200
+++ mysql-5.0.83-fix/sql/set_var.h	2009-07-27 20:26:56.043544130 +0200
@@ -770,7 +770,6 @@
   SHOW_TYPE show_type() { return SHOW_LONG; }
 };
 
-
 class sys_var_thd_date_time_format :public sys_var_thd
 {
   DATE_TIME_FORMAT *SV::*offset;
@@ -1044,6 +1043,7 @@
   }
   friend bool process_key_caches(int (* func) (const char *name,
 					       KEY_CACHE *));
+  friend int fill_keycache(THD *thd, TABLE_LIST *tables, COND *cond);
   friend void delete_elements(I_List<NAMED_LIST> *list,
 			      void (*free_element)(const char*, gptr));
 };
diff -rux Makefile -x config.log mysql-5.0.83-orig/sql/sql_show.cc mysql-5.0.83-fix/sql/sql_show.cc
--- mysql-5.0.83-orig/sql/sql_show.cc	2009-05-29 20:19:32.000000000 +0200
+++ mysql-5.0.83-fix/sql/sql_show.cc	2009-07-27 20:28:04.973543991 +0200
@@ -3626,6 +3626,7 @@
   const char *wild= lex->wild ? lex->wild->ptr() : NullS;
   int res= 0;
   STATUS_VAR tmp;
+  update_key_cache_status(dflt_key_cache);
   ha_update_statistics();                    /* Export engines statistics */
   pthread_mutex_lock(&LOCK_status);
   if (lex->option_type == OPT_GLOBAL)
@@ -4457,6 +4458,55 @@
   Description of ST_FIELD_INFO in table.h
 */
 
+int fill_keycache(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+  TABLE *table= tables->table;
+  CHARSET_INFO *scs= system_charset_info;
+  I_List_iterator<NAMED_LIST> it(key_caches);
+  NAMED_LIST *element;
+  int i;
+
+  while ((element= it++))
+  {
+    KEY_CACHE *key_cache= (KEY_CACHE *) element->data;
+    for (i = 0; i < keycache_segments; i++) {
+      R_KEY_CACHE *kc = &key_cache->key_cache[i];
+      restore_record(table, s->default_values);
+      table->field[0]->store(element->name, element->name_length, scs);
+      table->field[1]->store((longlong) i, TRUE);
+      table->field[2]->store((longlong) kc->key_cache_mem_size, TRUE);
+      table->field[3]->store((longlong) kc->key_cache_block_size, TRUE);
+      table->field[4]->store((longlong) kc->blocks_used, TRUE);
+      table->field[5]->store((longlong) kc->blocks_unused, TRUE);
+      table->field[6]->store((longlong) kc->global_cache_r_requests, TRUE);
+      table->field[7]->store((longlong) kc->global_cache_read, TRUE);
+      table->field[8]->store((longlong) kc->global_cache_w_requests, TRUE);
+      table->field[9]->store((longlong) kc->global_cache_write, TRUE);
+
+      if (schema_table_store_record(thd, table)) {
+        sql_print_error("store record failed");
+        return 1;
+      }
+    }
+    return 0;
+  }
+}
+
+ST_FIELD_INFO keycache_info[]=
+{
+  {"KEYCACHE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Keycache_name"},
+  {"SEGMENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Segment"},
+  {"SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Size"},
+  {"BLOCK_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Block_size"},
+  {"BLOCKS_USED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Blocks_used"},
+  {"BLOCKS_UNUSED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Blocks_unused"},
+  {"READ_REQUESTS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Read_requests"},
+  {"READS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Reads"},
+  {"WRITE_REQUESTS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Write_requests"},
+  {"WRITES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Writes"},
+  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+};
+
 ST_SCHEMA_TABLE schema_tables[]=
 {
   {"CHARACTER_SETS", charsets_fields_info, create_schema_table, 
@@ -4502,10 +4552,11 @@
    make_old_format, 0, -1, -1, 1},
   {"VIEWS", view_fields_info, create_schema_table, 
     get_all_tables, 0, get_schema_views_record, 1, 2, 0},
+  {"KEYCACHE", keycache_info, create_schema_table, 
+    fill_keycache, 0, 0, 1, 2, 0},
   {0, 0, 0, 0, 0, 0, 0, 0, 0}
 };
 
-
 #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
 template class List_iterator_fast<char>;
 template class List<char>;
diff -rux Makefile -x config.log mysql-5.0.83-orig/sql/sql_test.cc mysql-5.0.83-fix/sql/sql_test.cc
--- mysql-5.0.83-orig/sql/sql_test.cc	2009-05-29 20:19:32.000000000 +0200
+++ mysql-5.0.83-fix/sql/sql_test.cc	2009-08-03 11:47:31.393544149 +0200
@@ -420,11 +420,13 @@
   }
   else
   {
+    update_key_cache_status(key_cache);
     printf("%s\n\
 Buffer_size:    %10lu\n\
 Block_size:     %10lu\n\
 Division_limit: %10lu\n\
 Age_limit:      %10lu\n\
+Segments:	%10lu\n\
 blocks used:    %10lu\n\
 not flushed:    %10lu\n\
 w_requests:     %10s\n\
@@ -434,6 +436,7 @@
 	   name,
 	   (ulong) key_cache->param_buff_size, key_cache->param_block_size,
 	   key_cache->param_division_limit, key_cache->param_age_threshold,
+	   keycache_segments,
 	   key_cache->blocks_used,key_cache->global_blocks_changed,
 	   llstr(key_cache->global_cache_w_requests,llbuff1),
            llstr(key_cache->global_cache_write,llbuff2),
