nfs-ganesha 1.4
|
00001 /* 00002 * vim:expandtab:shiftwidth=8:tabstop=8: 00003 * 00004 * Copyright CEA/DAM/DIF (2008) 00005 * contributeur : Venkateswararao Jujjuri jujjuri@gmail.com 00006 * 00007 * This program is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 3 of the License, or (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with this library; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 * 00021 * --------------------------------------- 00022 */ 00023 00032 #ifdef HAVE_CONFIG_H 00033 #include "config.h" 00034 #endif 00035 00036 #ifdef _SOLARIS 00037 #include "solaris_port.h" 00038 #endif 00039 00040 #include <stdio.h> 00041 #include <string.h> 00042 #include <pthread.h> 00043 #include <fcntl.h> 00044 #include "nfs_tcb.h" 00045 #include "nlm_list.h" 00046 00047 00048 pthread_mutex_t gtcb_mutex = PTHREAD_MUTEX_INITIALIZER; 00049 static struct glist_head tcb_head; 00050 pthread_cond_t active_threads_cond = PTHREAD_COND_INITIALIZER; 00051 unsigned int num_active_threads = 0; 00052 unsigned int num_existing_threads = 0; 00053 awaken_reason_t awaken_reason = AWAKEN_STARTUP; 00054 int num_pauses = 0; /* Number of things trying to pause - do not awaken until this goes to 0 */ 00055 pause_state_t pause_state = STATE_STARTUP; 00056 00057 const char *pause_reason_str[] = 00058 { 00059 "PAUSE_RELOAD_EXPORTS", 00060 "PAUSE_SHUTDOWN", 00061 }; 00062 00063 const char *awaken_reason_str[] = 00064 { 00065 "AWAKEN_STARTUP", 00066 "AWAKEN_RELOAD_EXPORTS", 00067 }; 00068 00069 const char *pause_state_str[] = 00070 { 00071 "STATE_STARTUP", 00072 "STATE_AWAKEN", 00073 "STATE_AWAKE", 00074 "STATE_PAUSE", 00075 "STATE_PAUSED", 00076 "STATE_EXIT", 00077 }; 00078 00079 const char *pause_rc_str[] = 00080 { 00081 "PAUSE_OK", 00082 "PAUSE_AWAKE", 00083 "PAUSE_PAUSE", 00084 "PAUSE_EXIT", 00085 }; 00086 00087 void tcb_head_init(void) 00088 { 00089 init_glist(&tcb_head); 00090 } 00091 00092 void tcb_insert(nfs_tcb_t *element) 00093 { 00094 P(gtcb_mutex); 00095 glist_add_tail(&tcb_head, &element->tcb_list); 00096 V(gtcb_mutex); 00097 } 00098 00099 void tcb_remove(nfs_tcb_t *element) 00100 { 00101 P(gtcb_mutex); 00102 glist_del(&element->tcb_list); 00103 V(gtcb_mutex); 00104 } 00105 00110 int tcb_new(nfs_tcb_t *element, char *name) 00111 { 00112 if(pthread_mutex_init(&(element->tcb_mutex), NULL) != 0) 00113 return -1; 00114 if(pthread_cond_init(&(element->tcb_condvar), NULL) != 0) 00115 return -1; 00116 sprintf(element->tcb_name, "%s", name); 00117 00118 element->tcb_state = STATE_STARTUP; 00119 00120 tcb_insert(element); 00121 00122 return 0; 00123 } 00124 00129 void wait_for_threads_to_exit() 00130 { 00131 struct timespec timeout; 00132 int t1, t2; 00133 unsigned int original_existing = num_existing_threads; 00134 00135 LogDebug(COMPONENT_THREAD, "Waiting for threads to exit"); 00136 t1 = time(NULL); 00137 while(num_existing_threads > 0) 00138 { 00139 LogDebug(COMPONENT_THREAD, 00140 "Waiting one second for threads to exit, still existing: %u", 00141 num_existing_threads); 00142 timeout.tv_sec = time(NULL) + 1; 00143 timeout.tv_nsec = 0; 00144 pthread_cond_timedwait(&active_threads_cond, >cb_mutex, &timeout); 00145 } 00146 t2 = time(NULL); 00147 LogInfo(COMPONENT_THREAD, 00148 "%u threads exited out of %u after %d seconds", 00149 original_existing - num_existing_threads, original_existing, t2 - t1); 00150 } 00151 00152 pause_rc _wait_for_threads_to_pause() 00153 { 00154 struct timespec timeout; 00155 int t1, t2; 00156 LogDebug(COMPONENT_THREAD, "Waiting for thread threads to sleep"); 00157 00158 t1 = time(NULL); 00159 while(num_active_threads > 0) 00160 { 00161 /* If we are now trying to exit, just shortcut, let our caller deal 00162 * with exiting. */ 00163 if(pause_state == STATE_EXIT) 00164 { 00165 t2 = time(NULL); 00166 LogInfo(COMPONENT_THREAD, 00167 "%u threads asleep of %u after %d seconds before interruption for shutdown", 00168 num_existing_threads - num_active_threads, 00169 num_existing_threads, 00170 t2 - t1); 00171 return PAUSE_EXIT; 00172 } 00173 00174 timeout.tv_sec = time(NULL) + 1; 00175 timeout.tv_nsec = 0; 00176 pthread_cond_timedwait(&active_threads_cond, >cb_mutex, &timeout); 00177 } 00178 t2 = time(NULL); 00179 LogInfo(COMPONENT_THREAD, "%u threads asleep out of %u after %d seconds", 00180 num_existing_threads - num_active_threads, 00181 num_existing_threads, t2 - t1); 00182 return PAUSE_OK; 00183 } 00184 00185 pause_rc wait_for_threads_to_pause() 00186 { 00187 pause_rc rc; 00188 P(gtcb_mutex); 00189 rc = _wait_for_threads_to_pause(); 00190 V(gtcb_mutex); 00191 00192 return rc; 00193 } 00194 00199 static pause_rc _wait_for_threads_to_awaken() 00200 { 00201 struct timespec timeout; 00202 int t1, t2; 00203 00204 LogDebug(COMPONENT_THREAD, "Waiting for threads to awaken. active=%u, existing=%u", 00205 num_active_threads, num_existing_threads); 00206 t1 = time(NULL); 00207 while(num_active_threads < num_existing_threads) 00208 { 00209 /* If trying to exit, don't bother waiting */ 00210 if(pause_state == STATE_EXIT) 00211 { 00212 t2 = time(NULL); 00213 LogInfo(COMPONENT_THREAD, 00214 "%u threads awake of %u after %d seconds before interruption for shutdown", 00215 num_active_threads, 00216 num_existing_threads, 00217 t2 - t1); 00218 return PAUSE_EXIT; 00219 } 00220 00221 /* If trying to pause, don't bother waiting */ 00222 if(pause_state == STATE_PAUSE || pause_state == STATE_PAUSED) 00223 { 00224 t2 = time(NULL); 00225 LogInfo(COMPONENT_THREAD, 00226 "%u threads awake of %u after %d seconds before interruption for pause", 00227 num_active_threads, num_existing_threads, t2 - t1); 00228 return PAUSE_PAUSE; 00229 } 00230 00231 timeout.tv_sec = time(NULL) + 10; 00232 timeout.tv_nsec = 0; 00233 pthread_cond_timedwait(&active_threads_cond, >cb_mutex, &timeout); 00234 } 00235 t2 = time(NULL); 00236 LogInfo(COMPONENT_THREAD, 00237 "%u threads awake out of %u after %d seconds", 00238 num_active_threads, num_existing_threads, t2 - t1); 00239 return PAUSE_OK; 00240 } 00241 00242 /* 00243 * This API must NOT be called by more than a single thread - the caller witll 00244 * wait on a cond-var & only once hread will be awakened by a notification. 00245 */ 00246 pause_rc wait_for_threads_to_awaken() 00247 { 00248 pause_rc rc; 00249 00250 P(gtcb_mutex); 00251 00252 rc = _wait_for_threads_to_awaken(); 00253 00254 V(gtcb_mutex); 00255 00256 return rc; 00257 } 00258 00263 void mark_thread_asleep(nfs_tcb_t *wcb) 00264 { 00265 P(gtcb_mutex); 00266 P(wcb->tcb_mutex); 00267 if(wcb->tcb_state == STATE_PAUSE) 00268 { 00269 wcb->tcb_state = STATE_PAUSED; 00270 num_active_threads--; 00271 wcb->tcb_ready = FALSE; 00272 } 00273 00274 pthread_cond_signal(&active_threads_cond); 00275 LogDebug(COMPONENT_THREAD, "%s asleep", wcb->tcb_name); 00276 00277 00278 V(wcb->tcb_mutex); 00279 V(gtcb_mutex); 00280 } 00281 00286 void mark_thread_done(nfs_tcb_t *wcb) 00287 { 00288 P(gtcb_mutex); 00289 P(wcb->tcb_mutex); 00290 00291 if(wcb->tcb_ready) 00292 { 00293 num_active_threads--; 00294 wcb->tcb_ready = FALSE; 00295 } 00296 00297 num_existing_threads--; 00298 00299 pthread_cond_signal(&active_threads_cond); 00300 LogDebug(COMPONENT_THREAD, "%s exiting", wcb->tcb_name); 00301 00302 V(wcb->tcb_mutex); 00303 V(gtcb_mutex); 00304 tcb_remove(wcb); 00305 } 00306 00311 pause_rc mark_thread_existing(nfs_tcb_t *wcb) 00312 { 00313 pause_rc rc; 00314 00315 P(gtcb_mutex); 00316 P(wcb->tcb_mutex); 00317 00318 /* Increment count of existing (even if we are about to die, 00319 * mark_thread_done will be called in that case). 00320 */ 00321 num_existing_threads++; 00322 00323 if(pause_state == STATE_EXIT) 00324 rc = PAUSE_EXIT; 00325 else 00326 rc = PAUSE_OK; 00327 00328 pthread_cond_signal(&active_threads_cond); 00329 LogDebug(COMPONENT_THREAD, "%s exists", wcb->tcb_name); 00330 00331 V(wcb->tcb_mutex); 00332 V(gtcb_mutex); 00333 return rc; 00334 } 00335 00340 void mark_thread_awake(nfs_tcb_t *wcb) 00341 { 00342 P(gtcb_mutex); 00343 P(wcb->tcb_mutex); 00344 00345 if(wcb->tcb_state == STATE_STARTUP || wcb->tcb_state == STATE_AWAKEN) 00346 { 00347 wcb->tcb_state = STATE_AWAKE; 00348 num_active_threads++; 00349 wcb->tcb_ready = TRUE; 00350 } 00351 00352 pthread_cond_signal(&active_threads_cond); 00353 LogDebug(COMPONENT_THREAD, "%s active", wcb->tcb_name); 00354 00355 V(wcb->tcb_mutex); 00356 V(gtcb_mutex); 00357 } 00358 00359 void notify_threads_of_new_state() 00360 { 00361 struct glist_head *node; 00362 glist_for_each(node, &tcb_head) 00363 { 00364 nfs_tcb_t *wcb = (nfs_tcb_t *)container_of(node, nfs_tcb_t, tcb_list); 00365 P(wcb->tcb_mutex); 00366 LogDebug(COMPONENT_THREAD, 00367 "Changing state of %s from %s to %s", 00368 wcb->tcb_name, 00369 pause_state_str[wcb->tcb_state], 00370 pause_state_str[pause_state]); 00371 wcb->tcb_state = pause_state; 00372 if(pthread_cond_signal(&(wcb->tcb_condvar)) == -1) 00373 { 00374 V(wcb->tcb_mutex); 00375 LogMajor(COMPONENT_THREAD, 00376 "Error %d (%s) while signalling %s... Exiting", 00377 errno, strerror(errno), wcb->tcb_name); 00378 Fatal(); 00379 } 00380 V(wcb->tcb_mutex); 00381 } 00382 } 00383 00388 pause_rc pause_threads(pause_reason_t reason) 00389 { 00390 pause_rc rc = PAUSE_OK; 00391 bool_t new_state = FALSE; 00392 bool_t wait = TRUE; 00393 00394 P(gtcb_mutex); 00395 LogDebug(COMPONENT_THREAD, 00396 "Pause threads for reason: %s pause_state: %s", 00397 pause_reason_str[reason], pause_state_str[pause_state]); 00398 00399 switch(reason) 00400 { 00401 case PAUSE_RELOAD_EXPORTS: 00402 num_pauses++; 00403 switch(pause_state) 00404 { 00405 case STATE_STARTUP: 00406 /* We need to wait for all threads to come up the first time 00407 * before we can think of trying to pause them. 00408 */ 00409 rc = _wait_for_threads_to_awaken(); 00410 if(rc != PAUSE_OK) 00411 { 00412 LogDebug(COMPONENT_THREAD, 00413 "pause threads for %s interrupted for shutdown", 00414 pause_reason_str[reason]); 00415 V(gtcb_mutex); 00416 return rc; 00417 } 00418 /* fall through */ 00419 00420 case STATE_AWAKEN: 00421 case STATE_AWAKE: 00422 pause_state = STATE_PAUSE; 00423 new_state = TRUE; 00424 break; 00425 case STATE_PAUSE: 00426 break; 00427 case STATE_PAUSED: 00428 /* Already paused, nothing to do. */ 00429 wait = FALSE; 00430 break; 00431 00432 case STATE_EXIT: 00433 /* Ganesha is trying to exit, the caller should exit also */ 00434 V(gtcb_mutex); 00435 return PAUSE_EXIT; 00436 } 00437 break; 00438 case PAUSE_SHUTDOWN: 00439 num_pauses++; 00440 if(pause_state == STATE_EXIT) 00441 { 00442 /* Already paused, nothing more to do. */ 00443 wait = FALSE; 00444 rc = PAUSE_EXIT; 00445 } 00446 else 00447 { 00448 /* Otherwise don't care about current state, startup will handle need to exit. */ 00449 pause_state = STATE_EXIT; 00450 new_state = TRUE; 00451 } 00452 break; 00453 } 00454 00455 if(new_state) 00456 notify_threads_of_new_state(); 00457 00458 /* Wait for all threads to pause or exit */ 00459 if(pause_state == STATE_EXIT && wait) 00460 { 00461 wait_for_threads_to_exit(); 00462 rc = PAUSE_EXIT; 00463 } 00464 else if(wait) 00465 { 00466 rc = _wait_for_threads_to_pause(); 00467 if(rc == PAUSE_OK && pause_state == STATE_PAUSE) 00468 pause_state = STATE_PAUSED; 00469 } 00470 00471 V(gtcb_mutex); 00472 00473 return rc; 00474 } 00475 00480 pause_rc wake_threads(awaken_reason_t reason) 00481 { 00482 pause_rc rc; 00483 bool_t new_state = FALSE; 00484 bool_t wait = TRUE; 00485 00486 P(gtcb_mutex); 00487 00488 LogDebug(COMPONENT_THREAD, 00489 "Wake threads for reason: %s pause_state: %s", 00490 awaken_reason_str[reason], pause_state_str[pause_state]); 00491 00492 switch(reason) 00493 { 00494 case AWAKEN_STARTUP: 00495 /* Just wait. */ 00496 break; 00497 00498 case AWAKEN_RELOAD_EXPORTS: 00499 num_pauses--; 00500 switch(pause_state) 00501 { 00502 case STATE_STARTUP: 00503 case STATE_AWAKEN: 00504 /* Already trying to awaken, just wait. */ 00505 break; 00506 00507 case STATE_PAUSE: 00508 case STATE_PAUSED: 00509 if(num_pauses != 0) 00510 { 00511 /* Don't actually wake up yet. */ 00512 V(gtcb_mutex); 00513 return PAUSE_PAUSE; 00514 } 00515 pause_state = STATE_AWAKEN; 00516 new_state = TRUE; 00517 break; 00518 00519 case STATE_AWAKE: 00520 /* Already awake, nothing to do. */ 00521 wait = FALSE; 00522 rc = PAUSE_OK; 00523 break; 00524 00525 case STATE_EXIT: 00526 /* Ganesha is trying to exit, the caller should exit also */ 00527 V(gtcb_mutex); 00528 return PAUSE_EXIT; 00529 } 00530 } 00531 00532 if(new_state) 00533 notify_threads_of_new_state(); 00534 00535 /* Wait for all threads to wake up */ 00536 if(wait) 00537 { 00538 rc = _wait_for_threads_to_awaken(); 00539 if(rc == PAUSE_OK) 00540 pause_state = STATE_AWAKE; 00541 } 00542 00543 V(gtcb_mutex); 00544 00545 return rc; 00546 } 00547 00548 thread_sm_t thread_sm_locked(nfs_tcb_t *tcbp) 00549 { 00550 switch(tcbp->tcb_state) 00551 { 00552 case STATE_AWAKE: 00553 return THREAD_SM_BREAK; 00554 00555 case STATE_STARTUP: 00556 case STATE_AWAKEN: 00557 V(tcbp->tcb_mutex); 00558 mark_thread_awake(tcbp); 00559 P(tcbp->tcb_mutex); 00560 return THREAD_SM_RECHECK; 00561 00562 case STATE_PAUSE: 00563 V(tcbp->tcb_mutex); 00564 mark_thread_asleep(tcbp); 00565 P(tcbp->tcb_mutex); 00566 return THREAD_SM_RECHECK; 00567 00568 case STATE_PAUSED: 00569 pthread_cond_wait(&(tcbp->tcb_condvar), &(tcbp->tcb_mutex)); 00570 return THREAD_SM_RECHECK; 00571 00572 case STATE_EXIT: 00573 V(tcbp->tcb_mutex); 00574 mark_thread_done(tcbp); 00575 P(tcbp->tcb_mutex); 00576 return THREAD_SM_EXIT; 00577 } 00578 LogCrit(COMPONENT_THREAD, "Thread %s has unknown state: %d \n", 00579 tcbp->tcb_name, tcbp->tcb_state); 00580 V(tcbp->tcb_mutex); 00581 mark_thread_done(tcbp); 00582 P(tcbp->tcb_mutex); 00583 return THREAD_SM_EXIT; 00584 }