/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "sat-math.h"
#include "timeval.h"
#include "util.h"
-#include "vconn.h"
-#include "vlog.h"
+#include "openvswitch/vconn.h"
+#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(rconn);
COVERAGE_DEFINE(rconn_queued);
COVERAGE_DEFINE(rconn_sent);
+/* The connection states have the following meanings:
+ *
+ * - S_VOID: No connection information is configured.
+ *
+ * - S_BACKOFF: Waiting for a period of time before reconnecting.
+ *
+ * - S_CONNECTING: A connection attempt is in progress and has not yet
+ * succeeded or failed.
+ *
+ * - S_ACTIVE: A connection has been established and appears to be healthy.
+ *
+ * - S_IDLE: A connection has been established but has been idle for some
+ * time. An echo request has been sent, but no reply has yet been
+ * received.
+ *
+ * - S_DISCONNECTED: An unreliable connection has disconnected and cannot be
+ * automatically retried.
+ */
#define STATES \
STATE(VOID, 1 << 0) \
STATE(BACKOFF, 1 << 1) \
STATE(CONNECTING, 1 << 2) \
STATE(ACTIVE, 1 << 3) \
- STATE(IDLE, 1 << 4)
+ STATE(IDLE, 1 << 4) \
+ STATE(DISCONNECTED, 1 << 5)
enum state {
#define STATE(NAME, VALUE) S_##NAME = VALUE,
STATES
char *target; /* vconn name, passed to vconn_open(). */
bool reliable;
- struct list txq; /* Contains "struct ofpbuf"s. */
+ struct ovs_list txq; /* Contains "struct ofpbuf"s. */
int backoff;
int max_backoff;
time_t backoff_deadline;
time_t last_connected;
time_t last_disconnected;
- unsigned int packets_sent;
unsigned int seqno;
int last_error;
/* These values are simply for statistics reporting, not used directly by
* anything internal to the rconn (or ofproto for that matter). */
- unsigned int packets_received;
unsigned int n_attempted_connections, n_successful_connections;
time_t creation_time;
unsigned long int total_time_connected;
uint8_t dscp;
/* Messages sent or received are copied to the monitor connections. */
-#define MAX_MONITORS 8
- struct vconn *monitors[8];
+#define MAXIMUM_MONITORS 8
+ struct vconn *monitors[MAXIMUM_MONITORS];
size_t n_monitors;
uint32_t allowed_versions;
rc->last_disconnected = TIME_MIN;
rc->seqno = 0;
- rc->packets_sent = 0;
-
rc->probably_admitted = false;
rc->last_admitted = time_now();
- rc->packets_received = 0;
rc->n_attempted_connections = 0;
rc->n_successful_connections = 0;
rc->creation_time = time_now();
}
}
+static unsigned int
+timeout_DISCONNECTED(const struct rconn *rc OVS_UNUSED)
+ OVS_REQUIRES(rc->mutex)
+{
+ return UINT_MAX;
+}
+
+static void
+run_DISCONNECTED(struct rconn *rc OVS_UNUSED)
+ OVS_REQUIRES(rc->mutex)
+{
+ /* Nothing to do. */
+}
+
/* Performs whatever activities are necessary to maintain 'rc': if 'rc' is
* disconnected, attempts to (re)connect, backing off as necessary; if 'rc' is
* connected, attempts to send packets in the send queue, if any. */
ovs_mutex_lock(&rc->mutex);
if (rc->vconn) {
+ int error;
+
vconn_run(rc->vconn);
+
+ error = vconn_get_status(rc->vconn);
+ if (error) {
+ report_error(rc, error);
+ disconnect(rc, error);
+ }
}
for (i = 0; i < rc->n_monitors; ) {
struct ofpbuf *msg;
rc->last_admitted = time_now();
}
rc->last_activity = time_now();
- rc->packets_received++;
if (rc->state == S_IDLE) {
state_transition(rc, S_ACTIVE);
}
if (rconn_is_connected(rc)) {
COVERAGE_INC(rconn_queued);
copy_to_monitor(rc, b);
- b->private_p = counter;
+
if (counter) {
rconn_packet_counter_inc(counter, b->size);
}
+
+ /* Reuse 'frame' as a private pointer while 'b' is in txq. */
+ b->header = counter;
+
list_push_back(&rc->txq, &b->list_node);
/* If the queue was empty before we added 'b', try to send some
return error;
}
-/* Returns the total number of packets successfully sent on the underlying
- * vconn. A packet is not counted as sent while it is still queued in the
- * rconn, only when it has been successfuly passed to the vconn. */
-unsigned int
-rconn_packets_sent(const struct rconn *rc)
-{
- return rc->packets_sent;
-}
-
/* Adds 'vconn' to 'rc' as a monitoring connection, to which all messages sent
* and received on 'rconn' will be copied. 'rc' takes ownership of 'vconn'. */
void
bool
rconn_is_alive(const struct rconn *rconn)
{
- return rconn->state != S_VOID;
+ return rconn->state != S_VOID && rconn->state != S_DISCONNECTED;
}
/* Returns true if 'rconn' is connected, false otherwise. */
return version;
}
-/* Returns the total number of packets successfully received by the underlying
- * vconn. */
-unsigned int
-rconn_packets_received(const struct rconn *rc)
-{
- return rc->packets_received;
-}
-
/* Returns a string representing the internal state of 'rc'. The caller must
* not modify or free the string. */
const char *
}
\f
/* Set rc->target and rc->name to 'target' and 'name', respectively. If 'name'
- * is null, 'target' is used.
- *
- * Also, clear out the cached IP address and port information, since changing
- * the target also likely changes these values. */
+ * is null, 'target' is used. */
static void
rconn_set_target__(struct rconn *rc, const char *target, const char *name)
OVS_REQUIRES(rc->mutex)
{
struct ofpbuf *msg = ofpbuf_from_list(rc->txq.next);
unsigned int n_bytes = msg->size;
- struct rconn_packet_counter *counter = msg->private_p;
+ struct rconn_packet_counter *counter = msg->header;
int retval;
/* Eagerly remove 'msg' from the txq. We can't remove it from the list
* after sending, if sending is successful, because it is then owned by the
* vconn, which might have freed it already. */
list_remove(&msg->list_node);
+ msg->header = NULL;
retval = vconn_send(rc->vconn, msg);
if (retval) {
+ msg->header = counter;
list_push_front(&rc->txq, &msg->list_node);
if (retval != EAGAIN) {
report_error(rc, retval);
return retval;
}
COVERAGE_INC(rconn_sent);
- rc->packets_sent++;
if (counter) {
rconn_packet_counter_dec(counter, n_bytes);
}
report_error(struct rconn *rc, int error)
OVS_REQUIRES(rc->mutex)
{
- if (error == EOF) {
+ /* On Windows, when a peer terminates without calling a closesocket()
+ * on socket fd, we get WSAECONNRESET. Don't print warning messages
+ * for that case. */
+ if (error == EOF
+#ifdef _WIN32
+ || error == WSAECONNRESET
+#endif
+ ) {
/* If 'rc' isn't reliable, then we don't really expect this connection
* to last forever anyway (probably it's a connection that we received
* via accept()), so use DBG level to avoid cluttering the logs. */
OVS_REQUIRES(rc->mutex)
{
rc->last_error = error;
+ if (rc->vconn) {
+ vconn_close(rc->vconn);
+ rc->vconn = NULL;
+ }
if (rc->reliable) {
time_t now = time_now();
if (rc->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) {
rc->last_disconnected = now;
- vconn_close(rc->vconn);
- rc->vconn = NULL;
flush_queue(rc);
}
state_transition(rc, S_BACKOFF);
} else {
rc->last_disconnected = time_now();
- rconn_disconnect__(rc);
+ state_transition(rc, S_DISCONNECTED);
}
}
}
while (!list_is_empty(&rc->txq)) {
struct ofpbuf *b = ofpbuf_from_list(list_pop_front(&rc->txq));
- struct rconn_packet_counter *counter = b->private_p;
+ struct rconn_packet_counter *counter = b->header;
if (counter) {
rconn_packet_counter_dec(counter, b->size);
}
state_transition(struct rconn *rc, enum state state)
OVS_REQUIRES(rc->mutex)
{
- rc->seqno += (rc->state == S_ACTIVE) != (state == S_ACTIVE);
+ rc->seqno += is_connected_state(rc->state) != is_connected_state(state);
if (is_connected_state(state) && !is_connected_state(rc->state)) {
rc->probably_admitted = false;
}
case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
+ case OFPTYPE_TABLE_DESC_REQUEST:
+ case OFPTYPE_TABLE_DESC_REPLY:
return false;
case OFPTYPE_PACKET_IN:
case OFPTYPE_ROLE_REQUEST:
case OFPTYPE_ROLE_REPLY:
case OFPTYPE_ROLE_STATUS:
+ case OFPTYPE_REQUESTFORWARD:
+ case OFPTYPE_TABLE_STATUS:
case OFPTYPE_SET_FLOW_FORMAT:
case OFPTYPE_FLOW_MOD_TABLE_ID:
case OFPTYPE_SET_PACKET_IN_FORMAT:
case OFPTYPE_FLOW_MONITOR_CANCEL:
case OFPTYPE_FLOW_MONITOR_PAUSED:
case OFPTYPE_FLOW_MONITOR_RESUMED:
+ case OFPTYPE_BUNDLE_CONTROL:
+ case OFPTYPE_BUNDLE_ADD_MESSAGE:
+ case OFPTYPE_NXT_TLV_TABLE_MOD:
+ case OFPTYPE_NXT_TLV_TABLE_REQUEST:
+ case OFPTYPE_NXT_TLV_TABLE_REPLY:
+ case OFPTYPE_NXT_RESUME:
default:
return true;
}