nfs-ganesha 1.4

nfs_tcb.c

Go to the documentation of this file.
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, &gtcb_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, &gtcb_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, &gtcb_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 }