netdev: Allow recv, recv_wait, drain, send, send_wait to be null.
[sliver-openvswitch.git] / lib / reconnect.c
index fadeeb8..f159f01 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #define STATES                                  \
     STATE(VOID, 1 << 0)                         \
     STATE(BACKOFF, 1 << 1)                      \
-    STATE(CONNECTING, 1 << 2)                   \
-    STATE(ACTIVE, 1 << 3)                       \
-    STATE(IDLE, 1 << 4)                         \
-    STATE(RECONNECT, 1 << 5)
+    STATE(START_CONNECT, 1 << 2)                \
+    STATE(CONNECT_IN_PROGRESS, 1 << 3)          \
+    STATE(ACTIVE, 1 << 4)                       \
+    STATE(IDLE, 1 << 5)                         \
+    STATE(RECONNECT, 1 << 6)
 enum state {
 #define STATE(NAME, VALUE) S_##NAME = VALUE,
     STATES
@@ -57,6 +58,7 @@ struct reconnect {
     int backoff;
     long long int last_received;
     long long int last_connected;
+    unsigned int max_tries;
 
     /* These values are simply for statistics reporting, not otherwise used
      * directly by anything internal. */
@@ -69,6 +71,7 @@ struct reconnect {
 static void reconnect_transition__(struct reconnect *, long long int now,
                                    enum state state);
 static long long int reconnect_deadline__(const struct reconnect *);
+static bool reconnect_may_retry(struct reconnect *);
 
 static const char *
 reconnect_state_name__(enum state state)
@@ -99,6 +102,7 @@ reconnect_create(long long int now)
     fsm->backoff = 0;
     fsm->last_received = now;
     fsm->last_connected = now;
+    fsm->max_tries = UINT_MAX;
     fsm->creation_time = now;
 
     return fsm;
@@ -160,6 +164,26 @@ reconnect_get_probe_interval(const struct reconnect *fsm)
     return fsm->probe_interval;
 }
 
+/* Limits the maximum number of times that 'fsm' will ask the client to try to
+ * reconnect to 'max_tries'.  UINT_MAX (the default) means an unlimited number
+ * of tries.
+ *
+ * After the number of tries has expired, the 'fsm' will disable itself
+ * instead of backing off and retrying. */
+void
+reconnect_set_max_tries(struct reconnect *fsm, unsigned int max_tries)
+{
+    fsm->max_tries = max_tries;
+}
+
+/* Returns the current remaining number of connection attempts, UINT_MAX if
+ * the number is unlimited.  */
+unsigned int
+reconnect_get_max_tries(struct reconnect *fsm)
+{
+    return fsm->max_tries;
+}
+
 /* Configures the backoff parameters for 'fsm'.  'min_backoff' is the minimum
  * number of milliseconds, and 'max_backoff' is the maximum, between connection
  * attempts.
@@ -213,7 +237,7 @@ reconnect_is_enabled(const struct reconnect *fsm)
 void
 reconnect_enable(struct reconnect *fsm, long long int now)
 {
-    if (fsm->state == S_VOID) {
+    if (fsm->state == S_VOID && reconnect_may_retry(fsm)) {
         reconnect_transition__(fsm, now, S_BACKOFF);
         fsm->backoff = 0;
     }
@@ -236,7 +260,8 @@ reconnect_disable(struct reconnect *fsm, long long int now)
 void
 reconnect_force_reconnect(struct reconnect *fsm, long long int now)
 {
-    if (fsm->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) {
+    if (fsm->state & (S_START_CONNECT | S_CONNECT_IN_PROGRESS
+                      | S_ACTIVE | S_IDLE)) {
         reconnect_transition__(fsm, now, S_RECONNECT);
     }
 }
@@ -250,7 +275,7 @@ reconnect_force_reconnect(struct reconnect *fsm, long long int now)
 void
 reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
 {
-    if (fsm->state != S_BACKOFF) {
+    if (!(fsm->state & (S_BACKOFF | S_VOID))) {
         /* Report what happened. */
         if (fsm->state & (S_ACTIVE | S_IDLE)) {
             if (error > 0) {
@@ -285,7 +310,9 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
             VLOG_INFO("%s: waiting %.3g seconds before reconnect\n",
                       fsm->name, fsm->backoff / 1000.0);
         }
-        reconnect_transition__(fsm, now, S_BACKOFF);
+
+        reconnect_transition__(fsm, now,
+                               reconnect_may_retry(fsm) ? S_BACKOFF : S_VOID);
     }
 }
 
@@ -296,9 +323,9 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
 void
 reconnect_connecting(struct reconnect *fsm, long long int now)
 {
-    if (fsm->state != S_CONNECTING) {
+    if (fsm->state != S_CONNECT_IN_PROGRESS) {
         VLOG_INFO("%s: connecting...", fsm->name);
-        reconnect_transition__(fsm, now, S_CONNECTING);
+        reconnect_transition__(fsm, now, S_CONNECT_IN_PROGRESS);
     }
 }
 
@@ -346,7 +373,7 @@ static void
 reconnect_transition__(struct reconnect *fsm, long long int now,
                        enum state state)
 {
-    if (fsm->state == S_CONNECTING) {
+    if (fsm->state == S_CONNECT_IN_PROGRESS) {
         fsm->n_attempted_connections++;
         if (state == S_ACTIVE) {
             fsm->n_successful_connections++;
@@ -375,7 +402,8 @@ reconnect_deadline__(const struct reconnect *fsm)
     case S_BACKOFF:
         return fsm->state_entered + fsm->backoff;
 
-    case S_CONNECTING:
+    case S_START_CONNECT:
+    case S_CONNECT_IN_PROGRESS:
         return fsm->state_entered + MAX(1000, fsm->backoff);
 
     case S_ACTIVE:
@@ -432,7 +460,8 @@ reconnect_run(struct reconnect *fsm, long long int now)
         case S_BACKOFF:
             return RECONNECT_CONNECT;
 
-        case S_CONNECTING:
+        case S_START_CONNECT:
+        case S_CONNECT_IN_PROGRESS:
             return RECONNECT_DISCONNECT;
 
         case S_ACTIVE:
@@ -453,7 +482,7 @@ reconnect_run(struct reconnect *fsm, long long int now)
 
         NOT_REACHED();
     } else {
-        return fsm->state == S_CONNECTING ? RECONNECT_CONNECT : 0;
+        return fsm->state == S_START_CONNECT ? RECONNECT_CONNECT : 0;
     }
 }
 
@@ -521,3 +550,13 @@ reconnect_get_stats(const struct reconnect *fsm, long long int now,
     stats->state = reconnect_state_name__(fsm->state);
     stats->state_elapsed = now - fsm->state_entered;
 }
+
+static bool
+reconnect_may_retry(struct reconnect *fsm)
+{
+    bool may_retry = fsm->max_tries > 0;
+    if (may_retry && fsm->max_tries != UINT_MAX) {
+        fsm->max_tries--;
+    }
+    return may_retry;
+}