00001 /* Copyright (C) 2000 MySQL AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* 00018 These functions handle keyblock cacheing for ISAM and MyISAM tables. 00019 00020 One cache can handle many files. 00021 It must contain buffers of the same blocksize. 00022 init_key_cache() should be used to init cache handler. 00023 00024 The free list (free_block_list) is a stack like structure. 00025 When a block is freed by free_block(), it is pushed onto the stack. 00026 When a new block is required it is first tried to pop one from the stack. 00027 If the stack is empty, it is tried to get a never-used block from the pool. 00028 If this is empty too, then a block is taken from the LRU ring, flushing it 00029 to disk, if neccessary. This is handled in find_key_block(). 00030 With the new free list, the blocks can have three temperatures: 00031 hot, warm and cold (which is free). This is remembered in the block header 00032 by the enum BLOCK_TEMPERATURE temperature variable. Remembering the 00033 temperature is neccessary to correctly count the number of warm blocks, 00034 which is required to decide when blocks are allowed to become hot. Whenever 00035 a block is inserted to another (sub-)chain, we take the old and new 00036 temperature into account to decide if we got one more or less warm block. 00037 blocks_unused is the sum of never used blocks in the pool and of currently 00038 free blocks. blocks_used is the number of blocks fetched from the pool and 00039 as such gives the maximum number of in-use blocks at any time. 00040 */ 00041 00042 #include "mysys_priv.h" 00043 #include <keycache.h> 00044 #include "my_static.h" 00045 #include <m_string.h> 00046 #include <errno.h> 00047 #include <stdarg.h> 00048 00049 /* 00050 Some compilation flags have been added specifically for this module 00051 to control the following: 00052 - not to let a thread to yield the control when reading directly 00053 from key cache, which might improve performance in many cases; 00054 to enable this add: 00055 #define SERIALIZED_READ_FROM_CACHE 00056 - to set an upper bound for number of threads simultaneously 00057 using the key cache; this setting helps to determine an optimal 00058 size for hash table and improve performance when the number of 00059 blocks in the key cache much less than the number of threads 00060 accessing it; 00061 to set this number equal to <N> add 00062 #define MAX_THREADS <N> 00063 - to substitute calls of pthread_cond_wait for calls of 00064 pthread_cond_timedwait (wait with timeout set up); 00065 this setting should be used only when you want to trap a deadlock 00066 situation, which theoretically should not happen; 00067 to set timeout equal to <T> seconds add 00068 #define KEYCACHE_TIMEOUT <T> 00069 - to enable the module traps and to send debug information from 00070 key cache module to a special debug log add: 00071 #define KEYCACHE_DEBUG 00072 the name of this debug log file <LOG NAME> can be set through: 00073 #define KEYCACHE_DEBUG_LOG <LOG NAME> 00074 if the name is not defined, it's set by default; 00075 if the KEYCACHE_DEBUG flag is not set up and we are in a debug 00076 mode, i.e. when ! defined(DBUG_OFF), the debug information from the 00077 module is sent to the regular debug log. 00078 00079 Example of the settings: 00080 #define SERIALIZED_READ_FROM_CACHE 00081 #define MAX_THREADS 100 00082 #define KEYCACHE_TIMEOUT 1 00083 #define KEYCACHE_DEBUG 00084 #define KEYCACHE_DEBUG_LOG "my_key_cache_debug.log" 00085 */ 00086 00087 #define STRUCT_PTR(TYPE, MEMBER, a) \ 00088 (TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER)) 00089 00090 /* types of condition variables */ 00091 #define COND_FOR_REQUESTED 0 00092 #define COND_FOR_SAVED 1 00093 #define COND_FOR_READERS 2 00094 00095 typedef pthread_cond_t KEYCACHE_CONDVAR; 00096 00097 /* descriptor of the page in the key cache block buffer */ 00098 struct st_keycache_page 00099 { 00100 int file; /* file to which the page belongs to */ 00101 my_off_t filepos; /* position of the page in the file */ 00102 }; 00103 00104 /* element in the chain of a hash table bucket */ 00105 struct st_hash_link 00106 { 00107 struct st_hash_link *next, **prev; /* to connect links in the same bucket */ 00108 struct st_block_link *block; /* reference to the block for the page: */ 00109 File file; /* from such a file */ 00110 my_off_t diskpos; /* with such an offset */ 00111 uint requests; /* number of requests for the page */ 00112 }; 00113 00114 /* simple states of a block */ 00115 #define BLOCK_ERROR 1 /* an error occured when performing disk i/o */ 00116 #define BLOCK_READ 2 /* the is page in the block buffer */ 00117 #define BLOCK_IN_SWITCH 4 /* block is preparing to read new page */ 00118 #define BLOCK_REASSIGNED 8 /* block does not accept requests for old page */ 00119 #define BLOCK_IN_FLUSH 16 /* block is in flush operation */ 00120 #define BLOCK_CHANGED 32 /* block buffer contains a dirty page */ 00121 00122 /* page status, returned by find_key_block */ 00123 #define PAGE_READ 0 00124 #define PAGE_TO_BE_READ 1 00125 #define PAGE_WAIT_TO_BE_READ 2 00126 00127 /* block temperature determines in which (sub-)chain the block currently is */ 00128 enum BLOCK_TEMPERATURE { BLOCK_COLD /*free*/ , BLOCK_WARM , BLOCK_HOT }; 00129 00130 /* key cache block */ 00131 struct st_block_link 00132 { 00133 struct st_block_link 00134 *next_used, **prev_used; /* to connect links in the LRU chain (ring) */ 00135 struct st_block_link 00136 *next_changed, **prev_changed; /* for lists of file dirty/clean blocks */ 00137 struct st_hash_link *hash_link; /* backward ptr to referring hash_link */ 00138 KEYCACHE_WQUEUE wqueue[2]; /* queues on waiting requests for new/old pages */ 00139 uint requests; /* number of requests for the block */ 00140 byte *buffer; /* buffer for the block page */ 00141 uint offset; /* beginning of modified data in the buffer */ 00142 uint length; /* end of data in the buffer */ 00143 uint status; /* state of the block */ 00144 enum BLOCK_TEMPERATURE temperature; /* block temperature: cold, warm, hot */ 00145 uint hits_left; /* number of hits left until promotion */ 00146 ulonglong last_hit_time; /* timestamp of the last hit */ 00147 KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event */ 00148 }; 00149 00150 KEY_CACHE dflt_key_cache_var; 00151 KEY_CACHE *dflt_key_cache= &dflt_key_cache_var; 00152 00153 #define FLUSH_CACHE 2000 /* sort this many blocks at once */ 00154 00155 static int flush_all_key_blocks(KEY_CACHE *keycache); 00156 #ifdef THREAD 00157 static void link_into_queue(KEYCACHE_WQUEUE *wqueue, 00158 struct st_my_thread_var *thread); 00159 static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue, 00160 struct st_my_thread_var *thread); 00161 #endif 00162 static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block); 00163 static void test_key_cache(KEY_CACHE *keycache, 00164 const char *where, my_bool lock); 00165 00166 #define KEYCACHE_HASH(f, pos) \ 00167 (((ulong) ((pos) >> keycache->key_cache_shift)+ \ 00168 (ulong) (f)) & (keycache->hash_entries-1)) 00169 #define FILE_HASH(f) ((uint) (f) & (CHANGED_BLOCKS_HASH-1)) 00170 00171 #define DEFAULT_KEYCACHE_DEBUG_LOG "keycache_debug.log" 00172 00173 #if defined(KEYCACHE_DEBUG) && ! defined(KEYCACHE_DEBUG_LOG) 00174 #define KEYCACHE_DEBUG_LOG DEFAULT_KEYCACHE_DEBUG_LOG 00175 #endif 00176 00177 #if defined(KEYCACHE_DEBUG_LOG) 00178 static FILE *keycache_debug_log=NULL; 00179 static void keycache_debug_print _VARARGS((const char *fmt,...)); 00180 #define KEYCACHE_DEBUG_OPEN \ 00181 if (!keycache_debug_log) \ 00182 { \ 00183 keycache_debug_log= fopen(KEYCACHE_DEBUG_LOG, "w"); \ 00184 (void) setvbuf(keycache_debug_log, NULL, _IOLBF, BUFSIZ); \ 00185 } 00186 00187 #define KEYCACHE_DEBUG_CLOSE \ 00188 if (keycache_debug_log) \ 00189 { \ 00190 fclose(keycache_debug_log); \ 00191 keycache_debug_log= 0; \ 00192 } 00193 #else 00194 #define KEYCACHE_DEBUG_OPEN 00195 #define KEYCACHE_DEBUG_CLOSE 00196 #endif /* defined(KEYCACHE_DEBUG_LOG) */ 00197 00198 #if defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) 00199 #define KEYCACHE_DBUG_PRINT(l, m) \ 00200 { if (keycache_debug_log) fprintf(keycache_debug_log, "%s: ", l); \ 00201 keycache_debug_print m; } 00202 00203 #define KEYCACHE_DBUG_ASSERT(a) \ 00204 { if (! (a) && keycache_debug_log) fclose(keycache_debug_log); \ 00205 assert(a); } 00206 #else 00207 #define KEYCACHE_DBUG_PRINT(l, m) DBUG_PRINT(l, m) 00208 #define KEYCACHE_DBUG_ASSERT(a) DBUG_ASSERT(a) 00209 #endif /* defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) */ 00210 00211 #if defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) 00212 #ifdef THREAD 00213 static long keycache_thread_id; 00214 #define KEYCACHE_THREAD_TRACE(l) \ 00215 KEYCACHE_DBUG_PRINT(l,("|thread %ld",keycache_thread_id)) 00216 00217 #define KEYCACHE_THREAD_TRACE_BEGIN(l) \ 00218 { struct st_my_thread_var *thread_var= my_thread_var; \ 00219 keycache_thread_id= thread_var->id; \ 00220 KEYCACHE_DBUG_PRINT(l,("[thread %ld",keycache_thread_id)) } 00221 00222 #define KEYCACHE_THREAD_TRACE_END(l) \ 00223 KEYCACHE_DBUG_PRINT(l,("]thread %ld",keycache_thread_id)) 00224 #else /* THREAD */ 00225 #define KEYCACHE_THREAD_TRACE(l) KEYCACHE_DBUG_PRINT(l,("")) 00226 #define KEYCACHE_THREAD_TRACE_BEGIN(l) KEYCACHE_DBUG_PRINT(l,("")) 00227 #define KEYCACHE_THREAD_TRACE_END(l) KEYCACHE_DBUG_PRINT(l,("")) 00228 #endif /* THREAD */ 00229 #else 00230 #define KEYCACHE_THREAD_TRACE_BEGIN(l) 00231 #define KEYCACHE_THREAD_TRACE_END(l) 00232 #define KEYCACHE_THREAD_TRACE(l) 00233 #endif /* defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) */ 00234 00235 #define BLOCK_NUMBER(b) \ 00236 ((uint) (((char*)(b)-(char *) keycache->block_root)/sizeof(BLOCK_LINK))) 00237 #define HASH_LINK_NUMBER(h) \ 00238 ((uint) (((char*)(h)-(char *) keycache->hash_link_root)/sizeof(HASH_LINK))) 00239 00240 #if (defined(KEYCACHE_TIMEOUT) && !defined(__WIN__)) || defined(KEYCACHE_DEBUG) 00241 static int keycache_pthread_cond_wait(pthread_cond_t *cond, 00242 pthread_mutex_t *mutex); 00243 #else 00244 #define keycache_pthread_cond_wait pthread_cond_wait 00245 #endif 00246 00247 #if defined(KEYCACHE_DEBUG) 00248 static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex); 00249 static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex); 00250 static int keycache_pthread_cond_signal(pthread_cond_t *cond); 00251 #else 00252 #define keycache_pthread_mutex_lock pthread_mutex_lock 00253 #define keycache_pthread_mutex_unlock pthread_mutex_unlock 00254 #define keycache_pthread_cond_signal pthread_cond_signal 00255 #endif /* defined(KEYCACHE_DEBUG) */ 00256 00257 static inline uint next_power(uint value) 00258 { 00259 return (uint) my_round_up_to_next_power((uint32) value) << 1; 00260 } 00261 00262 00263 /* 00264 Initialize a key cache 00265 00266 SYNOPSIS 00267 init_key_cache() 00268 keycache pointer to a key cache data structure 00269 key_cache_block_size size of blocks to keep cached data 00270 use_mem total memory to use for the key cache 00271 division_limit division limit (may be zero) 00272 age_threshold age threshold (may be zero) 00273 00274 RETURN VALUE 00275 number of blocks in the key cache, if successful, 00276 0 - otherwise. 00277 00278 NOTES. 00279 if keycache->key_cache_inited != 0 we assume that the key cache 00280 is already initialized. This is for now used by myisamchk, but shouldn't 00281 be something that a program should rely on! 00282 00283 It's assumed that no two threads call this function simultaneously 00284 referring to the same key cache handle. 00285 00286 */ 00287 00288 int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, 00289 ulong use_mem, uint division_limit, 00290 uint age_threshold) 00291 { 00292 uint blocks, hash_links, length; 00293 int error; 00294 DBUG_ENTER("init_key_cache"); 00295 DBUG_ASSERT(key_cache_block_size >= 512); 00296 00297 KEYCACHE_DEBUG_OPEN; 00298 if (keycache->key_cache_inited && keycache->disk_blocks > 0) 00299 { 00300 DBUG_PRINT("warning",("key cache already in use")); 00301 DBUG_RETURN(0); 00302 } 00303 00304 keycache->global_cache_w_requests= keycache->global_cache_r_requests= 0; 00305 keycache->global_cache_read= keycache->global_cache_write= 0; 00306 keycache->disk_blocks= -1; 00307 if (! keycache->key_cache_inited) 00308 { 00309 keycache->key_cache_inited= 1; 00310 keycache->in_init= 0; 00311 pthread_mutex_init(&keycache->cache_lock, MY_MUTEX_INIT_FAST); 00312 keycache->resize_queue.last_thread= NULL; 00313 } 00314 00315 keycache->key_cache_mem_size= use_mem; 00316 keycache->key_cache_block_size= key_cache_block_size; 00317 keycache->key_cache_shift= my_bit_log2(key_cache_block_size); 00318 DBUG_PRINT("info", ("key_cache_block_size: %u", 00319 key_cache_block_size)); 00320 00321 blocks= (uint) (use_mem / (sizeof(BLOCK_LINK) + 2 * sizeof(HASH_LINK) + 00322 sizeof(HASH_LINK*) * 5/4 + key_cache_block_size)); 00323 /* It doesn't make sense to have too few blocks (less than 8) */ 00324 if (blocks >= 8 && keycache->disk_blocks < 0) 00325 { 00326 for ( ; ; ) 00327 { 00328 /* Set my_hash_entries to the next bigger 2 power */ 00329 if ((keycache->hash_entries= next_power(blocks)) < blocks * 5/4) 00330 keycache->hash_entries<<= 1; 00331 hash_links= 2 * blocks; 00332 #if defined(MAX_THREADS) 00333 if (hash_links < MAX_THREADS + blocks - 1) 00334 hash_links= MAX_THREADS + blocks - 1; 00335 #endif 00336 while ((length= (ALIGN_SIZE(blocks * sizeof(BLOCK_LINK)) + 00337 ALIGN_SIZE(hash_links * sizeof(HASH_LINK)) + 00338 ALIGN_SIZE(sizeof(HASH_LINK*) * 00339 keycache->hash_entries))) + 00340 ((ulong) blocks << keycache->key_cache_shift) > use_mem) 00341 blocks--; 00342 /* Allocate memory for cache page buffers */ 00343 if ((keycache->block_mem= 00344 my_large_malloc((ulong) blocks * keycache->key_cache_block_size, 00345 MYF(MY_WME)))) 00346 { 00347 /* 00348 Allocate memory for blocks, hash_links and hash entries; 00349 For each block 2 hash links are allocated 00350 */ 00351 if ((keycache->block_root= (BLOCK_LINK*) my_malloc((uint) length, 00352 MYF(0)))) 00353 break; 00354 my_large_free(keycache->block_mem, MYF(0)); 00355 keycache->block_mem= 0; 00356 } 00357 if (blocks < 8) 00358 { 00359 my_errno= ENOMEM; 00360 goto err; 00361 } 00362 blocks= blocks / 4*3; 00363 } 00364 keycache->blocks_unused= (ulong) blocks; 00365 keycache->disk_blocks= (int) blocks; 00366 keycache->hash_links= hash_links; 00367 keycache->hash_root= (HASH_LINK**) ((char*) keycache->block_root + 00368 ALIGN_SIZE(blocks*sizeof(BLOCK_LINK))); 00369 keycache->hash_link_root= (HASH_LINK*) ((char*) keycache->hash_root + 00370 ALIGN_SIZE((sizeof(HASH_LINK*) * 00371 keycache->hash_entries))); 00372 bzero((byte*) keycache->block_root, 00373 keycache->disk_blocks * sizeof(BLOCK_LINK)); 00374 bzero((byte*) keycache->hash_root, 00375 keycache->hash_entries * sizeof(HASH_LINK*)); 00376 bzero((byte*) keycache->hash_link_root, 00377 keycache->hash_links * sizeof(HASH_LINK)); 00378 keycache->hash_links_used= 0; 00379 keycache->free_hash_list= NULL; 00380 keycache->blocks_used= keycache->blocks_changed= 0; 00381 00382 keycache->global_blocks_changed= 0; 00383 keycache->blocks_available=0; /* For debugging */ 00384 00385 /* The LRU chain is empty after initialization */ 00386 keycache->used_last= NULL; 00387 keycache->used_ins= NULL; 00388 keycache->free_block_list= NULL; 00389 keycache->keycache_time= 0; 00390 keycache->warm_blocks= 0; 00391 keycache->min_warm_blocks= (division_limit ? 00392 blocks * division_limit / 100 + 1 : 00393 blocks); 00394 keycache->age_threshold= (age_threshold ? 00395 blocks * age_threshold / 100 : 00396 blocks); 00397 00398 keycache->cnt_for_resize_op= 0; 00399 keycache->resize_in_flush= 0; 00400 keycache->can_be_used= 1; 00401 00402 keycache->waiting_for_hash_link.last_thread= NULL; 00403 keycache->waiting_for_block.last_thread= NULL; 00404 DBUG_PRINT("exit", 00405 ("disk_blocks: %d block_root: 0x%lx hash_entries: %d\ 00406 hash_root: 0x%lx hash_links: %d hash_link_root: 0x%lx", 00407 keycache->disk_blocks, keycache->block_root, 00408 keycache->hash_entries, keycache->hash_root, 00409 keycache->hash_links, keycache->hash_link_root)); 00410 bzero((gptr) keycache->changed_blocks, 00411 sizeof(keycache->changed_blocks[0]) * CHANGED_BLOCKS_HASH); 00412 bzero((gptr) keycache->file_blocks, 00413 sizeof(keycache->file_blocks[0]) * CHANGED_BLOCKS_HASH); 00414 } 00415 00416 keycache->blocks= keycache->disk_blocks > 0 ? keycache->disk_blocks : 0; 00417 DBUG_RETURN((int) keycache->disk_blocks); 00418 00419 err: 00420 error= my_errno; 00421 keycache->disk_blocks= 0; 00422 keycache->blocks= 0; 00423 if (keycache->block_mem) 00424 { 00425 my_large_free((gptr) keycache->block_mem, MYF(0)); 00426 keycache->block_mem= NULL; 00427 } 00428 if (keycache->block_root) 00429 { 00430 my_free((gptr) keycache->block_root, MYF(0)); 00431 keycache->block_root= NULL; 00432 } 00433 my_errno= error; 00434 keycache->can_be_used= 0; 00435 DBUG_RETURN(0); 00436 } 00437 00438 00439 /* 00440 Resize a key cache 00441 00442 SYNOPSIS 00443 resize_key_cache() 00444 keycache pointer to a key cache data structure 00445 key_cache_block_size size of blocks to keep cached data 00446 use_mem total memory to use for the new key cache 00447 division_limit new division limit (if not zero) 00448 age_threshold new age threshold (if not zero) 00449 00450 RETURN VALUE 00451 number of blocks in the key cache, if successful, 00452 0 - otherwise. 00453 00454 NOTES. 00455 The function first compares the memory size and the block size parameters 00456 with the key cache values. 00457 00458 If they differ the function free the the memory allocated for the 00459 old key cache blocks by calling the end_key_cache function and 00460 then rebuilds the key cache with new blocks by calling 00461 init_key_cache. 00462 00463 The function starts the operation only when all other threads 00464 performing operations with the key cache let her to proceed 00465 (when cnt_for_resize=0). 00466 */ 00467 00468 int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, 00469 ulong use_mem, uint division_limit, 00470 uint age_threshold) 00471 { 00472 int blocks; 00473 #ifdef THREAD 00474 struct st_my_thread_var *thread; 00475 KEYCACHE_WQUEUE *wqueue; 00476 #endif 00477 DBUG_ENTER("resize_key_cache"); 00478 00479 if (!keycache->key_cache_inited) 00480 DBUG_RETURN(keycache->disk_blocks); 00481 00482 if(key_cache_block_size == keycache->key_cache_block_size && 00483 use_mem == keycache->key_cache_mem_size) 00484 { 00485 change_key_cache_param(keycache, division_limit, age_threshold); 00486 DBUG_RETURN(keycache->disk_blocks); 00487 } 00488 00489 keycache_pthread_mutex_lock(&keycache->cache_lock); 00490 00491 #ifdef THREAD 00492 wqueue= &keycache->resize_queue; 00493 thread= my_thread_var; 00494 link_into_queue(wqueue, thread); 00495 00496 while (wqueue->last_thread->next != thread) 00497 { 00498 keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); 00499 } 00500 #endif 00501 00502 keycache->resize_in_flush= 1; 00503 if (flush_all_key_blocks(keycache)) 00504 { 00505 /* TODO: if this happens, we should write a warning in the log file ! */ 00506 keycache->resize_in_flush= 0; 00507 blocks= 0; 00508 keycache->can_be_used= 0; 00509 goto finish; 00510 } 00511 keycache->resize_in_flush= 0; 00512 keycache->can_be_used= 0; 00513 #ifdef THREAD 00514 while (keycache->cnt_for_resize_op) 00515 { 00516 KEYCACHE_DBUG_PRINT("resize_key_cache: wait", 00517 ("suspend thread %ld", thread->id)); 00518 keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); 00519 } 00520 #else 00521 KEYCACHE_DBUG_ASSERT(keycache->cnt_for_resize_op == 0); 00522 #endif 00523 00524 end_key_cache(keycache, 0); /* Don't free mutex */ 00525 /* The following will work even if use_mem is 0 */ 00526 blocks= init_key_cache(keycache, key_cache_block_size, use_mem, 00527 division_limit, age_threshold); 00528 00529 finish: 00530 #ifdef THREAD 00531 unlink_from_queue(wqueue, thread); 00532 /* Signal for the next resize request to proceeed if any */ 00533 if (wqueue->last_thread) 00534 { 00535 KEYCACHE_DBUG_PRINT("resize_key_cache: signal", 00536 ("thread %ld", wqueue->last_thread->next->id)); 00537 keycache_pthread_cond_signal(&wqueue->last_thread->next->suspend); 00538 } 00539 #endif 00540 keycache_pthread_mutex_unlock(&keycache->cache_lock); 00541 DBUG_RETURN(blocks); 00542 } 00543 00544 00545 /* 00546 Increment counter blocking resize key cache operation 00547 */ 00548 static inline void inc_counter_for_resize_op(KEY_CACHE *keycache) 00549 { 00550 keycache->cnt_for_resize_op++; 00551 } 00552 00553 00554 /* 00555 Decrement counter blocking resize key cache operation; 00556 Signal the operation to proceed when counter becomes equal zero 00557 */ 00558 static inline void dec_counter_for_resize_op(KEY_CACHE *keycache) 00559 { 00560 #ifdef THREAD 00561 struct st_my_thread_var *last_thread; 00562 if (!--keycache->cnt_for_resize_op && 00563 (last_thread= keycache->resize_queue.last_thread)) 00564 { 00565 KEYCACHE_DBUG_PRINT("dec_counter_for_resize_op: signal", 00566 ("thread %ld", last_thread->next->id)); 00567 keycache_pthread_cond_signal(&last_thread->next->suspend); 00568 } 00569 #else 00570 keycache->cnt_for_resize_op--; 00571 #endif 00572 } 00573 00574 /* 00575 Change the key cache parameters 00576 00577 SYNOPSIS 00578 change_key_cache_param() 00579 keycache pointer to a key cache data structure 00580 division_limit new division limit (if not zero) 00581 age_threshold new age threshold (if not zero) 00582 00583 RETURN VALUE 00584 none 00585 00586 NOTES. 00587 Presently the function resets the key cache parameters 00588 concerning midpoint insertion strategy - division_limit and 00589 age_threshold. 00590 */ 00591 00592 void change_key_cache_param(KEY_CACHE *keycache, uint division_limit, 00593 uint age_threshold) 00594 { 00595 DBUG_ENTER("change_key_cache_param"); 00596 00597 keycache_pthread_mutex_lock(&keycache->cache_lock); 00598 if (division_limit) 00599 keycache->min_warm_blocks= (keycache->disk_blocks * 00600 division_limit / 100 + 1); 00601 if (age_threshold) 00602 keycache->age_threshold= (keycache->disk_blocks * 00603 age_threshold / 100); 00604 keycache_pthread_mutex_unlock(&keycache->cache_lock); 00605 DBUG_VOID_RETURN; 00606 } 00607 00608 00609 /* 00610 Remove key_cache from memory 00611 00612 SYNOPSIS 00613 end_key_cache() 00614 keycache key cache handle 00615 cleanup Complete free (Free also mutex for key cache) 00616 00617 RETURN VALUE 00618 none 00619 */ 00620 00621 void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) 00622 { 00623 DBUG_ENTER("end_key_cache"); 00624 DBUG_PRINT("enter", ("key_cache: 0x%lx", keycache)); 00625 00626 if (!keycache->key_cache_inited) 00627 DBUG_VOID_RETURN; 00628 00629 if (keycache->disk_blocks > 0) 00630 { 00631 if (keycache->block_mem) 00632 { 00633 my_large_free((gptr) keycache->block_mem, MYF(0)); 00634 keycache->block_mem= NULL; 00635 my_free((gptr) keycache->block_root, MYF(0)); 00636 keycache->block_root= NULL; 00637 } 00638 keycache->disk_blocks= -1; 00639 /* Reset blocks_changed to be safe if flush_all_key_blocks is called */ 00640 keycache->blocks_changed= 0; 00641 } 00642 00643 DBUG_PRINT("status", ("used: %d changed: %d w_requests: %lu " 00644 "writes: %lu r_requests: %lu reads: %lu", 00645 keycache->blocks_used, keycache->global_blocks_changed, 00646 (ulong) keycache->global_cache_w_requests, 00647 (ulong) keycache->global_cache_write, 00648 (ulong) keycache->global_cache_r_requests, 00649 (ulong) keycache->global_cache_read)); 00650 00651 if (cleanup) 00652 { 00653 pthread_mutex_destroy(&keycache->cache_lock); 00654 keycache->key_cache_inited= keycache->can_be_used= 0; 00655 KEYCACHE_DEBUG_CLOSE; 00656 } 00657 DBUG_VOID_RETURN; 00658 } /* end_key_cache */ 00659 00660 00661 #ifdef THREAD 00662 /* 00663 Link a thread into double-linked queue of waiting threads. 00664 00665 SYNOPSIS 00666 link_into_queue() 00667 wqueue pointer to the queue structure 00668 thread pointer to the thread to be added to the queue 00669 00670 RETURN VALUE 00671 none 00672 00673 NOTES. 00674 Queue is represented by a circular list of the thread structures 00675 The list is double-linked of the type (**prev,*next), accessed by 00676 a pointer to the last element. 00677 */ 00678 00679 static void link_into_queue(KEYCACHE_WQUEUE *wqueue, 00680 struct st_my_thread_var *thread) 00681 { 00682 struct st_my_thread_var *last; 00683 if (! (last= wqueue->last_thread)) 00684 { 00685 /* Queue is empty */ 00686 thread->next= thread; 00687 thread->prev= &thread->next; 00688 } 00689 else 00690 { 00691 thread->prev= last->next->prev; 00692 last->next->prev= &thread->next; 00693 thread->next= last->next; 00694 last->next= thread; 00695 } 00696 wqueue->last_thread= thread; 00697 } 00698 00699 /* 00700 Unlink a thread from double-linked queue of waiting threads 00701 00702 SYNOPSIS 00703 unlink_from_queue() 00704 wqueue pointer to the queue structure 00705 thread pointer to the thread to be removed from the queue 00706 00707 RETURN VALUE 00708 none 00709 00710 NOTES. 00711 See NOTES for link_into_queue 00712 */ 00713 00714 static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue, 00715 struct st_my_thread_var *thread) 00716 { 00717 KEYCACHE_DBUG_PRINT("unlink_from_queue", ("thread %ld", thread->id)); 00718 if (thread->next == thread) 00719 /* The queue contains only one member */ 00720 wqueue->last_thread= NULL; 00721 else 00722 { 00723 thread->next->prev= thread->prev; 00724 *thread->prev=thread->next; 00725 if (wqueue->last_thread == thread) 00726 wqueue->last_thread= STRUCT_PTR(struct st_my_thread_var, next, 00727 thread->prev); 00728 } 00729 thread->next= NULL; 00730 } 00731 00732 00733 /* 00734 Add a thread to single-linked queue of waiting threads 00735 00736 SYNOPSIS 00737 add_to_queue() 00738 wqueue pointer to the queue structure 00739 thread pointer to the thread to be added to the queue 00740 00741 RETURN VALUE 00742 none 00743 00744 NOTES. 00745 Queue is represented by a circular list of the thread structures 00746 The list is single-linked of the type (*next), accessed by a pointer 00747 to the last element. 00748 */ 00749 00750 static inline void add_to_queue(KEYCACHE_WQUEUE *wqueue, 00751 struct st_my_thread_var *thread) 00752 { 00753 struct st_my_thread_var *last; 00754 if (! (last= wqueue->last_thread)) 00755 thread->next= thread; 00756 else 00757 { 00758 thread->next= last->next; 00759 last->next= thread; 00760 } 00761 wqueue->last_thread= thread; 00762 } 00763 00764 00765 /* 00766 Remove all threads from queue signaling them to proceed 00767 00768 SYNOPSIS 00769 realease_queue() 00770 wqueue pointer to the queue structure 00771 thread pointer to the thread to be added to the queue 00772 00773 RETURN VALUE 00774 none 00775 00776 NOTES. 00777 See notes for add_to_queue 00778 When removed from the queue each thread is signaled via condition 00779 variable thread->suspend. 00780 */ 00781 00782 static void release_queue(KEYCACHE_WQUEUE *wqueue) 00783 { 00784 struct st_my_thread_var *last= wqueue->last_thread; 00785 struct st_my_thread_var *next= last->next; 00786 struct st_my_thread_var *thread; 00787 do 00788 { 00789 thread=next; 00790 KEYCACHE_DBUG_PRINT("release_queue: signal", ("thread %ld", thread->id)); 00791 keycache_pthread_cond_signal(&thread->suspend); 00792 next=thread->next; 00793 thread->next= NULL; 00794 } 00795 while (thread != last); 00796 wqueue->last_thread= NULL; 00797 } 00798 #endif 00799 00800 00801 /* 00802 Unlink a block from the chain of dirty/clean blocks 00803 */ 00804 00805 static inline void unlink_changed(BLOCK_LINK *block) 00806 { 00807 if (block->next_changed) 00808 block->next_changed->prev_changed= block->prev_changed; 00809 *block->prev_changed= block->next_changed; 00810 } 00811 00812 00813 /* 00814 Link a block into the chain of dirty/clean blocks 00815 */ 00816 00817 static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead) 00818 { 00819 block->prev_changed= phead; 00820 if ((block->next_changed= *phead)) 00821 (*phead)->prev_changed= &block->next_changed; 00822 *phead= block; 00823 } 00824 00825 00826 /* 00827 Unlink a block from the chain of dirty/clean blocks, if it's asked for, 00828 and link it to the chain of clean blocks for the specified file 00829 */ 00830 00831 static void link_to_file_list(KEY_CACHE *keycache, 00832 BLOCK_LINK *block, int file, my_bool unlink) 00833 { 00834 if (unlink) 00835 unlink_changed(block); 00836 link_changed(block, &keycache->file_blocks[FILE_HASH(file)]); 00837 if (block->status & BLOCK_CHANGED) 00838 { 00839 block->status&= ~BLOCK_CHANGED; 00840 keycache->blocks_changed--; 00841 keycache->global_blocks_changed--; 00842 } 00843 } 00844 00845 00846 /* 00847 Unlink a block from the chain of clean blocks for the specified 00848 file and link it to the chain of dirty blocks for this file 00849 */ 00850 00851 static inline void link_to_changed_list(KEY_CACHE *keycache, 00852 BLOCK_LINK *block) 00853 { 00854 unlink_changed(block); 00855 link_changed(block, 00856 &keycache->changed_blocks[FILE_HASH(block->hash_link->file)]); 00857 block->status|=BLOCK_CHANGED; 00858 keycache->blocks_changed++; 00859 keycache->global_blocks_changed++; 00860 } 00861 00862 00863 /* 00864 Link a block to the LRU chain at the beginning or at the end of 00865 one of two parts. 00866 00867 SYNOPSIS 00868 link_block() 00869 keycache pointer to a key cache data structure 00870 block pointer to the block to link to the LRU chain 00871 hot <-> to link the block into the hot subchain 00872 at_end <-> to link the block at the end of the subchain 00873 00874 RETURN VALUE 00875 none 00876 00877 NOTES. 00878 The LRU chain is represented by a curcular list of block structures. 00879 The list is double-linked of the type (**prev,*next) type. 00880 The LRU chain is divided into two parts - hot and warm. 00881 There are two pointers to access the last blocks of these two 00882 parts. The beginning of the warm part follows right after the 00883 end of the hot part. 00884 Only blocks of the warm part can be used for replacement. 00885 The first block from the beginning of this subchain is always 00886 taken for eviction (keycache->last_used->next) 00887 00888 LRU chain: +------+ H O T +------+ 00889 +----| end |----...<----| beg |----+ 00890 | +------+last +------+ | 00891 v<-link in latest hot (new end) | 00892 | link in latest warm (new end)->^ 00893 | +------+ W A R M +------+ | 00894 +----| beg |---->...----| end |----+ 00895 +------+ +------+ins 00896 first for eviction 00897 */ 00898 00899 static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot, 00900 my_bool at_end) 00901 { 00902 BLOCK_LINK *ins; 00903 BLOCK_LINK **pins; 00904 00905 KEYCACHE_DBUG_ASSERT(! (block->hash_link && block->hash_link->requests)); 00906 #ifdef THREAD 00907 if (!hot && keycache->waiting_for_block.last_thread) 00908 { 00909 /* Signal that in the LRU warm sub-chain an available block has appeared */ 00910 struct st_my_thread_var *last_thread= 00911 keycache->waiting_for_block.last_thread; 00912 struct st_my_thread_var *first_thread= last_thread->next; 00913 struct st_my_thread_var *next_thread= first_thread; 00914 HASH_LINK *hash_link= (HASH_LINK *) first_thread->opt_info; 00915 struct st_my_thread_var *thread; 00916 do 00917 { 00918 thread= next_thread; 00919 next_thread= thread->next; 00920 /* 00921 We notify about the event all threads that ask 00922 for the same page as the first thread in the queue 00923 */ 00924 if ((HASH_LINK *) thread->opt_info == hash_link) 00925 { 00926 KEYCACHE_DBUG_PRINT("link_block: signal", ("thread %ld", thread->id)); 00927 keycache_pthread_cond_signal(&thread->suspend); 00928 unlink_from_queue(&keycache->waiting_for_block, thread); 00929 block->requests++; 00930 } 00931 } 00932 while (thread != last_thread); 00933 hash_link->block= block; 00934 KEYCACHE_THREAD_TRACE("link_block: after signaling"); 00935 #if defined(KEYCACHE_DEBUG) 00936 KEYCACHE_DBUG_PRINT("link_block", 00937 ("linked,unlinked block %u status=%x #requests=%u #available=%u", 00938 BLOCK_NUMBER(block), block->status, 00939 block->requests, keycache->blocks_available)); 00940 #endif 00941 return; 00942 } 00943 #else /* THREAD */ 00944 KEYCACHE_DBUG_ASSERT(! (!hot && keycache->waiting_for_block.last_thread)); 00945 /* Condition not transformed using DeMorgan, to keep the text identical */ 00946 #endif /* THREAD */ 00947 pins= hot ? &keycache->used_ins : &keycache->used_last; 00948 ins= *pins; 00949 if (ins) 00950 { 00951 ins->next_used->prev_used= &block->next_used; 00952 block->next_used= ins->next_used; 00953 block->prev_used= &ins->next_used; 00954 ins->next_used= block; 00955 if (at_end) 00956 *pins= block; 00957 } 00958 else 00959 { 00960 /* The LRU chain is empty */ 00961 keycache->used_last= keycache->used_ins= block->next_used= block; 00962 block->prev_used= &block->next_used; 00963 } 00964 KEYCACHE_THREAD_TRACE("link_block"); 00965 #if defined(KEYCACHE_DEBUG) 00966 keycache->blocks_available++; 00967 KEYCACHE_DBUG_PRINT("link_block", 00968 ("linked block %u:%1u status=%x #requests=%u #available=%u", 00969 BLOCK_NUMBER(block), at_end, block->status, 00970 block->requests, keycache->blocks_available)); 00971 KEYCACHE_DBUG_ASSERT((ulong) keycache->blocks_available <= 00972 keycache->blocks_used); 00973 #endif 00974 } 00975 00976 00977 /* 00978 Unlink a block from the LRU chain 00979 00980 SYNOPSIS 00981 unlink_block() 00982 keycache pointer to a key cache data structure 00983 block pointer to the block to unlink from the LRU chain 00984 00985 RETURN VALUE 00986 none 00987 00988 NOTES. 00989 See NOTES for link_block 00990 */ 00991 00992 static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block) 00993 { 00994 if (block->next_used == block) 00995 /* The list contains only one member */ 00996 keycache->used_last= keycache->used_ins= NULL; 00997 else 00998 { 00999 block->next_used->prev_used= block->prev_used; 01000 *block->prev_used= block->next_used; 01001 if (keycache->used_last == block) 01002 keycache->used_last= STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used); 01003 if (keycache->used_ins == block) 01004 keycache->used_ins=STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used); 01005 } 01006 block->next_used= NULL; 01007 01008 KEYCACHE_THREAD_TRACE("unlink_block"); 01009 #if defined(KEYCACHE_DEBUG) 01010 keycache->blocks_available--; 01011 KEYCACHE_DBUG_PRINT("unlink_block", 01012 ("unlinked block %u status=%x #requests=%u #available=%u", 01013 BLOCK_NUMBER(block), block->status, 01014 block->requests, keycache->blocks_available)); 01015 KEYCACHE_DBUG_ASSERT(keycache->blocks_available >= 0); 01016 #endif 01017 } 01018 01019 01020 /* 01021 Register requests for a block 01022 */ 01023 static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count) 01024 { 01025 if (! block->requests) 01026 /* First request for the block unlinks it */ 01027 unlink_block(keycache, block); 01028 block->requests+=count; 01029 } 01030 01031 01032 /* 01033 Unregister request for a block 01034 linking it to the LRU chain if it's the last request 01035 01036 SYNOPSIS 01037 unreg_request() 01038 keycache pointer to a key cache data structure 01039 block pointer to the block to link to the LRU chain 01040 at_end <-> to link the block at the end of the LRU chain 01041 01042 RETURN VALUE 01043 none 01044 01045 NOTES. 01046 Every linking to the LRU chain decrements by one a special block 01047 counter (if it's positive). If the at_end parameter is TRUE the block is 01048 added either at the end of warm sub-chain or at the end of hot sub-chain. 01049 It is added to the hot subchain if its counter is zero and number of 01050 blocks in warm sub-chain is not less than some low limit (determined by 01051 the division_limit parameter). Otherwise the block is added to the warm 01052 sub-chain. If the at_end parameter is FALSE the block is always added 01053 at beginning of the warm sub-chain. 01054 Thus a warm block can be promoted to the hot sub-chain when its counter 01055 becomes zero for the first time. 01056 At the same time the block at the very beginning of the hot subchain 01057 might be moved to the beginning of the warm subchain if it stays untouched 01058 for a too long time (this time is determined by parameter age_threshold). 01059 */ 01060 01061 static void unreg_request(KEY_CACHE *keycache, 01062 BLOCK_LINK *block, int at_end) 01063 { 01064 if (! --block->requests) 01065 { 01066 my_bool hot; 01067 if (block->hits_left) 01068 block->hits_left--; 01069 hot= !block->hits_left && at_end && 01070 keycache->warm_blocks > keycache->min_warm_blocks; 01071 if (hot) 01072 { 01073 if (block->temperature == BLOCK_WARM) 01074 keycache->warm_blocks--; 01075 block->temperature= BLOCK_HOT; 01076 KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks=%u", 01077 keycache->warm_blocks)); 01078 } 01079 link_block(keycache, block, hot, (my_bool)at_end); 01080 block->last_hit_time= keycache->keycache_time; 01081 keycache->keycache_time++; 01082 01083 block= keycache->used_ins; 01084 /* Check if we should link a hot block to the warm block */ 01085 if (block && keycache->keycache_time - block->last_hit_time > 01086 keycache->age_threshold) 01087 { 01088 unlink_block(keycache, block); 01089 link_block(keycache, block, 0, 0); 01090 if (block->temperature != BLOCK_WARM) 01091 { 01092 keycache->warm_blocks++; 01093 block->temperature= BLOCK_WARM; 01094 } 01095 KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks=%u", 01096 keycache->warm_blocks)); 01097 } 01098 } 01099 } 01100 01101 /* 01102 Remove a reader of the page in block 01103 */ 01104 01105 static inline void remove_reader(BLOCK_LINK *block) 01106 { 01107 #ifdef THREAD 01108 if (! --block->hash_link->requests && block->condvar) 01109 keycache_pthread_cond_signal(block->condvar); 01110 #else 01111 --block->hash_link->requests; 01112 #endif 01113 } 01114 01115 01116 /* 01117 Wait until the last reader of the page in block 01118 signals on its termination 01119 */ 01120 01121 static inline void wait_for_readers(KEY_CACHE *keycache __attribute__((unused)), 01122 BLOCK_LINK *block) 01123 { 01124 #ifdef THREAD 01125 struct st_my_thread_var *thread= my_thread_var; 01126 while (block->hash_link->requests) 01127 { 01128 KEYCACHE_DBUG_PRINT("wait_for_readers: wait", 01129 ("suspend thread %ld block %u", 01130 thread->id, BLOCK_NUMBER(block))); 01131 block->condvar= &thread->suspend; 01132 keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); 01133 block->condvar= NULL; 01134 } 01135 #else 01136 KEYCACHE_DBUG_ASSERT(block->hash_link->requests == 0); 01137 #endif 01138 } 01139 01140 01141 /* 01142 Add a hash link to a bucket in the hash_table 01143 */ 01144 01145 static inline void link_hash(HASH_LINK **start, HASH_LINK *hash_link) 01146 { 01147 if (*start) 01148 (*start)->prev= &hash_link->next; 01149 hash_link->next= *start; 01150 hash_link->prev= start; 01151 *start= hash_link; 01152 } 01153 01154 01155 /* 01156 Remove a hash link from the hash table 01157 */ 01158 01159 static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link) 01160 { 01161 KEYCACHE_DBUG_PRINT("unlink_hash", ("fd: %u pos_ %lu #requests=%u", 01162 (uint) hash_link->file,(ulong) hash_link->diskpos, hash_link->requests)); 01163 KEYCACHE_DBUG_ASSERT(hash_link->requests == 0); 01164 if ((*hash_link->prev= hash_link->next)) 01165 hash_link->next->prev= hash_link->prev; 01166 hash_link->block= NULL; 01167 #ifdef THREAD 01168 if (keycache->waiting_for_hash_link.last_thread) 01169 { 01170 /* Signal that a free hash link has appeared */ 01171 struct st_my_thread_var *last_thread= 01172 keycache->waiting_for_hash_link.last_thread; 01173 struct st_my_thread_var *first_thread= last_thread->next; 01174 struct st_my_thread_var *next_thread= first_thread; 01175 KEYCACHE_PAGE *first_page= (KEYCACHE_PAGE *) (first_thread->opt_info); 01176 struct st_my_thread_var *thread; 01177 01178 hash_link->file= first_page->file; 01179 hash_link->diskpos= first_page->filepos; 01180 do 01181 { 01182 KEYCACHE_PAGE *page; 01183 thread= next_thread; 01184 page= (KEYCACHE_PAGE *) thread->opt_info; 01185 next_thread= thread->next; 01186 /* 01187 We notify about the event all threads that ask 01188 for the same page as the first thread in the queue 01189 */ 01190 if (page->file == hash_link->file && page->filepos == hash_link->diskpos) 01191 { 01192 KEYCACHE_DBUG_PRINT("unlink_hash: signal", ("thread %ld", thread->id)); 01193 keycache_pthread_cond_signal(&thread->suspend); 01194 unlink_from_queue(&keycache->waiting_for_hash_link, thread); 01195 } 01196 } 01197 while (thread != last_thread); 01198 link_hash(&keycache->hash_root[KEYCACHE_HASH(hash_link->file, 01199 hash_link->diskpos)], 01200 hash_link); 01201 return; 01202 } 01203 #else /* THREAD */ 01204 KEYCACHE_DBUG_ASSERT(! (keycache->waiting_for_hash_link.last_thread)); 01205 #endif /* THREAD */ 01206 hash_link->next= keycache->free_hash_list; 01207 keycache->free_hash_list= hash_link; 01208 } 01209 01210 01211 /* 01212 Get the hash link for a page 01213 */ 01214 01215 static HASH_LINK *get_hash_link(KEY_CACHE *keycache, 01216 int file, my_off_t filepos) 01217 { 01218 reg1 HASH_LINK *hash_link, **start; 01219 #if defined(KEYCACHE_DEBUG) 01220 int cnt; 01221 #endif 01222 01223 KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u pos: %lu", 01224 (uint) file,(ulong) filepos)); 01225 01226 restart: 01227 /* 01228 Find the bucket in the hash table for the pair (file, filepos); 01229 start contains the head of the bucket list, 01230 hash_link points to the first member of the list 01231 */ 01232 hash_link= *(start= &keycache->hash_root[KEYCACHE_HASH(file, filepos)]); 01233 #if defined(KEYCACHE_DEBUG) 01234 cnt= 0; 01235 #endif 01236 /* Look for an element for the pair (file, filepos) in the bucket chain */ 01237 while (hash_link && 01238 (hash_link->diskpos != filepos || hash_link->file != file)) 01239 { 01240 hash_link= hash_link->next; 01241 #if defined(KEYCACHE_DEBUG) 01242 cnt++; 01243 if (! (cnt <= keycache->hash_links_used)) 01244 { 01245 int i; 01246 for (i=0, hash_link= *start ; 01247 i < cnt ; i++, hash_link= hash_link->next) 01248 { 01249 KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u pos: %lu", 01250 (uint) hash_link->file,(ulong) hash_link->diskpos)); 01251 } 01252 } 01253 KEYCACHE_DBUG_ASSERT(cnt <= keycache->hash_links_used); 01254 #endif 01255 } 01256 if (! hash_link) 01257 { 01258 /* There is no hash link in the hash table for the pair (file, filepos) */ 01259 if (keycache->free_hash_list) 01260 { 01261 hash_link= keycache->free_hash_list; 01262 keycache->free_hash_list= hash_link->next; 01263 } 01264 else if (keycache->hash_links_used < keycache->hash_links) 01265 { 01266 hash_link= &keycache->hash_link_root[keycache->hash_links_used++]; 01267 } 01268 else 01269 { 01270 #ifdef THREAD 01271 /* Wait for a free hash link */ 01272 struct st_my_thread_var *thread= my_thread_var; 01273 KEYCACHE_PAGE page; 01274 KEYCACHE_DBUG_PRINT("get_hash_link", ("waiting")); 01275 page.file= file; 01276 page.filepos= filepos; 01277 thread->opt_info= (void *) &page; 01278 link_into_queue(&keycache->waiting_for_hash_link, thread); 01279 KEYCACHE_DBUG_PRINT("get_hash_link: wait", 01280 ("suspend thread %ld", thread->id)); 01281 keycache_pthread_cond_wait(&thread->suspend, 01282 &keycache->cache_lock); 01283 thread->opt_info= NULL; 01284 #else 01285 KEYCACHE_DBUG_ASSERT(0); 01286 #endif 01287 goto restart; 01288 } 01289 hash_link->file= file; 01290 hash_link->diskpos= filepos; 01291 link_hash(start, hash_link); 01292 } 01293 /* Register the request for the page */ 01294 hash_link->requests++; 01295 01296 return hash_link; 01297 } 01298 01299 01300 /* 01301 Get a block for the file page requested by a keycache read/write operation; 01302 If the page is not in the cache return a free block, if there is none 01303 return the lru block after saving its buffer if the page is dirty. 01304 01305 SYNOPSIS 01306 01307 find_key_block() 01308 keycache pointer to a key cache data structure 01309 file handler for the file to read page from 01310 filepos position of the page in the file 01311 init_hits_left how initialize the block counter for the page 01312 wrmode <-> get for writing 01313 page_st out {PAGE_READ,PAGE_TO_BE_READ,PAGE_WAIT_TO_BE_READ} 01314 01315 RETURN VALUE 01316 Pointer to the found block if successful, 0 - otherwise 01317 01318 NOTES. 01319 For the page from file positioned at filepos the function checks whether 01320 the page is in the key cache specified by the first parameter. 01321 If this is the case it immediately returns the block. 01322 If not, the function first chooses a block for this page. If there is 01323 no not used blocks in the key cache yet, the function takes the block 01324 at the very beginning of the warm sub-chain. It saves the page in that 01325 block if it's dirty before returning the pointer to it. 01326 The function returns in the page_st parameter the following values: 01327 PAGE_READ - if page already in the block, 01328 PAGE_TO_BE_READ - if it is to be read yet by the current thread 01329 WAIT_TO_BE_READ - if it is to be read by another thread 01330 If an error occurs THE BLOCK_ERROR bit is set in the block status. 01331 It might happen that there are no blocks in LRU chain (in warm part) - 01332 all blocks are unlinked for some read/write operations. Then the function 01333 waits until first of this operations links any block back. 01334 */ 01335 01336 static BLOCK_LINK *find_key_block(KEY_CACHE *keycache, 01337 File file, my_off_t filepos, 01338 int init_hits_left, 01339 int wrmode, int *page_st) 01340 { 01341 HASH_LINK *hash_link; 01342 BLOCK_LINK *block; 01343 int error= 0; 01344 int page_status; 01345 01346 DBUG_ENTER("find_key_block"); 01347 KEYCACHE_THREAD_TRACE("find_key_block:begin"); 01348 DBUG_PRINT("enter", ("fd: %u pos %lu wrmode: %lu", 01349 (uint) file, (ulong) filepos, (uint) wrmode)); 01350 KEYCACHE_DBUG_PRINT("find_key_block", ("fd: %u pos: %lu wrmode: %lu", 01351 (uint) file, (ulong) filepos, 01352 (uint) wrmode)); 01353 #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) 01354 DBUG_EXECUTE("check_keycache2", 01355 test_key_cache(keycache, "start of find_key_block", 0);); 01356 #endif 01357 01358 restart: 01359 /* Find the hash link for the requested page (file, filepos) */ 01360 hash_link= get_hash_link(keycache, file, filepos); 01361 01362 page_status= -1; 01363 if ((block= hash_link->block) && 01364 block->hash_link == hash_link && (block->status & BLOCK_READ)) 01365 page_status= PAGE_READ; 01366 01367 if (wrmode && keycache->resize_in_flush) 01368 { 01369 /* This is a write request during the flush phase of a resize operation */ 01370 01371 if (page_status != PAGE_READ) 01372 { 01373 /* We don't need the page in the cache: we are going to write on disk */ 01374 hash_link->requests--; 01375 unlink_hash(keycache, hash_link); 01376 return 0; 01377 } 01378 if (!(block->status & BLOCK_IN_FLUSH)) 01379 { 01380 hash_link->requests--; 01381 /* 01382 Remove block to invalidate the page in the block buffer 01383 as we are going to write directly on disk. 01384 Although we have an exlusive lock for the updated key part 01385 the control can be yieded by the current thread as we might 01386 have unfinished readers of other key parts in the block 01387 buffer. Still we are guaranteed not to have any readers 01388 of the key part we are writing into until the block is 01389 removed from the cache as we set the BLOCL_REASSIGNED 01390 flag (see the code below that handles reading requests). 01391 */ 01392 free_block(keycache, block); 01393 return 0; 01394 } 01395 /* Wait intil the page is flushed on disk */ 01396 hash_link->requests--; 01397 { 01398 #ifdef THREAD 01399 struct st_my_thread_var *thread= my_thread_var; 01400 add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); 01401 do 01402 { 01403 KEYCACHE_DBUG_PRINT("find_key_block: wait", 01404 ("suspend thread %ld", thread->id)); 01405 keycache_pthread_cond_wait(&thread->suspend, 01406 &keycache->cache_lock); 01407 } 01408 while(thread->next); 01409 #else 01410 KEYCACHE_DBUG_ASSERT(0); 01411 /* 01412 Given the use of "resize_in_flush", it seems impossible 01413 that this whole branch is ever entered in single-threaded case 01414 because "(wrmode && keycache->resize_in_flush)" cannot be true. 01415 TODO: Check this, and then put the whole branch into the 01416 "#ifdef THREAD" guard. 01417 */ 01418 #endif 01419 } 01420 /* Invalidate page in the block if it has not been done yet */ 01421 if (block->status) 01422 free_block(keycache, block); 01423 return 0; 01424 } 01425 01426 if (page_status == PAGE_READ && 01427 (block->status & (BLOCK_IN_SWITCH | BLOCK_REASSIGNED))) 01428 { 01429 /* This is a request for a page to be removed from cache */ 01430 01431 KEYCACHE_DBUG_PRINT("find_key_block", 01432 ("request for old page in block %u " 01433 "wrmode: %d block->status: %d", 01434 BLOCK_NUMBER(block), wrmode, block->status)); 01435 /* 01436 Only reading requests can proceed until the old dirty page is flushed, 01437 all others are to be suspended, then resubmitted 01438 */ 01439 if (!wrmode && !(block->status & BLOCK_REASSIGNED)) 01440 reg_requests(keycache, block, 1); 01441 else 01442 { 01443 hash_link->requests--; 01444 KEYCACHE_DBUG_PRINT("find_key_block", 01445 ("request waiting for old page to be saved")); 01446 { 01447 #ifdef THREAD 01448 struct st_my_thread_var *thread= my_thread_var; 01449 /* Put the request into the queue of those waiting for the old page */ 01450 add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); 01451 /* Wait until the request can be resubmitted */ 01452 do 01453 { 01454 KEYCACHE_DBUG_PRINT("find_key_block: wait", 01455 ("suspend thread %ld", thread->id)); 01456 keycache_pthread_cond_wait(&thread->suspend, 01457 &keycache->cache_lock); 01458 } 01459 while(thread->next); 01460 #else 01461 KEYCACHE_DBUG_ASSERT(0); 01462 /* No parallel requests in single-threaded case */ 01463 #endif 01464 } 01465 KEYCACHE_DBUG_PRINT("find_key_block", 01466 ("request for old page resubmitted")); 01467 /* Resubmit the request */ 01468 goto restart; 01469 } 01470 } 01471 else 01472 { 01473 /* This is a request for a new page or for a page not to be removed */ 01474 if (! block) 01475 { 01476 /* No block is assigned for the page yet */ 01477 if (keycache->blocks_unused) 01478 { 01479 if (keycache->free_block_list) 01480 { 01481 /* There is a block in the free list. */ 01482 block= keycache->free_block_list; 01483 keycache->free_block_list= block->next_used; 01484 block->next_used= NULL; 01485 } 01486 else 01487 { 01488 /* There are some never used blocks, take first of them */ 01489 block= &keycache->block_root[keycache->blocks_used]; 01490 block->buffer= ADD_TO_PTR(keycache->block_mem, 01491 ((ulong) keycache->blocks_used* 01492 keycache->key_cache_block_size), 01493 byte*); 01494 keycache->blocks_used++; 01495 } 01496 keycache->blocks_unused--; 01497 block->status= 0; 01498 block->length= 0; 01499 block->offset= keycache->key_cache_block_size; 01500 block->requests= 1; 01501 block->temperature= BLOCK_COLD; 01502 block->hits_left= init_hits_left; 01503 block->last_hit_time= 0; 01504 link_to_file_list(keycache, block, file, 0); 01505 block->hash_link= hash_link; 01506 hash_link->block= block; 01507 page_status= PAGE_TO_BE_READ; 01508 KEYCACHE_DBUG_PRINT("find_key_block", 01509 ("got free or never used block %u", 01510 BLOCK_NUMBER(block))); 01511 } 01512 else 01513 { 01514 /* There are no never used blocks, use a block from the LRU chain */ 01515 01516 /* 01517 Wait until a new block is added to the LRU chain; 01518 several threads might wait here for the same page, 01519 all of them must get the same block 01520 */ 01521 01522 #ifdef THREAD 01523 if (! keycache->used_last) 01524 { 01525 struct st_my_thread_var *thread= my_thread_var; 01526 thread->opt_info= (void *) hash_link; 01527 link_into_queue(&keycache->waiting_for_block, thread); 01528 do 01529 { 01530 KEYCACHE_DBUG_PRINT("find_key_block: wait", 01531 ("suspend thread %ld", thread->id)); 01532 keycache_pthread_cond_wait(&thread->suspend, 01533 &keycache->cache_lock); 01534 } 01535 while (thread->next); 01536 thread->opt_info= NULL; 01537 } 01538 #else 01539 KEYCACHE_DBUG_ASSERT(keycache->used_last); 01540 #endif 01541 block= hash_link->block; 01542 if (! block) 01543 { 01544 /* 01545 Take the first block from the LRU chain 01546 unlinking it from the chain 01547 */ 01548 block= keycache->used_last->next_used; 01549 block->hits_left= init_hits_left; 01550 block->last_hit_time= 0; 01551 reg_requests(keycache, block,1); 01552 hash_link->block= block; 01553 } 01554 01555 if (block->hash_link != hash_link && 01556 ! (block->status & BLOCK_IN_SWITCH) ) 01557 { 01558 /* this is a primary request for a new page */ 01559 block->status|= BLOCK_IN_SWITCH; 01560 01561 KEYCACHE_DBUG_PRINT("find_key_block", 01562 ("got block %u for new page", BLOCK_NUMBER(block))); 01563 01564 if (block->status & BLOCK_CHANGED) 01565 { 01566 /* The block contains a dirty page - push it out of the cache */ 01567 01568 KEYCACHE_DBUG_PRINT("find_key_block", ("block is dirty")); 01569 01570 keycache_pthread_mutex_unlock(&keycache->cache_lock); 01571 /* 01572 The call is thread safe because only the current 01573 thread might change the block->hash_link value 01574 */ 01575 error= my_pwrite(block->hash_link->file, 01576 block->buffer+block->offset, 01577 block->length - block->offset, 01578 block->hash_link->diskpos+ block->offset, 01579 MYF(MY_NABP | MY_WAIT_IF_FULL)); 01580 keycache_pthread_mutex_lock(&keycache->cache_lock); 01581 keycache->global_cache_write++; 01582 } 01583 01584 block->status|= BLOCK_REASSIGNED; 01585 if (block->hash_link) 01586 { 01587 /* 01588 Wait until all pending read requests 01589 for this page are executed 01590 (we could have avoided this waiting, if we had read 01591 a page in the cache in a sweep, without yielding control) 01592 */ 01593 wait_for_readers(keycache, block); 01594 01595 /* Remove the hash link for this page from the hash table */ 01596 unlink_hash(keycache, block->hash_link); 01597 /* All pending requests for this page must be resubmitted */ 01598 #ifdef THREAD 01599 if (block->wqueue[COND_FOR_SAVED].last_thread) 01600 release_queue(&block->wqueue[COND_FOR_SAVED]); 01601 #endif 01602 } 01603 link_to_file_list(keycache, block, file, 01604 (my_bool)(block->hash_link ? 1 : 0)); 01605 block->status= error? BLOCK_ERROR : 0; 01606 block->length= 0; 01607 block->offset= keycache->key_cache_block_size; 01608 block->hash_link= hash_link; 01609 page_status= PAGE_TO_BE_READ; 01610 01611 KEYCACHE_DBUG_ASSERT(block->hash_link->block == block); 01612 KEYCACHE_DBUG_ASSERT(hash_link->block->hash_link == hash_link); 01613 } 01614 else 01615 { 01616 /* This is for secondary requests for a new page only */ 01617 KEYCACHE_DBUG_PRINT("find_key_block", 01618 ("block->hash_link: %p hash_link: %p " 01619 "block->status: %u", block->hash_link, 01620 hash_link, block->status )); 01621 page_status= (((block->hash_link == hash_link) && 01622 (block->status & BLOCK_READ)) ? 01623 PAGE_READ : PAGE_WAIT_TO_BE_READ); 01624 } 01625 } 01626 keycache->global_cache_read++; 01627 } 01628 else 01629 { 01630 reg_requests(keycache, block, 1); 01631 KEYCACHE_DBUG_PRINT("find_key_block", 01632 ("block->hash_link: %p hash_link: %p " 01633 "block->status: %u", block->hash_link, 01634 hash_link, block->status )); 01635 page_status= (((block->hash_link == hash_link) && 01636 (block->status & BLOCK_READ)) ? 01637 PAGE_READ : PAGE_WAIT_TO_BE_READ); 01638 } 01639 } 01640 01641 KEYCACHE_DBUG_ASSERT(page_status != -1); 01642 *page_st=page_status; 01643 KEYCACHE_DBUG_PRINT("find_key_block", 01644 ("fd: %u pos %lu block->status %u page_status %lu", 01645 (uint) file, (ulong) filepos, block->status, 01646 (uint) page_status)); 01647 01648 #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) 01649 DBUG_EXECUTE("check_keycache2", 01650 test_key_cache(keycache, "end of find_key_block",0);); 01651 #endif 01652 KEYCACHE_THREAD_TRACE("find_key_block:end"); 01653 DBUG_RETURN(block); 01654 } 01655 01656 01657 /* 01658 Read into a key cache block buffer from disk. 01659 01660 SYNOPSIS 01661 01662 read_block() 01663 keycache pointer to a key cache data structure 01664 block block to which buffer the data is to be read 01665 read_length size of data to be read 01666 min_length at least so much data must be read 01667 primary <-> the current thread will read the data 01668 01669 RETURN VALUE 01670 None 01671 01672 NOTES. 01673 The function either reads a page data from file to the block buffer, 01674 or waits until another thread reads it. What page to read is determined 01675 by a block parameter - reference to a hash link for this page. 01676 If an error occurs THE BLOCK_ERROR bit is set in the block status. 01677 We do not report error when the size of successfully read 01678 portion is less than read_length, but not less than min_length. 01679 */ 01680 01681 static void read_block(KEY_CACHE *keycache __attribute__((unused)), 01682 BLOCK_LINK *block, uint read_length, 01683 uint min_length, my_bool primary) 01684 { 01685 uint got_length; 01686 01687 /* On entry cache_lock is locked */ 01688 01689 KEYCACHE_THREAD_TRACE("read_block"); 01690 if (primary) 01691 { 01692 /* 01693 This code is executed only by threads 01694 that submitted primary requests 01695 */ 01696 01697 KEYCACHE_DBUG_PRINT("read_block", 01698 ("page to be read by primary request")); 01699 01700 /* Page is not in buffer yet, is to be read from disk */ 01701 keycache_pthread_mutex_unlock(&keycache->cache_lock); 01702 /* 01703 Here other threads may step in and register as secondary readers. 01704 They will register in block->wqueue[COND_FOR_REQUESTED]. 01705 */ 01706 got_length= my_pread(block->hash_link->file, block->buffer, 01707 read_length, block->hash_link->diskpos, MYF(0)); 01708 keycache_pthread_mutex_lock(&keycache->cache_lock); 01709 if (got_length < min_length) 01710 block->status|= BLOCK_ERROR; 01711 else 01712 { 01713 block->status= BLOCK_READ; 01714 block->length= got_length; 01715 } 01716 KEYCACHE_DBUG_PRINT("read_block", 01717 ("primary request: new page in cache")); 01718 /* Signal that all pending requests for this page now can be processed */ 01719 #ifdef THREAD 01720 if (block->wqueue[COND_FOR_REQUESTED].last_thread) 01721 release_queue(&block->wqueue[COND_FOR_REQUESTED]); 01722 #endif 01723 } 01724 else 01725 { 01726 /* 01727 This code is executed only by threads 01728 that submitted secondary requests 01729 */ 01730 KEYCACHE_DBUG_PRINT("read_block", 01731 ("secondary request waiting for new page to be read")); 01732 { 01733 #ifdef THREAD 01734 struct st_my_thread_var *thread= my_thread_var; 01735 /* Put the request into a queue and wait until it can be processed */ 01736 add_to_queue(&block->wqueue[COND_FOR_REQUESTED], thread); 01737 do 01738 { 01739 KEYCACHE_DBUG_PRINT("read_block: wait", 01740 ("suspend thread %ld", thread->id)); 01741 keycache_pthread_cond_wait(&thread->suspend, 01742 &keycache->cache_lock); 01743 } 01744 while (thread->next); 01745 #else 01746 KEYCACHE_DBUG_ASSERT(0); 01747 /* No parallel requests in single-threaded case */ 01748 #endif 01749 } 01750 KEYCACHE_DBUG_PRINT("read_block", 01751 ("secondary request: new page in cache")); 01752 } 01753 } 01754 01755 01756 /* 01757 Read a block of data from a cached file into a buffer; 01758 01759 SYNOPSIS 01760 01761 key_cache_read() 01762 keycache pointer to a key cache data structure 01763 file handler for the file for the block of data to be read 01764 filepos position of the block of data in the file 01765 level determines the weight of the data 01766 buff buffer to where the data must be placed 01767 length length of the buffer 01768 block_length length of the block in the key cache buffer 01769 return_buffer return pointer to the key cache buffer with the data 01770 01771 RETURN VALUE 01772 Returns address from where the data is placed if sucessful, 0 - otherwise. 01773 01774 NOTES. 01775 The function ensures that a block of data of size length from file 01776 positioned at filepos is in the buffers for some key cache blocks. 01777 Then the function either copies the data into the buffer buff, or, 01778 if return_buffer is TRUE, it just returns the pointer to the key cache 01779 buffer with the data. 01780 Filepos must be a multiple of 'block_length', but it doesn't 01781 have to be a multiple of key_cache_block_size; 01782 */ 01783 01784 byte *key_cache_read(KEY_CACHE *keycache, 01785 File file, my_off_t filepos, int level, 01786 byte *buff, uint length, 01787 uint block_length __attribute__((unused)), 01788 int return_buffer __attribute__((unused))) 01789 { 01790 int error=0; 01791 uint offset= 0; 01792 byte *start= buff; 01793 DBUG_ENTER("key_cache_read"); 01794 DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u", 01795 (uint) file, (ulong) filepos, length)); 01796 01797 if (keycache->can_be_used) 01798 { 01799 /* Key cache is used */ 01800 reg1 BLOCK_LINK *block; 01801 uint read_length; 01802 uint status; 01803 int page_st; 01804 01805 offset= (uint) (filepos & (keycache->key_cache_block_size-1)); 01806 /* Read data in key_cache_block_size increments */ 01807 do 01808 { 01809 keycache_pthread_mutex_lock(&keycache->cache_lock); 01810 if (!keycache->can_be_used) 01811 { 01812 keycache_pthread_mutex_unlock(&keycache->cache_lock); 01813 goto no_key_cache; 01814 } 01815 filepos-= offset; 01816 read_length= length; 01817 set_if_smaller(read_length, keycache->key_cache_block_size-offset); 01818 KEYCACHE_DBUG_ASSERT(read_length > 0); 01819 01820 #ifndef THREAD 01821 if (block_length > keycache->key_cache_block_size || offset) 01822 return_buffer=0; 01823 #endif 01824 01825 inc_counter_for_resize_op(keycache); 01826 keycache->global_cache_r_requests++; 01827 block=find_key_block(keycache, file, filepos, level, 0, &page_st); 01828 if (block->status != BLOCK_ERROR && page_st != PAGE_READ) 01829 { 01830 /* The requested page is to be read into the block buffer */ 01831 read_block(keycache, block, 01832 keycache->key_cache_block_size, read_length+offset, 01833 (my_bool)(page_st == PAGE_TO_BE_READ)); 01834 } 01835 else if (! (block->status & BLOCK_ERROR) && 01836 block->length < read_length + offset) 01837 { 01838 /* 01839 Impossible if nothing goes wrong: 01840 this could only happen if we are using a file with 01841 small key blocks and are trying to read outside the file 01842 */ 01843 my_errno= -1; 01844 block->status|= BLOCK_ERROR; 01845 } 01846 01847 if (! ((status= block->status) & BLOCK_ERROR)) 01848 { 01849 #ifndef THREAD 01850 if (! return_buffer) 01851 #endif 01852 { 01853 #if !defined(SERIALIZED_READ_FROM_CACHE) 01854 keycache_pthread_mutex_unlock(&keycache->cache_lock); 01855 #endif 01856 01857 /* Copy data from the cache buffer */ 01858 if (!(read_length & 511)) 01859 bmove512(buff, block->buffer+offset, read_length); 01860 else 01861 memcpy(buff, block->buffer+offset, (size_t) read_length); 01862 01863 #if !defined(SERIALIZED_READ_FROM_CACHE) 01864 keycache_pthread_mutex_lock(&keycache->cache_lock); 01865 #endif 01866 } 01867 } 01868 01869 remove_reader(block); 01870 /* 01871 Link the block into the LRU chain 01872 if it's the last submitted request for the block 01873 */ 01874 unreg_request(keycache, block, 1); 01875 01876 dec_counter_for_resize_op(keycache); 01877 01878 keycache_pthread_mutex_unlock(&keycache->cache_lock); 01879 01880 if (status & BLOCK_ERROR) 01881 DBUG_RETURN((byte *) 0); 01882 01883 #ifndef THREAD 01884 /* This is only true if we where able to read everything in one block */ 01885 if (return_buffer) 01886 DBUG_RETURN(block->buffer); 01887 #endif 01888 buff+= read_length; 01889 filepos+= read_length+offset; 01890 offset= 0; 01891 01892 } while ((length-= read_length)); 01893 DBUG_RETURN(start); 01894 } 01895 01896 no_key_cache: /* Key cache is not used */ 01897 01898 /* We can't use mutex here as the key cache may not be initialized */ 01899 keycache->global_cache_r_requests++; 01900 keycache->global_cache_read++; 01901 if (my_pread(file, (byte*) buff, length, filepos+offset, MYF(MY_NABP))) 01902 error= 1; 01903 DBUG_RETURN(error ? (byte*) 0 : start); 01904 } 01905 01906 01907 /* 01908 Insert a block of file data from a buffer into key cache 01909 01910 SYNOPSIS 01911 key_cache_insert() 01912 keycache pointer to a key cache data structure 01913 file handler for the file to insert data from 01914 filepos position of the block of data in the file to insert 01915 level determines the weight of the data 01916 buff buffer to read data from 01917 length length of the data in the buffer 01918 01919 NOTES 01920 This is used by MyISAM to move all blocks from a index file to the key 01921 cache 01922 01923 RETURN VALUE 01924 0 if a success, 1 - otherwise. 01925 */ 01926 01927 int key_cache_insert(KEY_CACHE *keycache, 01928 File file, my_off_t filepos, int level, 01929 byte *buff, uint length) 01930 { 01931 DBUG_ENTER("key_cache_insert"); 01932 DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u", 01933 (uint) file,(ulong) filepos, length)); 01934 01935 if (keycache->can_be_used) 01936 { 01937 /* Key cache is used */ 01938 reg1 BLOCK_LINK *block; 01939 uint read_length; 01940 int page_st; 01941 int error; 01942 uint offset; 01943 01944 offset= (uint) (filepos & (keycache->key_cache_block_size-1)); 01945 do 01946 { 01947 keycache_pthread_mutex_lock(&keycache->cache_lock); 01948 if (!keycache->can_be_used) 01949 { 01950 keycache_pthread_mutex_unlock(&keycache->cache_lock); 01951 DBUG_RETURN(0); 01952 } 01953 /* Read data into key cache from buff in key_cache_block_size incr. */ 01954 filepos-= offset; 01955 read_length= length; 01956 set_if_smaller(read_length, keycache->key_cache_block_size-offset); 01957 KEYCACHE_DBUG_ASSERT(read_length > 0); 01958 01959 inc_counter_for_resize_op(keycache); 01960 keycache->global_cache_r_requests++; 01961 block= find_key_block(keycache, file, filepos, level, 0, &page_st); 01962 if (block->status != BLOCK_ERROR && page_st != PAGE_READ) 01963 { 01964 /* The requested page is to be read into the block buffer */ 01965 #if !defined(SERIALIZED_READ_FROM_CACHE) 01966 keycache_pthread_mutex_unlock(&keycache->cache_lock); 01967 /* 01968 Here other threads may step in and register as secondary readers. 01969 They will register in block->wqueue[COND_FOR_REQUESTED]. 01970 */ 01971 #endif 01972 01973 /* Copy data from buff */ 01974 if (!(read_length & 511)) 01975 bmove512(block->buffer+offset, buff, read_length); 01976 else 01977 memcpy(block->buffer+offset, buff, (size_t) read_length); 01978 01979 #if !defined(SERIALIZED_READ_FROM_CACHE) 01980 keycache_pthread_mutex_lock(&keycache->cache_lock); 01981 /* Here we are alone again. */ 01982 #endif 01983 block->status= BLOCK_READ; 01984 block->length= read_length+offset; 01985 KEYCACHE_DBUG_PRINT("key_cache_insert", 01986 ("primary request: new page in cache")); 01987 #ifdef THREAD 01988 /* Signal that all pending requests for this now can be processed. */ 01989 if (block->wqueue[COND_FOR_REQUESTED].last_thread) 01990 release_queue(&block->wqueue[COND_FOR_REQUESTED]); 01991 #endif 01992 } 01993 01994 remove_reader(block); 01995 /* 01996 Link the block into the LRU chain 01997 if it's the last submitted request for the block 01998 */ 01999 unreg_request(keycache, block, 1); 02000 02001 error= (block->status & BLOCK_ERROR); 02002 02003 dec_counter_for_resize_op(keycache); 02004 02005 keycache_pthread_mutex_unlock(&keycache->cache_lock); 02006 02007 if (error) 02008 DBUG_RETURN(1); 02009 02010 buff+= read_length; 02011 filepos+= read_length+offset; 02012 offset= 0; 02013 02014 } while ((length-= read_length)); 02015 } 02016 DBUG_RETURN(0); 02017 } 02018 02019 02020 /* 02021 Write a buffer into a cached file. 02022 02023 SYNOPSIS 02024 02025 key_cache_write() 02026 keycache pointer to a key cache data structure 02027 file handler for the file to write data to 02028 filepos position in the file to write data to 02029 level determines the weight of the data 02030 buff buffer with the data 02031 length length of the buffer 02032 dont_write if is 0 then all dirty pages involved in writing 02033 should have been flushed from key cache 02034 02035 RETURN VALUE 02036 0 if a success, 1 - otherwise. 02037 02038 NOTES. 02039 The function copies the data of size length from buff into buffers 02040 for key cache blocks that are assigned to contain the portion of 02041 the file starting with position filepos. 02042 It ensures that this data is flushed to the file if dont_write is FALSE. 02043 Filepos must be a multiple of 'block_length', but it doesn't 02044 have to be a multiple of key_cache_block_size; 02045 */ 02046 02047 int key_cache_write(KEY_CACHE *keycache, 02048 File file, my_off_t filepos, int level, 02049 byte *buff, uint length, 02050 uint block_length __attribute__((unused)), 02051 int dont_write) 02052 { 02053 reg1 BLOCK_LINK *block; 02054 int error=0; 02055 DBUG_ENTER("key_cache_write"); 02056 DBUG_PRINT("enter", 02057 ("fd: %u pos: %lu length: %u block_length: %u key_block_length: %u", 02058 (uint) file, (ulong) filepos, length, block_length, 02059 keycache ? keycache->key_cache_block_size : 0)); 02060 02061 if (!dont_write) 02062 { 02063 /* Force writing from buff into disk */ 02064 keycache->global_cache_write++; 02065 if (my_pwrite(file, buff, length, filepos, MYF(MY_NABP | MY_WAIT_IF_FULL))) 02066 DBUG_RETURN(1); 02067 } 02068 02069 #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) 02070 DBUG_EXECUTE("check_keycache", 02071 test_key_cache(keycache, "start of key_cache_write", 1);); 02072 #endif 02073 02074 if (keycache->can_be_used) 02075 { 02076 /* Key cache is used */ 02077 uint read_length; 02078 int page_st; 02079 uint offset; 02080 02081 offset= (uint) (filepos & (keycache->key_cache_block_size-1)); 02082 do 02083 { 02084 keycache_pthread_mutex_lock(&keycache->cache_lock); 02085 if (!keycache->can_be_used) 02086 { 02087 keycache_pthread_mutex_unlock(&keycache->cache_lock); 02088 goto no_key_cache; 02089 } 02090 /* Write data in key_cache_block_size increments */ 02091 filepos-= offset; 02092 read_length= length; 02093 set_if_smaller(read_length, keycache->key_cache_block_size-offset); 02094 KEYCACHE_DBUG_ASSERT(read_length > 0); 02095 02096 inc_counter_for_resize_op(keycache); 02097 keycache->global_cache_w_requests++; 02098 block= find_key_block(keycache, file, filepos, level, 1, &page_st); 02099 if (!block) 02100 { 02101 /* It happens only for requests submitted during resize operation */ 02102 dec_counter_for_resize_op(keycache); 02103 keycache_pthread_mutex_unlock(&keycache->cache_lock); 02104 if (dont_write) 02105 { 02106 keycache->global_cache_w_requests++; 02107 keycache->global_cache_write++; 02108 if (my_pwrite(file, (byte*) buff, length, filepos, 02109 MYF(MY_NABP | MY_WAIT_IF_FULL))) 02110 error=1; 02111 } 02112 goto next_block; 02113 } 02114 02115 if (block->status != BLOCK_ERROR && page_st != PAGE_READ && 02116 (offset || read_length < keycache->key_cache_block_size)) 02117 read_block(keycache, block, 02118 offset + read_length >= keycache->key_cache_block_size? 02119 offset : keycache->key_cache_block_size, 02120 offset,(my_bool)(page_st == PAGE_TO_BE_READ)); 02121 02122 if (!dont_write) 02123 { 02124 /* buff has been written to disk at start */ 02125 if ((block->status & BLOCK_CHANGED) && 02126 (!offset && read_length >= keycache->key_cache_block_size)) 02127 link_to_file_list(keycache, block, block->hash_link->file, 1); 02128 } 02129 else if (! (block->status & BLOCK_CHANGED)) 02130 link_to_changed_list(keycache, block); 02131 02132 set_if_smaller(block->offset, offset); 02133 set_if_bigger(block->length, read_length+offset); 02134 02135 if (! (block->status & BLOCK_ERROR)) 02136 { 02137 if (!(read_length & 511)) 02138 bmove512(block->buffer+offset, buff, read_length); 02139 else 02140 memcpy(block->buffer+offset, buff, (size_t) read_length); 02141 } 02142 02143 block->status|=BLOCK_READ; 02144 02145 /* Unregister the request */ 02146 block->hash_link->requests--; 02147 unreg_request(keycache, block, 1); 02148 02149 if (block->status & BLOCK_ERROR) 02150 { 02151 keycache_pthread_mutex_unlock(&keycache->cache_lock); 02152 error= 1; 02153 break; 02154 } 02155 02156 dec_counter_for_resize_op(keycache); 02157 02158 keycache_pthread_mutex_unlock(&keycache->cache_lock); 02159 02160 next_block: 02161 buff+= read_length; 02162 filepos+= read_length+offset; 02163 offset= 0; 02164 02165 } while ((length-= read_length)); 02166 goto end; 02167 } 02168 02169 no_key_cache: 02170 /* Key cache is not used */ 02171 if (dont_write) 02172 { 02173 keycache->global_cache_w_requests++; 02174 keycache->global_cache_write++; 02175 if (my_pwrite(file, (byte*) buff, length, filepos, 02176 MYF(MY_NABP | MY_WAIT_IF_FULL))) 02177 error=1; 02178 } 02179 02180 end: 02181 #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) 02182 DBUG_EXECUTE("exec", 02183 test_key_cache(keycache, "end of key_cache_write", 1);); 02184 #endif 02185 DBUG_RETURN(error); 02186 } 02187 02188 02189 /* 02190 Free block: remove reference to it from hash table, 02191 remove it from the chain file of dirty/clean blocks 02192 and add it to the free list. 02193 */ 02194 02195 static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block) 02196 { 02197 KEYCACHE_THREAD_TRACE("free block"); 02198 KEYCACHE_DBUG_PRINT("free_block", 02199 ("block %u to be freed, hash_link %p", 02200 BLOCK_NUMBER(block), block->hash_link)); 02201 if (block->hash_link) 02202 { 02203 /* 02204 While waiting for readers to finish, new readers might request the 02205 block. But since we set block->status|= BLOCK_REASSIGNED, they 02206 will wait on block->wqueue[COND_FOR_SAVED]. They must be signalled 02207 later. 02208 */ 02209 block->status|= BLOCK_REASSIGNED; 02210 wait_for_readers(keycache, block); 02211 unlink_hash(keycache, block->hash_link); 02212 } 02213 02214 unlink_changed(block); 02215 block->status= 0; 02216 block->length= 0; 02217 block->offset= keycache->key_cache_block_size; 02218 KEYCACHE_THREAD_TRACE("free block"); 02219 KEYCACHE_DBUG_PRINT("free_block", 02220 ("block is freed")); 02221 unreg_request(keycache, block, 0); 02222 block->hash_link= NULL; 02223 02224 /* Remove the free block from the LRU ring. */ 02225 unlink_block(keycache, block); 02226 if (block->temperature == BLOCK_WARM) 02227 keycache->warm_blocks--; 02228 block->temperature= BLOCK_COLD; 02229 /* Insert the free block in the free list. */ 02230 block->next_used= keycache->free_block_list; 02231 keycache->free_block_list= block; 02232 /* Keep track of the number of currently unused blocks. */ 02233 keycache->blocks_unused++; 02234 02235 #ifdef THREAD 02236 /* All pending requests for this page must be resubmitted. */ 02237 if (block->wqueue[COND_FOR_SAVED].last_thread) 02238 release_queue(&block->wqueue[COND_FOR_SAVED]); 02239 #endif 02240 } 02241 02242 02243 static int cmp_sec_link(BLOCK_LINK **a, BLOCK_LINK **b) 02244 { 02245 return (((*a)->hash_link->diskpos < (*b)->hash_link->diskpos) ? -1 : 02246 ((*a)->hash_link->diskpos > (*b)->hash_link->diskpos) ? 1 : 0); 02247 } 02248 02249 02250 /* 02251 Flush a portion of changed blocks to disk, 02252 free used blocks if requested 02253 */ 02254 02255 static int flush_cached_blocks(KEY_CACHE *keycache, 02256 File file, BLOCK_LINK **cache, 02257 BLOCK_LINK **end, 02258 enum flush_type type) 02259 { 02260 int error; 02261 int last_errno= 0; 02262 uint count= (uint) (end-cache); 02263 02264 /* Don't lock the cache during the flush */ 02265 keycache_pthread_mutex_unlock(&keycache->cache_lock); 02266 /* 02267 As all blocks referred in 'cache' are marked by BLOCK_IN_FLUSH 02268 we are guarunteed no thread will change them 02269 */ 02270 qsort((byte*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link); 02271 02272 keycache_pthread_mutex_lock(&keycache->cache_lock); 02273 for ( ; cache != end ; cache++) 02274 { 02275 BLOCK_LINK *block= *cache; 02276 02277 KEYCACHE_DBUG_PRINT("flush_cached_blocks", 02278 ("block %u to be flushed", BLOCK_NUMBER(block))); 02279 keycache_pthread_mutex_unlock(&keycache->cache_lock); 02280 error= my_pwrite(file, 02281 block->buffer+block->offset, 02282 block->length - block->offset, 02283 block->hash_link->diskpos+ block->offset, 02284 MYF(MY_NABP | MY_WAIT_IF_FULL)); 02285 keycache_pthread_mutex_lock(&keycache->cache_lock); 02286 keycache->global_cache_write++; 02287 if (error) 02288 { 02289 block->status|= BLOCK_ERROR; 02290 if (!last_errno) 02291 last_errno= errno ? errno : -1; 02292 } 02293 #ifdef THREAD 02294 /* 02295 Let to proceed for possible waiting requests to write to the block page. 02296 It might happen only during an operation to resize the key cache. 02297 */ 02298 if (block->wqueue[COND_FOR_SAVED].last_thread) 02299 release_queue(&block->wqueue[COND_FOR_SAVED]); 02300 #endif 02301 /* type will never be FLUSH_IGNORE_CHANGED here */ 02302 if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) 02303 { 02304 keycache->blocks_changed--; 02305 keycache->global_blocks_changed--; 02306 free_block(keycache, block); 02307 } 02308 else 02309 { 02310 block->status&= ~BLOCK_IN_FLUSH; 02311 link_to_file_list(keycache, block, file, 1); 02312 unreg_request(keycache, block, 1); 02313 } 02314 02315 } 02316 return last_errno; 02317 } 02318 02319 02320 /* 02321 flush all key blocks for a file to disk, but don't do any mutex locks 02322 02323 flush_key_blocks_int() 02324 keycache pointer to a key cache data structure 02325 file handler for the file to flush to 02326 flush_type type of the flush 02327 02328 NOTES 02329 This function doesn't do any mutex locks because it needs to be called both 02330 from flush_key_blocks and flush_all_key_blocks (the later one does the 02331 mutex lock in the resize_key_cache() function). 02332 02333 RETURN 02334 0 ok 02335 1 error 02336 */ 02337 02338 static int flush_key_blocks_int(KEY_CACHE *keycache, 02339 File file, enum flush_type type) 02340 { 02341 BLOCK_LINK *cache_buff[FLUSH_CACHE],**cache; 02342 int last_errno= 0; 02343 DBUG_ENTER("flush_key_blocks_int"); 02344 DBUG_PRINT("enter",("file: %d blocks_used: %d blocks_changed: %d", 02345 file, keycache->blocks_used, keycache->blocks_changed)); 02346 02347 #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) 02348 DBUG_EXECUTE("check_keycache", 02349 test_key_cache(keycache, "start of flush_key_blocks", 0);); 02350 #endif 02351 02352 cache= cache_buff; 02353 if (keycache->disk_blocks > 0 && 02354 (!my_disable_flush_key_blocks || type != FLUSH_KEEP)) 02355 { 02356 /* Key cache exists and flush is not disabled */ 02357 int error= 0; 02358 uint count= 0; 02359 BLOCK_LINK **pos,**end; 02360 BLOCK_LINK *first_in_switch= NULL; 02361 BLOCK_LINK *block, *next; 02362 #if defined(KEYCACHE_DEBUG) 02363 uint cnt=0; 02364 #endif 02365 02366 if (type != FLUSH_IGNORE_CHANGED) 02367 { 02368 /* 02369 Count how many key blocks we have to cache to be able 02370 to flush all dirty pages with minimum seek moves 02371 */ 02372 for (block= keycache->changed_blocks[FILE_HASH(file)] ; 02373 block ; 02374 block= block->next_changed) 02375 { 02376 if (block->hash_link->file == file) 02377 { 02378 count++; 02379 KEYCACHE_DBUG_ASSERT(count<= keycache->blocks_used); 02380 } 02381 } 02382 /* Allocate a new buffer only if its bigger than the one we have */ 02383 if (count > FLUSH_CACHE && 02384 !(cache= (BLOCK_LINK**) my_malloc(sizeof(BLOCK_LINK*)*count, 02385 MYF(0)))) 02386 { 02387 cache= cache_buff; 02388 count= FLUSH_CACHE; 02389 } 02390 } 02391 02392 /* Retrieve the blocks and write them to a buffer to be flushed */ 02393 restart: 02394 end= (pos= cache)+count; 02395 for (block= keycache->changed_blocks[FILE_HASH(file)] ; 02396 block ; 02397 block= next) 02398 { 02399 #if defined(KEYCACHE_DEBUG) 02400 cnt++; 02401 KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used); 02402 #endif 02403 next= block->next_changed; 02404 if (block->hash_link->file == file) 02405 { 02406 /* 02407 Mark the block with BLOCK_IN_FLUSH in order not to let 02408 other threads to use it for new pages and interfere with 02409 our sequence ot flushing dirty file pages 02410 */ 02411 block->status|= BLOCK_IN_FLUSH; 02412 02413 if (! (block->status & BLOCK_IN_SWITCH)) 02414 { 02415 /* 02416 We care only for the blocks for which flushing was not 02417 initiated by other threads as a result of page swapping 02418 */ 02419 reg_requests(keycache, block, 1); 02420 if (type != FLUSH_IGNORE_CHANGED) 02421 { 02422 /* It's not a temporary file */ 02423 if (pos == end) 02424 { 02425 /* 02426 This happens only if there is not enough 02427 memory for the big block 02428 */ 02429 if ((error= flush_cached_blocks(keycache, file, cache, 02430 end,type))) 02431 last_errno=error; 02432 /* 02433 Restart the scan as some other thread might have changed 02434 the changed blocks chain: the blocks that were in switch 02435 state before the flush started have to be excluded 02436 */ 02437 goto restart; 02438 } 02439 *pos++= block; 02440 } 02441 else 02442 { 02443 /* It's a temporary file */ 02444 keycache->blocks_changed--; 02445 keycache->global_blocks_changed--; 02446 free_block(keycache, block); 02447 } 02448 } 02449 else 02450 { 02451 /* Link the block into a list of blocks 'in switch' */ 02452 unlink_changed(block); 02453 link_changed(block, &first_in_switch); 02454 } 02455 } 02456 } 02457 if (pos != cache) 02458 { 02459 if ((error= flush_cached_blocks(keycache, file, cache, pos, type))) 02460 last_errno= error; 02461 } 02462 /* Wait until list of blocks in switch is empty */ 02463 while (first_in_switch) 02464 { 02465 #if defined(KEYCACHE_DEBUG) 02466 cnt= 0; 02467 #endif 02468 block= first_in_switch; 02469 { 02470 #ifdef THREAD 02471 struct st_my_thread_var *thread= my_thread_var; 02472 add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); 02473 do 02474 { 02475 KEYCACHE_DBUG_PRINT("flush_key_blocks_int: wait", 02476 ("suspend thread %ld", thread->id)); 02477 keycache_pthread_cond_wait(&thread->suspend, 02478 &keycache->cache_lock); 02479 } 02480 while (thread->next); 02481 #else 02482 KEYCACHE_DBUG_ASSERT(0); 02483 /* No parallel requests in single-threaded case */ 02484 #endif 02485 } 02486 #if defined(KEYCACHE_DEBUG) 02487 cnt++; 02488 KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used); 02489 #endif 02490 } 02491 /* The following happens very seldom */ 02492 if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) 02493 { 02494 #if defined(KEYCACHE_DEBUG) 02495 cnt=0; 02496 #endif 02497 for (block= keycache->file_blocks[FILE_HASH(file)] ; 02498 block ; 02499 block= next) 02500 { 02501 #if defined(KEYCACHE_DEBUG) 02502 cnt++; 02503 KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used); 02504 #endif 02505 next= block->next_changed; 02506 if (block->hash_link->file == file && 02507 (! (block->status & BLOCK_CHANGED) 02508 || type == FLUSH_IGNORE_CHANGED)) 02509 { 02510 reg_requests(keycache, block, 1); 02511 free_block(keycache, block); 02512 } 02513 } 02514 } 02515 } 02516 02517 #ifndef DBUG_OFF 02518 DBUG_EXECUTE("check_keycache", 02519 test_key_cache(keycache, "end of flush_key_blocks", 0);); 02520 #endif 02521 if (cache != cache_buff) 02522 my_free((gptr) cache, MYF(0)); 02523 if (last_errno) 02524 errno=last_errno; /* Return first error */ 02525 DBUG_RETURN(last_errno != 0); 02526 } 02527 02528 02529 /* 02530 Flush all blocks for a file to disk 02531 02532 SYNOPSIS 02533 02534 flush_key_blocks() 02535 keycache pointer to a key cache data structure 02536 file handler for the file to flush to 02537 flush_type type of the flush 02538 02539 RETURN 02540 0 ok 02541 1 error 02542 */ 02543 02544 int flush_key_blocks(KEY_CACHE *keycache, 02545 File file, enum flush_type type) 02546 { 02547 int res; 02548 DBUG_ENTER("flush_key_blocks"); 02549 DBUG_PRINT("enter", ("keycache: 0x%lx", keycache)); 02550 02551 if (keycache->disk_blocks <= 0) 02552 DBUG_RETURN(0); 02553 keycache_pthread_mutex_lock(&keycache->cache_lock); 02554 inc_counter_for_resize_op(keycache); 02555 res= flush_key_blocks_int(keycache, file, type); 02556 dec_counter_for_resize_op(keycache); 02557 keycache_pthread_mutex_unlock(&keycache->cache_lock); 02558 DBUG_RETURN(res); 02559 } 02560 02561 02562 /* 02563 Flush all blocks in the key cache to disk 02564 */ 02565 02566 static int flush_all_key_blocks(KEY_CACHE *keycache) 02567 { 02568 #if defined(KEYCACHE_DEBUG) 02569 uint cnt=0; 02570 #endif 02571 while (keycache->blocks_changed > 0) 02572 { 02573 BLOCK_LINK *block; 02574 for (block= keycache->used_last->next_used ; ; block=block->next_used) 02575 { 02576 if (block->hash_link) 02577 { 02578 #if defined(KEYCACHE_DEBUG) 02579 cnt++; 02580 KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used); 02581 #endif 02582 if (flush_key_blocks_int(keycache, block->hash_link->file, 02583 FLUSH_RELEASE)) 02584 return 1; 02585 break; 02586 } 02587 if (block == keycache->used_last) 02588 break; 02589 } 02590 } 02591 return 0; 02592 } 02593 02594 02595 /* 02596 Reset the counters of a key cache. 02597 02598 SYNOPSIS 02599 reset_key_cache_counters() 02600 name the name of a key cache 02601 key_cache pointer to the key kache to be reset 02602 02603 DESCRIPTION 02604 This procedure is used by process_key_caches() to reset the counters of all 02605 currently used key caches, both the default one and the named ones. 02606 02607 RETURN 02608 0 on success (always because it can't fail) 02609 */ 02610 02611 int reset_key_cache_counters(const char *name, KEY_CACHE *key_cache) 02612 { 02613 DBUG_ENTER("reset_key_cache_counters"); 02614 if (!key_cache->key_cache_inited) 02615 { 02616 DBUG_PRINT("info", ("Key cache %s not initialized.", name)); 02617 DBUG_RETURN(0); 02618 } 02619 DBUG_PRINT("info", ("Resetting counters for key cache %s.", name)); 02620 02621 key_cache->global_blocks_changed= 0; /* Key_blocks_not_flushed */ 02622 key_cache->global_cache_r_requests= 0; /* Key_read_requests */ 02623 key_cache->global_cache_read= 0; /* Key_reads */ 02624 key_cache->global_cache_w_requests= 0; /* Key_write_requests */ 02625 key_cache->global_cache_write= 0; /* Key_writes */ 02626 DBUG_RETURN(0); 02627 } 02628 02629 02630 #ifndef DBUG_OFF 02631 /* 02632 Test if disk-cache is ok 02633 */ 02634 static void test_key_cache(KEY_CACHE *keycache __attribute__((unused)), 02635 const char *where __attribute__((unused)), 02636 my_bool lock __attribute__((unused))) 02637 { 02638 /* TODO */ 02639 } 02640 #endif 02641 02642 #if defined(KEYCACHE_TIMEOUT) 02643 02644 #define KEYCACHE_DUMP_FILE "keycache_dump.txt" 02645 #define MAX_QUEUE_LEN 100 02646 02647 02648 static void keycache_dump(KEY_CACHE *keycache) 02649 { 02650 FILE *keycache_dump_file=fopen(KEYCACHE_DUMP_FILE, "w"); 02651 struct st_my_thread_var *last; 02652 struct st_my_thread_var *thread; 02653 BLOCK_LINK *block; 02654 HASH_LINK *hash_link; 02655 KEYCACHE_PAGE *page; 02656 uint i; 02657 02658 fprintf(keycache_dump_file, "thread:%u\n", thread->id); 02659 02660 i=0; 02661 thread=last=waiting_for_hash_link.last_thread; 02662 fprintf(keycache_dump_file, "queue of threads waiting for hash link\n"); 02663 if (thread) 02664 do 02665 { 02666 thread=thread->next; 02667 page= (KEYCACHE_PAGE *) thread->opt_info; 02668 fprintf(keycache_dump_file, 02669 "thread:%u, (file,filepos)=(%u,%lu)\n", 02670 thread->id,(uint) page->file,(ulong) page->filepos); 02671 if (++i == MAX_QUEUE_LEN) 02672 break; 02673 } 02674 while (thread != last); 02675 02676 i=0; 02677 thread=last=waiting_for_block.last_thread; 02678 fprintf(keycache_dump_file, "queue of threads waiting for block\n"); 02679 if (thread) 02680 do 02681 { 02682 thread=thread->next; 02683 hash_link= (HASH_LINK *) thread->opt_info; 02684 fprintf(keycache_dump_file, 02685 "thread:%u hash_link:%u (file,filepos)=(%u,%lu)\n", 02686 thread->id, (uint) HASH_LINK_NUMBER(hash_link), 02687 (uint) hash_link->file,(ulong) hash_link->diskpos); 02688 if (++i == MAX_QUEUE_LEN) 02689 break; 02690 } 02691 while (thread != last); 02692 02693 for (i=0 ; i< keycache->blocks_used ; i++) 02694 { 02695 int j; 02696 block= &keycache->block_root[i]; 02697 hash_link= block->hash_link; 02698 fprintf(keycache_dump_file, 02699 "block:%u hash_link:%d status:%x #requests=%u waiting_for_readers:%d\n", 02700 i, (int) (hash_link ? HASH_LINK_NUMBER(hash_link) : -1), 02701 block->status, block->requests, block->condvar ? 1 : 0); 02702 for (j=0 ; j < 2; j++) 02703 { 02704 KEYCACHE_WQUEUE *wqueue=&block->wqueue[j]; 02705 thread= last= wqueue->last_thread; 02706 fprintf(keycache_dump_file, "queue #%d\n", j); 02707 if (thread) 02708 { 02709 do 02710 { 02711 thread=thread->next; 02712 fprintf(keycache_dump_file, 02713 "thread:%u\n", thread->id); 02714 if (++i == MAX_QUEUE_LEN) 02715 break; 02716 } 02717 while (thread != last); 02718 } 02719 } 02720 } 02721 fprintf(keycache_dump_file, "LRU chain:"); 02722 block= keycache= used_last; 02723 if (block) 02724 { 02725 do 02726 { 02727 block= block->next_used; 02728 fprintf(keycache_dump_file, 02729 "block:%u, ", BLOCK_NUMBER(block)); 02730 } 02731 while (block != keycache->used_last); 02732 } 02733 fprintf(keycache_dump_file, "\n"); 02734 02735 fclose(keycache_dump_file); 02736 } 02737 02738 #endif /* defined(KEYCACHE_TIMEOUT) */ 02739 02740 #if defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) 02741 02742 02743 static int keycache_pthread_cond_wait(pthread_cond_t *cond, 02744 pthread_mutex_t *mutex) 02745 { 02746 int rc; 02747 struct timeval now; /* time when we started waiting */ 02748 struct timespec timeout; /* timeout value for the wait function */ 02749 struct timezone tz; 02750 #if defined(KEYCACHE_DEBUG) 02751 int cnt=0; 02752 #endif 02753 02754 /* Get current time */ 02755 gettimeofday(&now, &tz); 02756 /* Prepare timeout value */ 02757 timeout.tv_sec= now.tv_sec + KEYCACHE_TIMEOUT; 02758 /* 02759 timeval uses microseconds. 02760 timespec uses nanoseconds. 02761 1 nanosecond = 1000 micro seconds 02762 */ 02763 timeout.tv_nsec= now.tv_usec * 1000; 02764 KEYCACHE_THREAD_TRACE_END("started waiting"); 02765 #if defined(KEYCACHE_DEBUG) 02766 cnt++; 02767 if (cnt % 100 == 0) 02768 fprintf(keycache_debug_log, "waiting...\n"); 02769 fflush(keycache_debug_log); 02770 #endif 02771 rc= pthread_cond_timedwait(cond, mutex, &timeout); 02772 KEYCACHE_THREAD_TRACE_BEGIN("finished waiting"); 02773 if (rc == ETIMEDOUT || rc == ETIME) 02774 { 02775 #if defined(KEYCACHE_DEBUG) 02776 fprintf(keycache_debug_log,"aborted by keycache timeout\n"); 02777 fclose(keycache_debug_log); 02778 abort(); 02779 #endif 02780 keycache_dump(); 02781 } 02782 02783 #if defined(KEYCACHE_DEBUG) 02784 KEYCACHE_DBUG_ASSERT(rc != ETIMEDOUT); 02785 #else 02786 assert(rc != ETIMEDOUT); 02787 #endif 02788 return rc; 02789 } 02790 #else 02791 #if defined(KEYCACHE_DEBUG) 02792 static int keycache_pthread_cond_wait(pthread_cond_t *cond, 02793 pthread_mutex_t *mutex) 02794 { 02795 int rc; 02796 KEYCACHE_THREAD_TRACE_END("started waiting"); 02797 rc= pthread_cond_wait(cond, mutex); 02798 KEYCACHE_THREAD_TRACE_BEGIN("finished waiting"); 02799 return rc; 02800 } 02801 #endif 02802 #endif /* defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) */ 02803 02804 #if defined(KEYCACHE_DEBUG) 02805 02806 02807 static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex) 02808 { 02809 int rc; 02810 rc= pthread_mutex_lock(mutex); 02811 KEYCACHE_THREAD_TRACE_BEGIN(""); 02812 return rc; 02813 } 02814 02815 02816 static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex) 02817 { 02818 KEYCACHE_THREAD_TRACE_END(""); 02819 pthread_mutex_unlock(mutex); 02820 } 02821 02822 02823 static int keycache_pthread_cond_signal(pthread_cond_t *cond) 02824 { 02825 int rc; 02826 KEYCACHE_THREAD_TRACE("signal"); 02827 rc= pthread_cond_signal(cond); 02828 return rc; 02829 } 02830 02831 02832 #if defined(KEYCACHE_DEBUG_LOG) 02833 02834 02835 static void keycache_debug_print(const char * fmt,...) 02836 { 02837 va_list args; 02838 va_start(args,fmt); 02839 if (keycache_debug_log) 02840 { 02841 VOID(vfprintf(keycache_debug_log, fmt, args)); 02842 VOID(fputc('\n',keycache_debug_log)); 02843 } 02844 va_end(args); 02845 } 02846 #endif /* defined(KEYCACHE_DEBUG_LOG) */ 02847 02848 #if defined(KEYCACHE_DEBUG_LOG) 02849 02850 02851 void keycache_debug_log_close(void) 02852 { 02853 if (keycache_debug_log) 02854 fclose(keycache_debug_log); 02855 } 02856 #endif /* defined(KEYCACHE_DEBUG_LOG) */ 02857 02858 #endif /* defined(KEYCACHE_DEBUG) */
1.4.7

