LCOV - code coverage report
Current view: top level - lib - reconnect.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 213 237 89.9 %
Date: 2016-09-14 01:02:56 Functions: 33 38 86.8 %
Branches: 127 169 75.1 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2008, 2009, 2010, 2012, 2013 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 "reconnect.h"
      19                 :            : 
      20                 :            : #include <stdlib.h>
      21                 :            : 
      22                 :            : #include "poll-loop.h"
      23                 :            : #include "util.h"
      24                 :            : #include "openvswitch/vlog.h"
      25                 :            : 
      26                 :      53956 : VLOG_DEFINE_THIS_MODULE(reconnect);
      27                 :            : 
      28                 :            : #define STATES                                  \
      29                 :            :     STATE(VOID, 1 << 0)                         \
      30                 :            :     STATE(BACKOFF, 1 << 1)                      \
      31                 :            :     STATE(CONNECTING, 1 << 3)          \
      32                 :            :     STATE(ACTIVE, 1 << 4)                       \
      33                 :            :     STATE(IDLE, 1 << 5)                         \
      34                 :            :     STATE(RECONNECT, 1 << 6)                    \
      35                 :            :     STATE(LISTENING, 1 << 7)
      36                 :            : enum state {
      37                 :            : #define STATE(NAME, VALUE) S_##NAME = VALUE,
      38                 :            :     STATES
      39                 :            : #undef STATE
      40                 :            : };
      41                 :            : 
      42                 :            : static bool
      43                 :     202985 : is_connected_state(enum state state)
      44                 :            : {
      45                 :     202985 :     return (state & (S_ACTIVE | S_IDLE)) != 0;
      46                 :            : }
      47                 :            : 
      48                 :            : struct reconnect {
      49                 :            :     /* Configuration. */
      50                 :            :     char *name;
      51                 :            :     int min_backoff;
      52                 :            :     int max_backoff;
      53                 :            :     int probe_interval;
      54                 :            :     bool passive;
      55                 :            :     enum vlog_level info;       /* Used for informational messages. */
      56                 :            : 
      57                 :            :     /* State. */
      58                 :            :     enum state state;
      59                 :            :     long long int state_entered;
      60                 :            :     int backoff;
      61                 :            :     long long int last_activity;
      62                 :            :     long long int last_connected;
      63                 :            :     long long int last_disconnected;
      64                 :            :     unsigned int max_tries;
      65                 :            : 
      66                 :            :     /* These values are simply for statistics reporting, not otherwise used
      67                 :            :      * directly by anything internal. */
      68                 :            :     long long int creation_time;
      69                 :            :     unsigned int n_attempted_connections, n_successful_connections;
      70                 :            :     unsigned int total_connected_duration;
      71                 :            :     unsigned int seqno;
      72                 :            : };
      73                 :            : 
      74                 :            : static void reconnect_transition__(struct reconnect *, long long int now,
      75                 :            :                                    enum state state);
      76                 :            : static long long int reconnect_deadline__(const struct reconnect *);
      77                 :            : static bool reconnect_may_retry(struct reconnect *);
      78                 :            : 
      79                 :            : static const char *
      80                 :        290 : reconnect_state_name__(enum state state)
      81                 :            : {
      82   [ +  +  +  +  :        290 :     switch (state) {
             +  -  +  - ]
      83                 :            : #define STATE(NAME, VALUE) case S_##NAME: return #NAME;
      84                 :        290 :         STATES
      85                 :            : #undef STATE
      86                 :            :     }
      87                 :          0 :     return "***ERROR***";
      88                 :            : }
      89                 :            : 
      90                 :            : /* Creates and returns a new reconnect FSM with default settings.  The FSM is
      91                 :            :  * initially disabled.  The caller will likely want to call reconnect_enable()
      92                 :            :  * and reconnect_set_name() on the returned object. */
      93                 :            : struct reconnect *
      94                 :      15454 : reconnect_create(long long int now)
      95                 :            : {
      96                 :      15454 :     struct reconnect *fsm = xzalloc(sizeof *fsm);
      97                 :            : 
      98                 :      15454 :     fsm->name = xstrdup("void");
      99                 :      15454 :     fsm->min_backoff = RECONNECT_DEFAULT_MIN_BACKOFF;
     100                 :      15454 :     fsm->max_backoff = RECONNECT_DEFAULT_MAX_BACKOFF;
     101                 :      15454 :     fsm->probe_interval = RECONNECT_DEFAULT_PROBE_INTERVAL;
     102                 :      15454 :     fsm->passive = false;
     103                 :      15454 :     fsm->info = VLL_INFO;
     104                 :            : 
     105                 :      15454 :     fsm->state = S_VOID;
     106                 :      15454 :     fsm->state_entered = now;
     107                 :      15454 :     fsm->backoff = 0;
     108                 :      15454 :     fsm->last_activity = now;
     109                 :      15454 :     fsm->last_connected = LLONG_MAX;
     110                 :      15454 :     fsm->last_disconnected = LLONG_MAX;
     111                 :      15454 :     fsm->max_tries = UINT_MAX;
     112                 :      15454 :     fsm->creation_time = now;
     113                 :            : 
     114                 :      15454 :     return fsm;
     115                 :            : }
     116                 :            : 
     117                 :            : /* Frees 'fsm'. */
     118                 :            : void
     119                 :      15348 : reconnect_destroy(struct reconnect *fsm)
     120                 :            : {
     121         [ +  - ]:      15348 :     if (fsm) {
     122                 :      15348 :         free(fsm->name);
     123                 :      15348 :         free(fsm);
     124                 :            :     }
     125                 :      15348 : }
     126                 :            : 
     127                 :            : /* If 'quiet' is true, 'fsm' will log informational messages at level VLL_DBG,
     128                 :            :  * by default keeping them out of log files.  This is appropriate if the
     129                 :            :  * connection is one that is expected to be short-lived, so that the log
     130                 :            :  * messages are merely distracting.
     131                 :            :  *
     132                 :            :  * If 'quiet' is false, 'fsm' logs informational messages at level VLL_INFO.
     133                 :            :  * This is the default.
     134                 :            :  *
     135                 :            :  * This setting has no effect on the log level of debugging, warning, or error
     136                 :            :  * messages. */
     137                 :            : void
     138                 :       8186 : reconnect_set_quiet(struct reconnect *fsm, bool quiet)
     139                 :            : {
     140         [ +  - ]:       8186 :     fsm->info = quiet ? VLL_DBG : VLL_INFO;
     141                 :       8186 : }
     142                 :            : 
     143                 :            : /* Returns 'fsm''s name. */
     144                 :            : const char *
     145                 :       7200 : reconnect_get_name(const struct reconnect *fsm)
     146                 :            : {
     147                 :       7200 :     return fsm->name;
     148                 :            : }
     149                 :            : 
     150                 :            : /* Sets 'fsm''s name to 'name'.  If 'name' is null, then "void" is used
     151                 :            :  * instead.
     152                 :            :  *
     153                 :            :  * The name set for 'fsm' is used in log messages. */
     154                 :            : void
     155                 :      15454 : reconnect_set_name(struct reconnect *fsm, const char *name)
     156                 :            : {
     157                 :      15454 :     free(fsm->name);
     158         [ +  - ]:      15454 :     fsm->name = xstrdup(name ? name : "void");
     159                 :      15454 : }
     160                 :            : 
     161                 :            : /* Return the minimum number of milliseconds to back off between consecutive
     162                 :            :  * connection attempts.  The default is RECONNECT_DEFAULT_MIN_BACKOFF. */
     163                 :            : int
     164                 :          0 : reconnect_get_min_backoff(const struct reconnect *fsm)
     165                 :            : {
     166                 :          0 :     return fsm->min_backoff;
     167                 :            : }
     168                 :            : 
     169                 :            : /* Return the maximum number of milliseconds to back off between consecutive
     170                 :            :  * connection attempts.  The default is RECONNECT_DEFAULT_MAX_BACKOFF. */
     171                 :            : int
     172                 :          0 : reconnect_get_max_backoff(const struct reconnect *fsm)
     173                 :            : {
     174                 :          0 :     return fsm->max_backoff;
     175                 :            : }
     176                 :            : 
     177                 :            : /* Returns the "probe interval" for 'fsm' in milliseconds.  If this is zero, it
     178                 :            :  * disables the connection keepalive feature.  If it is nonzero, then if the
     179                 :            :  * interval passes while 'fsm' is connected and without reconnect_activity()
     180                 :            :  * being called for 'fsm', reconnect_run() returns RECONNECT_PROBE.  If the
     181                 :            :  * interval passes again without reconnect_activity() being called,
     182                 :            :  * reconnect_run() returns RECONNECT_DISCONNECT for 'fsm'. */
     183                 :            : int
     184                 :          0 : reconnect_get_probe_interval(const struct reconnect *fsm)
     185                 :            : {
     186                 :          0 :     return fsm->probe_interval;
     187                 :            : }
     188                 :            : 
     189                 :            : /* Limits the maximum number of times that 'fsm' will ask the client to try to
     190                 :            :  * reconnect to 'max_tries'.  UINT_MAX (the default) means an unlimited number
     191                 :            :  * of tries.
     192                 :            :  *
     193                 :            :  * After the number of tries has expired, the 'fsm' will disable itself
     194                 :            :  * instead of backing off and retrying. */
     195                 :            : void
     196                 :      16690 : reconnect_set_max_tries(struct reconnect *fsm, unsigned int max_tries)
     197                 :            : {
     198                 :      16690 :     fsm->max_tries = max_tries;
     199                 :      16690 : }
     200                 :            : 
     201                 :            : /* Returns the current remaining number of connection attempts, UINT_MAX if
     202                 :            :  * the number is unlimited.  */
     203                 :            : unsigned int
     204                 :       8444 : reconnect_get_max_tries(struct reconnect *fsm)
     205                 :            : {
     206                 :       8444 :     return fsm->max_tries;
     207                 :            : }
     208                 :            : 
     209                 :            : /* Configures the backoff parameters for 'fsm'.  'min_backoff' is the minimum
     210                 :            :  * number of milliseconds, and 'max_backoff' is the maximum, between connection
     211                 :            :  * attempts.  The current backoff is also the duration that 'fsm' is willing to
     212                 :            :  * wait for a given connection to succeed or fail.
     213                 :            :  *
     214                 :            :  * 'min_backoff' must be at least 1000, and 'max_backoff' must be greater than
     215                 :            :  * or equal to 'min_backoff'.
     216                 :            :  *
     217                 :            :  * Pass 0 for 'min_backoff' or 'max_backoff' or both to use the defaults. */
     218                 :            : void
     219                 :      95492 : reconnect_set_backoff(struct reconnect *fsm, int min_backoff, int max_backoff)
     220                 :            : {
     221                 :      95492 :     fsm->min_backoff = MAX(min_backoff, 1000);
     222                 :      95492 :     fsm->max_backoff = (max_backoff
     223                 :            :                         ? MAX(max_backoff, 1000)
     224         [ +  - ]:      95492 :                         : RECONNECT_DEFAULT_MAX_BACKOFF);
     225         [ -  + ]:      95492 :     if (fsm->min_backoff > fsm->max_backoff) {
     226                 :          0 :         fsm->max_backoff = fsm->min_backoff;
     227                 :            :     }
     228                 :            : 
     229 [ +  + ][ -  + ]:      95492 :     if (fsm->state == S_BACKOFF && fsm->backoff > max_backoff) {
     230                 :          0 :         fsm->backoff = max_backoff;
     231                 :            :     }
     232                 :      95492 : }
     233                 :            : 
     234                 :            : /* Sets the "probe interval" for 'fsm' to 'probe_interval', in milliseconds.
     235                 :            :  * If this is zero, it disables the connection keepalive feature.  If it is
     236                 :            :  * nonzero, then if the interval passes while 'fsm' is connected and without
     237                 :            :  * reconnect_activity() being called for 'fsm', reconnect_run() returns
     238                 :            :  * RECONNECT_PROBE.  If the interval passes again without reconnect_activity()
     239                 :            :  * being called, reconnect_run() returns RECONNECT_DISCONNECT for 'fsm'.
     240                 :            :  *
     241                 :            :  * If 'probe_interval' is nonzero, then it will be forced to a value of at
     242                 :            :  * least 1000 ms. */
     243                 :            : void
     244                 :      97345 : reconnect_set_probe_interval(struct reconnect *fsm, int probe_interval)
     245                 :            : {
     246         [ +  + ]:      97345 :     fsm->probe_interval = probe_interval ? MAX(1000, probe_interval) : 0;
     247                 :      97345 : }
     248                 :            : 
     249                 :            : /* Returns true if 'fsm' is in passive mode, false if 'fsm' is in active mode
     250                 :            :  * (the default). */
     251                 :            : bool
     252                 :       7200 : reconnect_is_passive(const struct reconnect *fsm)
     253                 :            : {
     254                 :       7200 :     return fsm->passive;
     255                 :            : }
     256                 :            : 
     257                 :            : /* Configures 'fsm' for active or passive mode.  In active mode (the default),
     258                 :            :  * the FSM is attempting to connect to a remote host.  In passive mode, the FSM
     259                 :            :  * is listening for connections from a remote host. */
     260                 :            : void
     261                 :         61 : reconnect_set_passive(struct reconnect *fsm, bool passive, long long int now)
     262                 :            : {
     263         [ +  + ]:         61 :     if (fsm->passive != passive) {
     264                 :          1 :         fsm->passive = passive;
     265                 :            : 
     266 [ +  - ][ -  + ]:          1 :         if (passive
     267                 :          1 :             ? fsm->state & (S_CONNECTING | S_RECONNECT)
     268 [ #  # ][ #  # ]:          0 :             : fsm->state == S_LISTENING && reconnect_may_retry(fsm)) {
     269                 :          0 :             reconnect_transition__(fsm, now, S_BACKOFF);
     270                 :          0 :             fsm->backoff = 0;
     271                 :            :         }
     272                 :            :     }
     273                 :         61 : }
     274                 :            : 
     275                 :            : /* Returns true if 'fsm' has been enabled with reconnect_enable().  Calling
     276                 :            :  * another function that indicates a change in connection state, such as
     277                 :            :  * reconnect_disconnected() or reconnect_force_reconnect(), will also enable
     278                 :            :  * a reconnect FSM. */
     279                 :            : bool
     280                 :          0 : reconnect_is_enabled(const struct reconnect *fsm)
     281                 :            : {
     282                 :          0 :     return fsm->state != S_VOID;
     283                 :            : }
     284                 :            : 
     285                 :            : /* If 'fsm' is disabled (the default for newly created FSMs), enables it, so
     286                 :            :  * that the next call to reconnect_run() for 'fsm' will return
     287                 :            :  * RECONNECT_CONNECT.
     288                 :            :  *
     289                 :            :  * If 'fsm' is not disabled, this function has no effect. */
     290                 :            : void
     291                 :       7267 : reconnect_enable(struct reconnect *fsm, long long int now)
     292                 :            : {
     293 [ +  - ][ +  + ]:       7267 :     if (fsm->state == S_VOID && reconnect_may_retry(fsm)) {
     294                 :       7266 :         reconnect_transition__(fsm, now, S_BACKOFF);
     295                 :       7266 :         fsm->backoff = 0;
     296                 :            :     }
     297                 :       7267 : }
     298                 :            : 
     299                 :            : /* Disables 'fsm'.  Until 'fsm' is enabled again, reconnect_run() will always
     300                 :            :  * return 0. */
     301                 :            : void
     302                 :          0 : reconnect_disable(struct reconnect *fsm, long long int now)
     303                 :            : {
     304         [ #  # ]:          0 :     if (fsm->state != S_VOID) {
     305                 :          0 :         reconnect_transition__(fsm, now, S_VOID);
     306                 :            :     }
     307                 :          0 : }
     308                 :            : 
     309                 :            : /* If 'fsm' is enabled and currently connected (or attempting to connect),
     310                 :            :  * forces reconnect_run() for 'fsm' to return RECONNECT_DISCONNECT the next
     311                 :            :  * time it is called, which should cause the client to drop the connection (or
     312                 :            :  * attempt), back off, and then reconnect. */
     313                 :            : void
     314                 :          4 : reconnect_force_reconnect(struct reconnect *fsm, long long int now)
     315                 :            : {
     316         [ +  - ]:          4 :     if (fsm->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) {
     317                 :          4 :         reconnect_transition__(fsm, now, S_RECONNECT);
     318                 :            :     }
     319                 :          4 : }
     320                 :            : 
     321                 :            : /* Tell 'fsm' that the connection dropped or that a connection attempt failed.
     322                 :            :  * 'error' specifies the reason: a positive value represents an errno value,
     323                 :            :  * EOF indicates that the connection was closed by the peer (e.g. read()
     324                 :            :  * returned 0), and 0 indicates no specific error.
     325                 :            :  *
     326                 :            :  * The FSM will back off, then reconnect. */
     327                 :            : void
     328                 :       8197 : reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
     329                 :            : {
     330         [ +  - ]:       8197 :     if (!(fsm->state & (S_BACKOFF | S_VOID))) {
     331                 :            :         /* Report what happened. */
     332         [ +  + ]:       8197 :         if (fsm->state & (S_ACTIVE | S_IDLE)) {
     333         [ +  + ]:       8166 :             if (error > 0) {
     334         [ +  - ]:         58 :                 VLOG_WARN("%s: connection dropped (%s)",
     335                 :            :                           fsm->name, ovs_strerror(error));
     336         [ +  + ]:       8108 :             } else if (error == EOF) {
     337         [ +  + ]:       8098 :                 VLOG(fsm->info, "%s: connection closed by peer", fsm->name);
     338                 :            :             } else {
     339         [ -  + ]:       8166 :                 VLOG(fsm->info, "%s: connection dropped", fsm->name);
     340                 :            :             }
     341         [ +  + ]:         31 :         } else if (fsm->state == S_LISTENING) {
     342         [ -  + ]:          1 :             if (error > 0) {
     343         [ #  # ]:          0 :                 VLOG_WARN("%s: error listening for connections (%s)",
     344                 :            :                           fsm->name, ovs_strerror(error));
     345                 :            :             } else {
     346         [ -  + ]:          1 :                 VLOG(fsm->info, "%s: error listening for connections",
     347                 :            :                      fsm->name);
     348                 :            :             }
     349                 :            :         } else {
     350         [ -  + ]:         30 :             const char *type = fsm->passive ? "listen" : "connection";
     351         [ +  + ]:         30 :             if (error > 0) {
     352         [ +  + ]:         14 :                 VLOG_INFO("%s: %s attempt failed (%s)",
     353                 :            :                           fsm->name, type, ovs_strerror(error));
     354                 :            :             } else {
     355         [ +  + ]:         16 :                 VLOG(fsm->info, "%s: %s attempt timed out", fsm->name, type);
     356                 :            :             }
     357                 :            :         }
     358                 :            : 
     359         [ +  + ]:       8197 :         if (fsm->state & (S_ACTIVE | S_IDLE)) {
     360                 :       8166 :             fsm->last_disconnected = now;
     361                 :            :         }
     362                 :            :         /* Back off. */
     363         [ +  + ]:       8197 :         if (fsm->state & (S_ACTIVE | S_IDLE)
     364         [ +  + ]:       8166 :              && (fsm->last_activity - fsm->last_connected >= fsm->backoff
     365         [ -  + ]:          6 :                  || fsm->passive)) {
     366         [ +  + ]:       8160 :             fsm->backoff = fsm->passive ? 0 : fsm->min_backoff;
     367                 :            :         } else {
     368         [ +  + ]:         37 :             if (fsm->backoff < fsm->min_backoff) {
     369                 :         18 :                 fsm->backoff = fsm->min_backoff;
     370         [ +  + ]:         19 :             } else if (fsm->backoff >= fsm->max_backoff / 2) {
     371                 :          4 :                 fsm->backoff = fsm->max_backoff;
     372                 :            :             } else {
     373                 :         15 :                 fsm->backoff *= 2;
     374                 :            :             }
     375         [ +  + ]:         37 :             if (fsm->passive) {
     376         [ -  + ]:          1 :                 VLOG(fsm->info, "%s: waiting %.3g seconds before trying to "
     377                 :            :                           "listen again", fsm->name, fsm->backoff / 1000.0);
     378                 :            :             } else {
     379         [ +  + ]:         36 :                 VLOG(fsm->info, "%s: waiting %.3g seconds before reconnect",
     380                 :            :                           fsm->name, fsm->backoff / 1000.0);
     381                 :            :             }
     382                 :            :         }
     383                 :            : 
     384         [ +  + ]:       8197 :         reconnect_transition__(fsm, now,
     385                 :       8197 :                                reconnect_may_retry(fsm) ? S_BACKOFF : S_VOID);
     386                 :            :     }
     387                 :       8197 : }
     388                 :            : 
     389                 :            : /* Tell 'fsm' that a connection or listening attempt is in progress.
     390                 :            :  *
     391                 :            :  * The FSM will start a timer, after which the connection or listening attempt
     392                 :            :  * will be aborted (by returning RECONNECT_DISCONNECT from
     393                 :            :  * reconnect_run()).  */
     394                 :            : void
     395                 :      22677 : reconnect_connecting(struct reconnect *fsm, long long int now)
     396                 :            : {
     397         [ +  + ]:      22677 :     if (fsm->state != S_CONNECTING) {
     398         [ +  + ]:      15470 :         if (fsm->passive) {
     399         [ -  + ]:          1 :             VLOG(fsm->info, "%s: listening...", fsm->name);
     400                 :            :         } else {
     401         [ +  + ]:      15469 :             VLOG(fsm->info, "%s: connecting...", fsm->name);
     402                 :            :         }
     403                 :      15470 :         reconnect_transition__(fsm, now, S_CONNECTING);
     404                 :            :     }
     405                 :      22677 : }
     406                 :            : 
     407                 :            : /* Tell 'fsm' that the client is listening for connection attempts.  This state
     408                 :            :  * last indefinitely until the client reports some change.
     409                 :            :  *
     410                 :            :  * The natural progression from this state is for the client to report that a
     411                 :            :  * connection has been accepted or is in progress of being accepted, by calling
     412                 :            :  * reconnect_connecting() or reconnect_connected().
     413                 :            :  *
     414                 :            :  * The client may also report that listening failed (e.g. accept() returned an
     415                 :            :  * unexpected error such as ENOMEM) by calling reconnect_listen_error(), in
     416                 :            :  * which case the FSM will back off and eventually return RECONNECT_CONNECT
     417                 :            :  * from reconnect_run() to tell the client to try listening again. */
     418                 :            : void
     419                 :          3 : reconnect_listening(struct reconnect *fsm, long long int now)
     420                 :            : {
     421         [ +  - ]:          3 :     if (fsm->state != S_LISTENING) {
     422         [ -  + ]:          3 :         VLOG(fsm->info, "%s: listening...", fsm->name);
     423                 :          3 :         reconnect_transition__(fsm, now, S_LISTENING);
     424                 :            :     }
     425                 :          3 : }
     426                 :            : 
     427                 :            : /* Tell 'fsm' that the client's attempt to accept a connection failed
     428                 :            :  * (e.g. accept() returned an unexpected error such as ENOMEM).
     429                 :            :  *
     430                 :            :  * If the FSM is currently listening (reconnect_listening() was called), it
     431                 :            :  * will back off and eventually return RECONNECT_CONNECT from reconnect_run()
     432                 :            :  * to tell the client to try listening again.  If there is an active
     433                 :            :  * connection, this will be delayed until that connection drops. */
     434                 :            : void
     435                 :          1 : reconnect_listen_error(struct reconnect *fsm, long long int now, int error)
     436                 :            : {
     437         [ +  - ]:          1 :     if (fsm->state == S_LISTENING) {
     438                 :          1 :         reconnect_disconnected(fsm, now, error);
     439                 :            :     }
     440                 :          1 : }
     441                 :            : 
     442                 :            : /* Tell 'fsm' that the connection was successful.
     443                 :            :  *
     444                 :            :  * The FSM will start the probe interval timer, which is reset by
     445                 :            :  * reconnect_activity().  If the timer expires, a probe will be sent (by
     446                 :            :  * returning RECONNECT_PROBE from reconnect_run()).  If the timer expires
     447                 :            :  * again without being reset, the connection will be aborted (by returning
     448                 :            :  * RECONNECT_DISCONNECT from reconnect_run()). */
     449                 :            : void
     450                 :      15444 : reconnect_connected(struct reconnect *fsm, long long int now)
     451                 :            : {
     452         [ +  - ]:      15444 :     if (!is_connected_state(fsm->state)) {
     453                 :      15444 :         reconnect_connecting(fsm, now);
     454                 :            : 
     455         [ +  + ]:      15444 :         VLOG(fsm->info, "%s: connected", fsm->name);
     456                 :      15444 :         reconnect_transition__(fsm, now, S_ACTIVE);
     457                 :      15444 :         fsm->last_connected = now;
     458                 :            :     }
     459                 :      15444 : }
     460                 :            : 
     461                 :            : /* Tell 'fsm' that the connection attempt failed.
     462                 :            :  *
     463                 :            :  * The FSM will back off and attempt to reconnect. */
     464                 :            : void
     465                 :         26 : reconnect_connect_failed(struct reconnect *fsm, long long int now, int error)
     466                 :            : {
     467                 :         26 :     reconnect_connecting(fsm, now);
     468                 :         26 :     reconnect_disconnected(fsm, now, error);
     469                 :         26 : }
     470                 :            : 
     471                 :            : /* Tell 'fsm' that some activity has occurred on the connection.  This resets
     472                 :            :  * the probe interval timer, so that the connection is known not to be idle. */
     473                 :            : void
     474                 :      60833 : reconnect_activity(struct reconnect *fsm, long long int now)
     475                 :            : {
     476         [ +  + ]:      60833 :     if (fsm->state != S_ACTIVE) {
     477                 :        103 :         reconnect_transition__(fsm, now, S_ACTIVE);
     478                 :            :     }
     479                 :      60833 :     fsm->last_activity = now;
     480                 :      60833 : }
     481                 :            : 
     482                 :            : static void
     483                 :      46597 : reconnect_transition__(struct reconnect *fsm, long long int now,
     484                 :            :                        enum state state)
     485                 :            : {
     486         [ +  + ]:      46597 :     if (fsm->state == S_CONNECTING) {
     487                 :      15470 :         fsm->n_attempted_connections++;
     488         [ +  + ]:      15470 :         if (state == S_ACTIVE) {
     489                 :      15444 :             fsm->n_successful_connections++;
     490                 :            :         }
     491                 :            :     }
     492         [ +  + ]:      46597 :     if (is_connected_state(fsm->state) != is_connected_state(state)) {
     493         [ +  + ]:      23614 :         if (is_connected_state(fsm->state)) {
     494                 :       8170 :             fsm->total_connected_duration += now - fsm->last_connected;
     495                 :            :         }
     496                 :      23614 :         fsm->seqno++;
     497                 :            :     }
     498                 :            : 
     499         [ -  + ]:      46597 :     VLOG_DBG("%s: entering %s", fsm->name, reconnect_state_name__(state));
     500                 :      46597 :     fsm->state = state;
     501                 :      46597 :     fsm->state_entered = now;
     502                 :      46597 : }
     503                 :            : 
     504                 :            : static long long int
     505                 :     501366 : reconnect_deadline__(const struct reconnect *fsm)
     506                 :            : {
     507         [ -  + ]:     501366 :     ovs_assert(fsm->state_entered != LLONG_MIN);
     508   [ +  +  +  +  :     501366 :     switch (fsm->state) {
                +  +  - ]
     509                 :            :     case S_VOID:
     510                 :            :     case S_LISTENING:
     511                 :       8059 :         return LLONG_MAX;
     512                 :            : 
     513                 :            :     case S_BACKOFF:
     514                 :       7915 :         return fsm->state_entered + fsm->backoff;
     515                 :            : 
     516                 :            :     case S_CONNECTING:
     517                 :       7229 :         return fsm->state_entered + MAX(1000, fsm->backoff);
     518                 :            : 
     519                 :            :     case S_ACTIVE:
     520         [ +  + ]:     477947 :         if (fsm->probe_interval) {
     521                 :      26106 :             long long int base = MAX(fsm->last_activity, fsm->state_entered);
     522                 :      26106 :             return base + fsm->probe_interval;
     523                 :            :         }
     524                 :     451841 :         return LLONG_MAX;
     525                 :            : 
     526                 :            :     case S_IDLE:
     527         [ +  - ]:        212 :         if (fsm->probe_interval) {
     528                 :        212 :             return fsm->state_entered + fsm->probe_interval;
     529                 :            :         }
     530                 :          0 :         return LLONG_MAX;
     531                 :            : 
     532                 :            :     case S_RECONNECT:
     533                 :          4 :         return fsm->state_entered;
     534                 :            :     }
     535                 :            : 
     536                 :          0 :     OVS_NOT_REACHED();
     537                 :            : }
     538                 :            : 
     539                 :            : /* Assesses whether any action should be taken on 'fsm'.  The return value is
     540                 :            :  * one of:
     541                 :            :  *
     542                 :            :  *     - 0: The client need not take any action.
     543                 :            :  *
     544                 :            :  *     - Active client, RECONNECT_CONNECT: The client should start a connection
     545                 :            :  *       attempt and indicate this by calling reconnect_connecting().  If the
     546                 :            :  *       connection attempt has definitely succeeded, it should call
     547                 :            :  *       reconnect_connected().  If the connection attempt has definitely
     548                 :            :  *       failed, it should call reconnect_connect_failed().
     549                 :            :  *
     550                 :            :  *       The FSM is smart enough to back off correctly after successful
     551                 :            :  *       connections that quickly abort, so it is OK to call
     552                 :            :  *       reconnect_connected() after a low-level successful connection
     553                 :            :  *       (e.g. connect()) even if the connection might soon abort due to a
     554                 :            :  *       failure at a high-level (e.g. SSL negotiation failure).
     555                 :            :  *
     556                 :            :  *     - Passive client, RECONNECT_CONNECT: The client should try to listen for
     557                 :            :  *       a connection, if it is not already listening.  It should call
     558                 :            :  *       reconnect_listening() if successful, otherwise reconnect_connecting()
     559                 :            :  *       or reconnected_connect_failed() if the attempt is in progress or
     560                 :            :  *       definitely failed, respectively.
     561                 :            :  *
     562                 :            :  *       A listening passive client should constantly attempt to accept a new
     563                 :            :  *       connection and report an accepted connection with
     564                 :            :  *       reconnect_connected().
     565                 :            :  *
     566                 :            :  *     - RECONNECT_DISCONNECT: The client should abort the current connection
     567                 :            :  *       or connection attempt or listen attempt and call
     568                 :            :  *       reconnect_disconnected() or reconnect_connect_failed() to indicate it.
     569                 :            :  *
     570                 :            :  *     - RECONNECT_PROBE: The client should send some kind of request to the
     571                 :            :  *       peer that will elicit a response, to ensure that the connection is
     572                 :            :  *       indeed in working order.  (This will only be returned if the "probe
     573                 :            :  *       interval" is nonzero--see reconnect_set_probe_interval()).
     574                 :            :  */
     575                 :            : enum reconnect_action
     576                 :     277677 : reconnect_run(struct reconnect *fsm, long long int now)
     577                 :            : {
     578         [ +  + ]:     277677 :     if (now >= reconnect_deadline__(fsm)) {
     579   [ -  +  +  +  :       7364 :         switch (fsm->state) {
             +  +  -  - ]
     580                 :            :         case S_VOID:
     581                 :          0 :             return 0;
     582                 :            : 
     583                 :            :         case S_BACKOFF:
     584                 :       7231 :             return RECONNECT_CONNECT;
     585                 :            : 
     586                 :            :         case S_CONNECTING:
     587                 :         12 :             return RECONNECT_DISCONNECT;
     588                 :            : 
     589                 :            :         case S_ACTIVE:
     590         [ -  + ]:        110 :             VLOG_DBG("%s: idle %lld ms, sending inactivity probe", fsm->name,
     591                 :            :                      now - MAX(fsm->last_activity, fsm->state_entered));
     592                 :        110 :             reconnect_transition__(fsm, now, S_IDLE);
     593                 :        110 :             return RECONNECT_PROBE;
     594                 :            : 
     595                 :            :         case S_IDLE:
     596         [ -  + ]:          7 :             VLOG_ERR("%s: no response to inactivity probe after %.3g "
     597                 :            :                      "seconds, disconnecting",
     598                 :            :                      fsm->name, (now - fsm->state_entered) / 1000.0);
     599                 :          7 :             return RECONNECT_DISCONNECT;
     600                 :            : 
     601                 :            :         case S_RECONNECT:
     602                 :          4 :             return RECONNECT_DISCONNECT;
     603                 :            : 
     604                 :            :         case S_LISTENING:
     605                 :          0 :             return 0;
     606                 :            :         }
     607                 :            : 
     608                 :          0 :         OVS_NOT_REACHED();
     609                 :            :     } else {
     610                 :     270313 :         return 0;
     611                 :            :     }
     612                 :            : }
     613                 :            : 
     614                 :            : /* Causes the next call to poll_block() to wake up when reconnect_run() should
     615                 :            :  * be called on 'fsm'. */
     616                 :            : void
     617                 :     223638 : reconnect_wait(struct reconnect *fsm, long long int now)
     618                 :            : {
     619                 :     223638 :     int timeout = reconnect_timeout(fsm, now);
     620         [ +  + ]:     223638 :     if (timeout >= 0) {
     621                 :      20686 :         poll_timer_wait(timeout);
     622                 :            :     }
     623                 :     223638 : }
     624                 :            : 
     625                 :            : /* Returns the number of milliseconds after which reconnect_run() should be
     626                 :            :  * called on 'fsm' if nothing else notable happens in the meantime, or a
     627                 :            :  * negative number if this is currently unnecessary. */
     628                 :            : int
     629                 :     223689 : reconnect_timeout(struct reconnect *fsm, long long int now)
     630                 :            : {
     631                 :     223689 :     long long int deadline = reconnect_deadline__(fsm);
     632         [ +  + ]:     223689 :     if (deadline != LLONG_MAX) {
     633                 :      20734 :         long long int remaining = deadline - now;
     634 [ +  + ][ +  + ]:      20734 :         return MAX(0, MIN(INT_MAX, remaining));
     635                 :            :     }
     636                 :     202955 :     return -1;
     637                 :            : }
     638                 :            : 
     639                 :            : /* Returns true if 'fsm' is currently believed to be connected, that is, if
     640                 :            :  * reconnect_connected() was called more recently than any call to
     641                 :            :  * reconnect_connect_failed() or reconnect_disconnected() or
     642                 :            :  * reconnect_disable(), and false otherwise.  */
     643                 :            : bool
     644                 :      70443 : reconnect_is_connected(const struct reconnect *fsm)
     645                 :            : {
     646                 :      70443 :     return is_connected_state(fsm->state);
     647                 :            : }
     648                 :            : 
     649                 :            : /* Returns the number of milliseconds since 'fsm' last successfully connected
     650                 :            :  * to its peer (even if it has since disconnected). Returns UINT_MAX if never
     651                 :            :  * connected. */
     652                 :            : unsigned int
     653                 :        362 : reconnect_get_last_connect_elapsed(const struct reconnect *fsm,
     654                 :            :                                    long long int now)
     655                 :            : {
     656         [ +  + ]:        362 :     return fsm->last_connected == LLONG_MAX ? UINT_MAX
     657                 :        182 :         : now - fsm->last_connected;
     658                 :            : }
     659                 :            : 
     660                 :            : /* Returns the number of milliseconds since 'fsm' last disconnected
     661                 :            :  * from its peer (even if it has since reconnected). Returns UINT_MAX if never
     662                 :            :  * disconnected. */
     663                 :            : unsigned int
     664                 :        290 : reconnect_get_last_disconnect_elapsed(const struct reconnect *fsm,
     665                 :            :                                       long long int now)
     666                 :            : {
     667         [ +  + ]:        290 :     return fsm->last_disconnected == LLONG_MAX ? UINT_MAX
     668                 :         48 :         : now - fsm->last_disconnected;
     669                 :            : }
     670                 :            : 
     671                 :            : /* Copies various statistics for 'fsm' into '*stats'. */
     672                 :            : void
     673                 :        290 : reconnect_get_stats(const struct reconnect *fsm, long long int now,
     674                 :            :                     struct reconnect_stats *stats)
     675                 :            : {
     676                 :        290 :     stats->creation_time = fsm->creation_time;
     677                 :        290 :     stats->last_activity = fsm->last_activity;
     678                 :        290 :     stats->last_connected = fsm->last_connected;
     679                 :        290 :     stats->last_disconnected = fsm->last_disconnected;
     680                 :        290 :     stats->backoff = fsm->backoff;
     681                 :        290 :     stats->seqno = fsm->seqno;
     682                 :        290 :     stats->is_connected = reconnect_is_connected(fsm);
     683                 :            :     stats->msec_since_connect
     684                 :        290 :         = reconnect_get_last_connect_elapsed(fsm, now);
     685                 :            :     stats->msec_since_disconnect
     686                 :        290 :         = reconnect_get_last_disconnect_elapsed(fsm, now);
     687                 :        580 :     stats->total_connected_duration = fsm->total_connected_duration
     688                 :        290 :         + (is_connected_state(fsm->state)
     689         [ +  + ]:        290 :            ? reconnect_get_last_connect_elapsed(fsm, now) : 0);
     690                 :        290 :     stats->n_attempted_connections = fsm->n_attempted_connections;
     691                 :        290 :     stats->n_successful_connections = fsm->n_successful_connections;
     692                 :        290 :     stats->state = reconnect_state_name__(fsm->state);
     693                 :        290 :     stats->state_elapsed = now - fsm->state_entered;
     694                 :        290 : }
     695                 :            : 
     696                 :            : static bool
     697                 :      15464 : reconnect_may_retry(struct reconnect *fsm)
     698                 :            : {
     699                 :      15464 :     bool may_retry = fsm->max_tries > 0;
     700 [ +  + ][ +  + ]:      15464 :     if (may_retry && fsm->max_tries != UINT_MAX) {
     701                 :          6 :         fsm->max_tries--;
     702                 :            :     }
     703                 :      15464 :     return may_retry;
     704                 :            : }

Generated by: LCOV version 1.12