LCOV - code coverage report
Current view: top level - lib - ovs-rcu.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 153 153 100.0 %
Date: 2016-09-14 01:02:56 Functions: 18 18 100.0 %
Branches: 46 50 92.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2014 Nicira, Inc.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :            :  * you may not use this file except in compliance with the License.
       6                 :            :  * You may obtain a copy of the License at:
       7                 :            :  *
       8                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :            :  *
      10                 :            :  * Unless required by applicable law or agreed to in writing, software
      11                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :            :  * See the License for the specific language governing permissions and
      14                 :            :  * limitations under the License.
      15                 :            :  */
      16                 :            : 
      17                 :            : #include <config.h>
      18                 :            : #include <errno.h>
      19                 :            : #include "ovs-rcu.h"
      20                 :            : #include "fatal-signal.h"
      21                 :            : #include "guarded-list.h"
      22                 :            : #include "openvswitch/list.h"
      23                 :            : #include "ovs-thread.h"
      24                 :            : #include "poll-loop.h"
      25                 :            : #include "seq.h"
      26                 :            : #include "timeval.h"
      27                 :            : #include "util.h"
      28                 :            : #include "openvswitch/vlog.h"
      29                 :            : 
      30                 :      53956 : VLOG_DEFINE_THIS_MODULE(ovs_rcu);
      31                 :            : 
      32                 :            : struct ovsrcu_cb {
      33                 :            :     void (*function)(void *aux);
      34                 :            :     void *aux;
      35                 :            : };
      36                 :            : 
      37                 :            : struct ovsrcu_cbset {
      38                 :            :     struct ovs_list list_node;
      39                 :            :     struct ovsrcu_cb cbs[16];
      40                 :            :     int n_cbs;
      41                 :            : };
      42                 :            : 
      43                 :            : struct ovsrcu_perthread {
      44                 :            :     struct ovs_list list_node;  /* In global list. */
      45                 :            : 
      46                 :            :     struct ovs_mutex mutex;
      47                 :            :     uint64_t seqno;
      48                 :            :     struct ovsrcu_cbset *cbset;
      49                 :            :     char name[16];              /* This thread's name. */
      50                 :            : };
      51                 :            : 
      52                 :            : static struct seq *global_seqno;
      53                 :            : 
      54                 :            : static pthread_key_t perthread_key;
      55                 :            : static struct ovs_list ovsrcu_threads;
      56                 :            : static struct ovs_mutex ovsrcu_threads_mutex;
      57                 :            : 
      58                 :            : static struct guarded_list flushed_cbsets;
      59                 :            : static struct seq *flushed_cbsets_seq;
      60                 :            : 
      61                 :            : static void ovsrcu_init_module(void);
      62                 :            : static void ovsrcu_flush_cbset__(struct ovsrcu_perthread *, bool);
      63                 :            : static void ovsrcu_flush_cbset(struct ovsrcu_perthread *);
      64                 :            : static void ovsrcu_unregister__(struct ovsrcu_perthread *);
      65                 :            : static bool ovsrcu_call_postponed(void);
      66                 :            : static void *ovsrcu_postpone_thread(void *arg OVS_UNUSED);
      67                 :            : 
      68                 :            : static struct ovsrcu_perthread *
      69                 :   19995024 : ovsrcu_perthread_get(void)
      70                 :            : {
      71                 :            :     struct ovsrcu_perthread *perthread;
      72                 :            : 
      73                 :   19995024 :     ovsrcu_init_module();
      74                 :            : 
      75                 :   19994844 :     perthread = pthread_getspecific(perthread_key);
      76         [ +  + ]:   19994899 :     if (!perthread) {
      77                 :     313962 :         const char *name = get_subprogram_name();
      78                 :            : 
      79                 :     313954 :         perthread = xmalloc(sizeof *perthread);
      80                 :     313963 :         ovs_mutex_init(&perthread->mutex);
      81                 :     313953 :         perthread->seqno = seq_read(global_seqno);
      82                 :     313963 :         perthread->cbset = NULL;
      83         [ +  + ]:     313963 :         ovs_strlcpy(perthread->name, name[0] ? name : "main",
      84                 :            :                     sizeof perthread->name);
      85                 :            : 
      86                 :     313964 :         ovs_mutex_lock(&ovsrcu_threads_mutex);
      87                 :     313964 :         ovs_list_push_back(&ovsrcu_threads, &perthread->list_node);
      88                 :     313964 :         ovs_mutex_unlock(&ovsrcu_threads_mutex);
      89                 :            : 
      90                 :     313964 :         pthread_setspecific(perthread_key, perthread);
      91                 :            :     }
      92                 :   19994857 :     return perthread;
      93                 :            : }
      94                 :            : 
      95                 :            : /* Indicates the end of a quiescent state.  See "Details" near the top of
      96                 :            :  * ovs-rcu.h.
      97                 :            :  *
      98                 :            :  * Quiescent states don't stack or nest, so this always ends a quiescent state
      99                 :            :  * even if ovsrcu_quiesce_start() was called multiple times in a row. */
     100                 :            : void
     101                 :     313840 : ovsrcu_quiesce_end(void)
     102                 :            : {
     103                 :     313840 :     ovsrcu_perthread_get();
     104                 :     313837 : }
     105                 :            : 
     106                 :            : static void
     107                 :   16430566 : ovsrcu_quiesced(void)
     108                 :            : {
     109         [ +  + ]:   16430566 :     if (single_threaded()) {
     110                 :     229217 :         ovsrcu_call_postponed();
     111                 :            :     } else {
     112                 :            :         static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
     113         [ +  + ]:   16201350 :         if (ovsthread_once_start(&once)) {
     114                 :        615 :             ovs_thread_create("urcu", ovsrcu_postpone_thread, NULL);
     115                 :        615 :             ovsthread_once_done(&once);
     116                 :            :         }
     117                 :            :     }
     118                 :   16430566 : }
     119                 :            : 
     120                 :            : /* Indicates the beginning of a quiescent state.  See "Details" near the top of
     121                 :            :  * ovs-rcu.h. */
     122                 :            : void
     123                 :     308607 : ovsrcu_quiesce_start(void)
     124                 :            : {
     125                 :            :     struct ovsrcu_perthread *perthread;
     126                 :            : 
     127                 :     308607 :     ovsrcu_init_module();
     128                 :     308609 :     perthread = pthread_getspecific(perthread_key);
     129         [ +  - ]:     308610 :     if (perthread) {
     130                 :     308610 :         pthread_setspecific(perthread_key, NULL);
     131                 :     308610 :         ovsrcu_unregister__(perthread);
     132                 :            :     }
     133                 :            : 
     134                 :     308610 :     ovsrcu_quiesced();
     135                 :     308609 : }
     136                 :            : 
     137                 :            : /* Indicates a momentary quiescent state.  See "Details" near the top of
     138                 :            :  * ovs-rcu.h.
     139                 :            :  *
     140                 :            :  * Provides a full memory barrier via seq_change().
     141                 :            :  */
     142                 :            : void
     143                 :   15901872 : ovsrcu_quiesce(void)
     144                 :            : {
     145                 :            :     struct ovsrcu_perthread *perthread;
     146                 :            : 
     147                 :   15901872 :     perthread = ovsrcu_perthread_get();
     148                 :   15901872 :     perthread->seqno = seq_read(global_seqno);
     149         [ +  + ]:   15901872 :     if (perthread->cbset) {
     150                 :     240610 :         ovsrcu_flush_cbset(perthread);
     151                 :            :     }
     152                 :   15901872 :     seq_change(global_seqno);
     153                 :            : 
     154                 :   15901872 :     ovsrcu_quiesced();
     155                 :   15901872 : }
     156                 :            : 
     157                 :            : int
     158                 :     257491 : ovsrcu_try_quiesce(void)
     159                 :            : {
     160                 :            :     struct ovsrcu_perthread *perthread;
     161                 :     257491 :     int ret = EBUSY;
     162                 :            : 
     163         [ -  + ]:     257491 :     ovs_assert(!single_threaded());
     164                 :     260225 :     perthread = ovsrcu_perthread_get();
     165         [ +  + ]:     260220 :     if (!seq_try_lock()) {
     166                 :     220093 :         perthread->seqno = seq_read_protected(global_seqno);
     167         [ +  + ]:     220093 :         if (perthread->cbset) {
     168                 :         29 :             ovsrcu_flush_cbset__(perthread, true);
     169                 :            :         }
     170                 :     220093 :         seq_change_protected(global_seqno);
     171                 :     220093 :         seq_unlock();
     172                 :     220093 :         ovsrcu_quiesced();
     173                 :     220093 :         ret = 0;
     174                 :            :     }
     175                 :     260229 :     return ret;
     176                 :            : }
     177                 :            : 
     178                 :            : bool
     179                 :     344757 : ovsrcu_is_quiescent(void)
     180                 :            : {
     181                 :     344757 :     ovsrcu_init_module();
     182                 :     344759 :     return pthread_getspecific(perthread_key) == NULL;
     183                 :            : }
     184                 :            : 
     185                 :            : void
     186                 :     281375 : ovsrcu_synchronize(void)
     187                 :            : {
     188                 :     281375 :     unsigned int warning_threshold = 1000;
     189                 :            :     uint64_t target_seqno;
     190                 :            :     long long int start;
     191                 :            : 
     192         [ +  + ]:     281375 :     if (single_threaded()) {
     193                 :     227811 :         return;
     194                 :            :     }
     195                 :            : 
     196                 :      53564 :     target_seqno = seq_read(global_seqno);
     197                 :      53564 :     ovsrcu_quiesce_start();
     198                 :      53564 :     start = time_msec();
     199                 :            : 
     200                 :            :     for (;;) {
     201                 :     105470 :         uint64_t cur_seqno = seq_read(global_seqno);
     202                 :            :         struct ovsrcu_perthread *perthread;
     203                 :            :         char stalled_thread[16];
     204                 :            :         unsigned int elapsed;
     205                 :     105470 :         bool done = true;
     206                 :            : 
     207                 :     105470 :         ovs_mutex_lock(&ovsrcu_threads_mutex);
     208         [ +  + ]:     118218 :         LIST_FOR_EACH (perthread, list_node, &ovsrcu_threads) {
     209         [ +  + ]:      65207 :             if (perthread->seqno <= target_seqno) {
     210                 :      52459 :                 ovs_strlcpy(stalled_thread, perthread->name,
     211                 :            :                             sizeof stalled_thread);
     212                 :      52459 :                 done = false;
     213                 :      52459 :                 break;
     214                 :            :             }
     215                 :            :         }
     216                 :     105470 :         ovs_mutex_unlock(&ovsrcu_threads_mutex);
     217                 :            : 
     218         [ +  + ]:     105470 :         if (done) {
     219                 :      53011 :             break;
     220                 :            :         }
     221                 :            : 
     222                 :      52459 :         elapsed = time_msec() - start;
     223         [ +  + ]:      52459 :         if (elapsed >= warning_threshold) {
     224         [ +  - ]:          1 :             VLOG_WARN("blocked %u ms waiting for %s to quiesce",
     225                 :            :                       elapsed, stalled_thread);
     226                 :          1 :             warning_threshold *= 2;
     227                 :            :         }
     228                 :      52459 :         poll_timer_wait_until(start + warning_threshold);
     229                 :            : 
     230                 :      52459 :         seq_wait(global_seqno, cur_seqno);
     231                 :      52459 :         poll_block();
     232                 :      51906 :     }
     233                 :      53011 :     ovsrcu_quiesce_end();
     234                 :            : }
     235                 :            : 
     236                 :            : /* Registers 'function' to be called, passing 'aux' as argument, after the
     237                 :            :  * next grace period.
     238                 :            :  *
     239                 :            :  * The call is guaranteed to happen after the next time all participating
     240                 :            :  * threads have quiesced at least once, but there is no quarantee that all
     241                 :            :  * registered functions are called as early as possible, or that the functions
     242                 :            :  * registered by different threads would be called in the order the
     243                 :            :  * registrations took place.  In particular, even if two threads provably
     244                 :            :  * register a function each in a specific order, the functions may still be
     245                 :            :  * called in the opposite order, depending on the timing of when the threads
     246                 :            :  * call ovsrcu_quiesce(), how many functions they postpone, and when the
     247                 :            :  * ovs-rcu thread happens to grab the functions to be called.
     248                 :            :  *
     249                 :            :  * All functions registered by a single thread are guaranteed to execute in the
     250                 :            :  * registering order, however.
     251                 :            :  *
     252                 :            :  * This function is more conveniently called through the ovsrcu_postpone()
     253                 :            :  * macro, which provides a type-safe way to allow 'function''s parameter to be
     254                 :            :  * any pointer type. */
     255                 :            : void
     256                 :    3519294 : ovsrcu_postpone__(void (*function)(void *aux), void *aux)
     257                 :            : {
     258                 :    3519294 :     struct ovsrcu_perthread *perthread = ovsrcu_perthread_get();
     259                 :            :     struct ovsrcu_cbset *cbset;
     260                 :            :     struct ovsrcu_cb *cb;
     261                 :            : 
     262                 :    3519294 :     cbset = perthread->cbset;
     263         [ +  + ]:    3519294 :     if (!cbset) {
     264                 :     337775 :         cbset = perthread->cbset = xmalloc(sizeof *perthread->cbset);
     265                 :     337775 :         cbset->n_cbs = 0;
     266                 :            :     }
     267                 :            : 
     268                 :    3519294 :     cb = &cbset->cbs[cbset->n_cbs++];
     269                 :    3519294 :     cb->function = function;
     270                 :    3519294 :     cb->aux = aux;
     271                 :            : 
     272         [ +  + ]:    3519294 :     if (cbset->n_cbs >= ARRAY_SIZE(cbset->cbs)) {
     273                 :      83603 :         ovsrcu_flush_cbset(perthread);
     274                 :            :     }
     275                 :    3519294 : }
     276                 :            : 
     277                 :            : static bool
     278                 :     277284 : ovsrcu_call_postponed(void)
     279                 :            : {
     280                 :            :     struct ovsrcu_cbset *cbset;
     281                 :            :     struct ovs_list cbsets;
     282                 :            : 
     283                 :     277284 :     guarded_list_pop_all(&flushed_cbsets, &cbsets);
     284         [ +  + ]:     277284 :     if (ovs_list_is_empty(&cbsets)) {
     285                 :      25075 :         return false;
     286                 :            :     }
     287                 :            : 
     288                 :     252209 :     ovsrcu_synchronize();
     289                 :            : 
     290         [ +  + ]:     563212 :     LIST_FOR_EACH_POP (cbset, list_node, &cbsets) {
     291                 :            :         struct ovsrcu_cb *cb;
     292                 :            : 
     293         [ +  + ]:    3435258 :         for (cb = cbset->cbs; cb < &cbset->cbs[cbset->n_cbs]; cb++) {
     294                 :    3123702 :             cb->function(cb->aux);
     295                 :            :         }
     296                 :     311556 :         free(cbset);
     297                 :            :     }
     298                 :            : 
     299                 :     276731 :     return true;
     300                 :            : }
     301                 :            : 
     302                 :            : static void *
     303                 :        615 : ovsrcu_postpone_thread(void *arg OVS_UNUSED)
     304                 :            : {
     305                 :        615 :     pthread_detach(pthread_self());
     306                 :            : 
     307                 :            :     for (;;) {
     308                 :      48067 :         uint64_t seqno = seq_read(flushed_cbsets_seq);
     309         [ +  + ]:      48067 :         if (!ovsrcu_call_postponed()) {
     310                 :      23669 :             seq_wait(flushed_cbsets_seq, seqno);
     311                 :      23669 :             poll_block();
     312                 :            :         }
     313                 :      47452 :     }
     314                 :            : 
     315                 :            :     OVS_NOT_REACHED();
     316                 :            : }
     317                 :            : 
     318                 :            : static void
     319                 :     337069 : ovsrcu_flush_cbset__(struct ovsrcu_perthread *perthread, bool protected)
     320                 :            : {
     321                 :     337069 :     struct ovsrcu_cbset *cbset = perthread->cbset;
     322                 :            : 
     323         [ +  - ]:     337069 :     if (cbset) {
     324                 :     337069 :         guarded_list_push_back(&flushed_cbsets, &cbset->list_node, SIZE_MAX);
     325                 :     337069 :         perthread->cbset = NULL;
     326                 :            : 
     327         [ +  + ]:     337069 :         if (protected) {
     328                 :         29 :             seq_change_protected(flushed_cbsets_seq);
     329                 :            :         } else {
     330                 :     337040 :             seq_change(flushed_cbsets_seq);
     331                 :            :         }
     332                 :            :     }
     333                 :     337069 : }
     334                 :            : 
     335                 :            : static void
     336                 :     337040 : ovsrcu_flush_cbset(struct ovsrcu_perthread *perthread)
     337                 :            : {
     338                 :     337040 :     ovsrcu_flush_cbset__(perthread, false);
     339                 :     337040 : }
     340                 :            : 
     341                 :            : static void
     342                 :     313203 : ovsrcu_unregister__(struct ovsrcu_perthread *perthread)
     343                 :            : {
     344         [ +  + ]:     313203 :     if (perthread->cbset) {
     345                 :      12827 :         ovsrcu_flush_cbset(perthread);
     346                 :            :     }
     347                 :            : 
     348                 :     313203 :     ovs_mutex_lock(&ovsrcu_threads_mutex);
     349                 :     313204 :     ovs_list_remove(&perthread->list_node);
     350                 :     313204 :     ovs_mutex_unlock(&ovsrcu_threads_mutex);
     351                 :            : 
     352                 :     313204 :     ovs_mutex_destroy(&perthread->mutex);
     353                 :     313204 :     free(perthread);
     354                 :            : 
     355                 :     313204 :     seq_change(global_seqno);
     356                 :     313204 : }
     357                 :            : 
     358                 :            : static void
     359                 :       4594 : ovsrcu_thread_exit_cb(void *perthread)
     360                 :            : {
     361                 :       4594 :     ovsrcu_unregister__(perthread);
     362                 :       4594 : }
     363                 :            : 
     364                 :            : /* Cancels the callback to ovsrcu_thread_exit_cb().
     365                 :            :  *
     366                 :            :  * Cancelling the call to the destructor during the main thread exit
     367                 :            :  * is needed while using pthreads-win32 library in Windows. It has been
     368                 :            :  * observed that in pthreads-win32, a call to the destructor during
     369                 :            :  * main thread exit causes undefined behavior. */
     370                 :            : static void
     371                 :      15778 : ovsrcu_cancel_thread_exit_cb(void *aux OVS_UNUSED)
     372                 :            : {
     373                 :      15778 :     pthread_setspecific(perthread_key, NULL);
     374                 :      15778 : }
     375                 :            : 
     376                 :            : static void
     377                 :   20647918 : ovsrcu_init_module(void)
     378                 :            : {
     379                 :            :     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
     380         [ +  + ]:   20647918 :     if (ovsthread_once_start(&once)) {
     381                 :      15799 :         global_seqno = seq_create();
     382                 :      15799 :         xpthread_key_create(&perthread_key, ovsrcu_thread_exit_cb);
     383                 :      15799 :         fatal_signal_add_hook(ovsrcu_cancel_thread_exit_cb, NULL, NULL, true);
     384                 :      15799 :         ovs_list_init(&ovsrcu_threads);
     385                 :      15799 :         ovs_mutex_init(&ovsrcu_threads_mutex);
     386                 :            : 
     387                 :      15799 :         guarded_list_init(&flushed_cbsets);
     388                 :      15799 :         flushed_cbsets_seq = seq_create();
     389                 :            : 
     390                 :      15799 :         ovsthread_once_done(&once);
     391                 :            :     }
     392                 :   20648016 : }

Generated by: LCOV version 1.12