X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Freconnect.c;h=d7357c2025c7a4897c78bac882cc393090d90ce2;hb=a4e47607974ee30782d7d8085a2c12a91971177e;hp=f159f01c0bb526e8f52aca348703885b626258e8;hpb=5f55c39b21e69025045437ffbd3bb98fe6ce2e89;p=sliver-openvswitch.git diff --git a/lib/reconnect.c b/lib/reconnect.c index f159f01c0..d7357c202 100644 --- a/lib/reconnect.c +++ b/lib/reconnect.c @@ -21,18 +21,18 @@ #include #include "poll-loop.h" - -#define THIS_MODULE VLM_reconnect #include "vlog.h" +VLOG_DEFINE_THIS_MODULE(reconnect); + #define STATES \ STATE(VOID, 1 << 0) \ STATE(BACKOFF, 1 << 1) \ - STATE(START_CONNECT, 1 << 2) \ STATE(CONNECT_IN_PROGRESS, 1 << 3) \ STATE(ACTIVE, 1 << 4) \ STATE(IDLE, 1 << 5) \ - STATE(RECONNECT, 1 << 6) + STATE(RECONNECT, 1 << 6) \ + STATE(LISTENING, 1 << 7) enum state { #define STATE(NAME, VALUE) S_##NAME = VALUE, STATES @@ -51,6 +51,8 @@ struct reconnect { int min_backoff; int max_backoff; int probe_interval; + bool passive; + enum vlog_level info; /* Used for informational messages. */ /* State. */ enum state state; @@ -93,9 +95,11 @@ reconnect_create(long long int now) struct reconnect *fsm = xzalloc(sizeof *fsm); fsm->name = xstrdup("void"); - fsm->min_backoff = 1000; - fsm->max_backoff = 8000; - fsm->probe_interval = 5000; + fsm->min_backoff = RECONNECT_DEFAULT_MIN_BACKOFF; + fsm->max_backoff = RECONNECT_DEFAULT_MAX_BACKOFF; + fsm->probe_interval = RECONNECT_DEFAULT_PROBE_INTERVAL; + fsm->passive = false; + fsm->info = VLL_INFO; fsm->state = S_VOID; fsm->state_entered = now; @@ -118,6 +122,22 @@ reconnect_destroy(struct reconnect *fsm) } } +/* If 'quiet' is true, 'fsm' will log informational messages at level VLL_DBG, + * by default keeping them out of log files. This is appropriate if the + * connection is one that is expected to be short-lived, so that the log + * messages are merely distracting. + * + * If 'quiet' is false, 'fsm' logs informational messages at level VLL_INFO. + * This is the default. + * + * This setting has no effect on the log level of debugging, warning, or error + * messages. */ +void +reconnect_set_quiet(struct reconnect *fsm, bool quiet) +{ + fsm->info = quiet ? VLL_DBG : VLL_INFO; +} + /* Returns 'fsm''s name. */ const char * reconnect_get_name(const struct reconnect *fsm) @@ -137,7 +157,7 @@ reconnect_set_name(struct reconnect *fsm, const char *name) } /* Return the minimum number of milliseconds to back off between consecutive - * connection attempts. The default is 1000 ms. */ + * connection attempts. The default is RECONNECT_DEFAULT_MIN_BACKOFF. */ int reconnect_get_min_backoff(const struct reconnect *fsm) { @@ -145,7 +165,7 @@ reconnect_get_min_backoff(const struct reconnect *fsm) } /* Return the maximum number of milliseconds to back off between consecutive - * connection attempts. The default is 8000 ms. */ + * connection attempts. The default is RECONNECT_DEFAULT_MAX_BACKOFF. */ int reconnect_get_max_backoff(const struct reconnect *fsm) { @@ -189,12 +209,16 @@ reconnect_get_max_tries(struct reconnect *fsm) * attempts. * * 'min_backoff' must be at least 1000, and 'max_backoff' must be greater than - * or equal to 'min_backoff'. */ + * or equal to 'min_backoff'. + * + * Pass 0 for 'min_backoff' or 'max_backoff' or both to use the defaults. */ void reconnect_set_backoff(struct reconnect *fsm, int min_backoff, int max_backoff) { fsm->min_backoff = MAX(min_backoff, 1000); - fsm->max_backoff = max_backoff ? MAX(max_backoff, 1000) : 8000; + fsm->max_backoff = (max_backoff + ? MAX(max_backoff, 1000) + : RECONNECT_DEFAULT_MAX_BACKOFF); if (fsm->min_backoff > fsm->max_backoff) { fsm->max_backoff = fsm->min_backoff; } @@ -219,6 +243,32 @@ reconnect_set_probe_interval(struct reconnect *fsm, int probe_interval) fsm->probe_interval = probe_interval ? MAX(1000, probe_interval) : 0; } +/* Returns true if 'fsm' is in passive mode, false if 'fsm' is in active mode + * (the default). */ +bool +reconnect_is_passive(const struct reconnect *fsm) +{ + return fsm->passive; +} + +/* Configures 'fsm' for active or passive mode. In active mode (the default), + * the FSM is attempting to connect to a remote host. In passive mode, the FSM + * is listening for connections from a remote host. */ +void +reconnect_set_passive(struct reconnect *fsm, bool passive, long long int now) +{ + if (fsm->passive != passive) { + fsm->passive = passive; + + if (passive + ? fsm->state & (S_CONNECT_IN_PROGRESS | S_RECONNECT) + : fsm->state == S_LISTENING && reconnect_may_retry(fsm)) { + reconnect_transition__(fsm, now, S_BACKOFF); + fsm->backoff = 0; + } + } +} + /* Returns true if 'fsm' has been enabled with reconnect_enable(). Calling * another function that indicates a change in connection state, such as * reconnect_disconnected() or reconnect_force_reconnect(), will also enable @@ -260,8 +310,7 @@ reconnect_disable(struct reconnect *fsm, long long int now) void reconnect_force_reconnect(struct reconnect *fsm, long long int now) { - if (fsm->state & (S_START_CONNECT | S_CONNECT_IN_PROGRESS - | S_ACTIVE | S_IDLE)) { + if (fsm->state & (S_CONNECT_IN_PROGRESS | S_ACTIVE | S_IDLE)) { reconnect_transition__(fsm, now, S_RECONNECT); } } @@ -282,23 +331,33 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error) VLOG_WARN("%s: connection dropped (%s)", fsm->name, strerror(error)); } else if (error == EOF) { - VLOG_INFO("%s: connection closed by peer", fsm->name); + VLOG(fsm->info, "%s: connection closed by peer", fsm->name); } else { - VLOG_INFO("%s: connection dropped", fsm->name); + VLOG(fsm->info, "%s: connection dropped", fsm->name); } - } else { + } else if (fsm->state == S_LISTENING) { if (error > 0) { - VLOG_WARN("%s: connection attempt failed (%s)", + VLOG_WARN("%s: error listening for connections (%s)", fsm->name, strerror(error)); } else { - VLOG_INFO("%s: connection attempt timed out", fsm->name); + VLOG(fsm->info, "%s: error listening for connections", + fsm->name); + } + } else { + const char *type = fsm->passive ? "listen" : "connection"; + if (error > 0) { + VLOG_WARN("%s: %s attempt failed (%s)", + fsm->name, type, strerror(error)); + } else { + VLOG(fsm->info, "%s: %s attempt timed out", fsm->name, type); } } /* Back off. */ if (fsm->state & (S_ACTIVE | S_IDLE) - && fsm->last_received - fsm->last_connected >= fsm->backoff) { - fsm->backoff = fsm->min_backoff; + && (fsm->last_received - fsm->last_connected >= fsm->backoff + || fsm->passive)) { + fsm->backoff = fsm->passive ? 0 : fsm->min_backoff; } else { if (fsm->backoff < fsm->min_backoff) { fsm->backoff = fsm->min_backoff; @@ -307,8 +366,13 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error) } else { fsm->backoff *= 2; } - VLOG_INFO("%s: waiting %.3g seconds before reconnect\n", - fsm->name, fsm->backoff / 1000.0); + if (fsm->passive) { + VLOG(fsm->info, "%s: waiting %.3g seconds before trying to " + "listen again", fsm->name, fsm->backoff / 1000.0); + } else { + VLOG(fsm->info, "%s: waiting %.3g seconds before reconnect", + fsm->name, fsm->backoff / 1000.0); + } } reconnect_transition__(fsm, now, @@ -316,19 +380,59 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error) } } -/* Tell 'fsm' that a connection attempt is in progress. +/* Tell 'fsm' that a connection or listening attempt is in progress. * - * The FSM will start a timer, after which the connection attempt will be - * aborted (by returning RECONNECT_DISCONNECT from reconect_run()). */ + * The FSM will start a timer, after which the connection or listening attempt + * will be aborted (by returning RECONNECT_DISCONNECT from + * reconnect_run()). */ void reconnect_connecting(struct reconnect *fsm, long long int now) { if (fsm->state != S_CONNECT_IN_PROGRESS) { - VLOG_INFO("%s: connecting...", fsm->name); + if (fsm->passive) { + VLOG(fsm->info, "%s: listening...", fsm->name); + } else { + VLOG(fsm->info, "%s: connecting...", fsm->name); + } reconnect_transition__(fsm, now, S_CONNECT_IN_PROGRESS); } } +/* Tell 'fsm' that the client is listening for connection attempts. This state + * last indefinitely until the client reports some change. + * + * The natural progression from this state is for the client to report that a + * connection has been accepted or is in progress of being accepted, by calling + * reconnect_connecting() or reconnect_connected(). + * + * The client may also report that listening failed (e.g. accept() returned an + * unexpected error such as ENOMEM) by calling reconnect_listen_error(), in + * which case the FSM will back off and eventually return RECONNECT_CONNECT + * from reconnect_run() to tell the client to try listening again. */ +void +reconnect_listening(struct reconnect *fsm, long long int now) +{ + if (fsm->state != S_LISTENING) { + VLOG(fsm->info, "%s: listening...", fsm->name); + reconnect_transition__(fsm, now, S_LISTENING); + } +} + +/* Tell 'fsm' that the client's attempt to accept a connection failed + * (e.g. accept() returned an unexpected error such as ENOMEM). + * + * If the FSM is currently listening (reconnect_listening() was called), it + * will back off and eventually return RECONNECT_CONNECT from reconnect_run() + * to tell the client to try listening again. If there is an active + * connection, this will be delayed until that connection drops. */ +void +reconnect_listen_error(struct reconnect *fsm, long long int now, int error) +{ + if (fsm->state == S_LISTENING) { + reconnect_disconnected(fsm, now, error); + } +} + /* Tell 'fsm' that the connection was successful. * * The FSM will start the probe interval timer, which is reset by @@ -342,7 +446,7 @@ reconnect_connected(struct reconnect *fsm, long long int now) if (!is_connected_state(fsm->state)) { reconnect_connecting(fsm, now); - VLOG_INFO("%s: connected", fsm->name); + VLOG(fsm->info, "%s: connected", fsm->name); reconnect_transition__(fsm, now, S_ACTIVE); fsm->last_connected = now; } @@ -397,12 +501,12 @@ reconnect_deadline__(const struct reconnect *fsm) assert(fsm->state_entered != LLONG_MIN); switch (fsm->state) { case S_VOID: + case S_LISTENING: return LLONG_MAX; case S_BACKOFF: return fsm->state_entered + fsm->backoff; - case S_START_CONNECT: case S_CONNECT_IN_PROGRESS: return fsm->state_entered + MAX(1000, fsm->backoff); @@ -428,9 +532,9 @@ reconnect_deadline__(const struct reconnect *fsm) * * - 0: The client need not take any action. * - * - RECONNECT_CONNECT: The client should start a connection attempt and - * indicate this by calling reconnect_connecting(). If the connection - * attempt has definitely succeeded, it should call + * - Active client, RECONNECT_CONNECT: The client should start a connection + * attempt and indicate this by calling reconnect_connecting(). If the + * connection attempt has definitely succeeded, it should call * reconnect_connected(). If the connection attempt has definitely * failed, it should call reconnect_connect_failed(). * @@ -440,9 +544,19 @@ reconnect_deadline__(const struct reconnect *fsm) * (e.g. connect()) even if the connection might soon abort due to a * failure at a high-level (e.g. SSL negotiation failure). * + * - Passive client, RECONNECT_CONNECT: The client should try to listen for + * a connection, if it is not already listening. It should call + * reconnect_listening() if successful, otherwise reconnect_connecting() + * or reconnected_connect_failed() if the attempt is in progress or + * definitely failed, respectively. + * + * A listening passive client should constantly attempt to accept a new + * connection and report an accepted connection with + * reconnect_connected(). + * * - RECONNECT_DISCONNECT: The client should abort the current connection - * or connection attempt and call reconnect_disconnected() or - * reconnect_connect_failed() to indicate it. + * or connection attempt or listen attempt and call + * reconnect_disconnected() or reconnect_connect_failed() to indicate it. * * - RECONNECT_PROBE: The client should send some kind of request to the * peer that will elicit a response, to ensure that the connection is @@ -460,7 +574,6 @@ reconnect_run(struct reconnect *fsm, long long int now) case S_BACKOFF: return RECONNECT_CONNECT; - case S_START_CONNECT: case S_CONNECT_IN_PROGRESS: return RECONNECT_DISCONNECT; @@ -478,11 +591,14 @@ reconnect_run(struct reconnect *fsm, long long int now) case S_RECONNECT: return RECONNECT_DISCONNECT; + + case S_LISTENING: + return 0; } NOT_REACHED(); } else { - return fsm->state == S_START_CONNECT ? RECONNECT_CONNECT : 0; + return 0; } }