91493ade293775ef067004b1baefc5b2a07337cb
[openvswitch] / switch / controller.c
1 /* Copyright (C) 2008 Board of Trustees, Leland Stanford Jr. University.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21
22 #include "controller.h"
23 #include <errno.h>
24 #include <string.h>
25 #include "buffer.h"
26 #include "forward.h"
27 #include "poll-loop.h"
28 #include "ofp-print.h"
29 #include "util.h"
30 #include "vconn.h"
31
32 #define THIS_MODULE VLM_controller_connection
33 #include "vlog.h"
34
35 void
36 controller_init(struct controller_connection *cc,
37                 const char *name, bool reliable)
38 {
39     cc->reliable = reliable;
40     cc->name = name;
41     cc->vconn = NULL;
42     queue_init(&cc->txq);
43     cc->backoff_deadline = 0;
44     cc->backoff = 0;
45 }
46
47 static int
48 try_send(struct controller_connection *cc)
49 {
50     int retval = 0;
51     struct buffer *next = cc->txq.head->next;
52     retval = vconn_send(cc->vconn, cc->txq.head);
53     if (retval) {
54         return retval;
55     }
56     queue_advance_head(&cc->txq, next);
57     return 0;
58 }
59
60 void
61 controller_run(struct controller_connection *cc, struct datapath *dp)
62 {
63     if (!cc->vconn) {
64         if (time(0) >= cc->backoff_deadline) {
65             int retval;
66
67             retval = vconn_open(cc->name, &cc->vconn);
68             if (!retval) {
69                 cc->backoff_deadline = time(0) + cc->backoff;
70                 cc->connected = false;
71             } else {
72                 VLOG_WARN("%s: connection failed (%s)",
73                           cc->name, strerror(retval)); 
74                 controller_disconnect(cc, 0);
75             }
76         }
77     } else if (!cc->connected) {
78         int error = vconn_connect(cc->vconn);
79         if (!error) {
80             VLOG_WARN("%s: connected", cc->name);
81             if (vconn_is_passive(cc->vconn)) {
82                 fatal(0, "%s: passive vconn not supported in switch",
83                       cc->name);
84             }
85             cc->connected = true;
86         } else if (error != EAGAIN) {
87             VLOG_WARN("%s: connection failed (%s)",
88                       cc->name, strerror(error));
89             controller_disconnect(cc, 0);
90         }
91     } else {
92         int iterations;
93
94         for (iterations = 0; iterations < 50; iterations++) {
95             struct buffer *buffer;
96             int error = vconn_recv(cc->vconn, &buffer);
97             if (!error) {
98                 fwd_control_input(dp, buffer->data, buffer->size);
99                 buffer_delete(buffer);
100             } else if (error == EAGAIN) {
101                 break;
102             } else {
103                 controller_disconnect(cc, error);
104                 return;
105             }
106         }
107
108         while (cc->txq.n > 0) {
109             int error = try_send(cc);
110             if (error == EAGAIN) {
111                 break;
112             } else if (error) {
113                 controller_disconnect(cc, error);
114                 return;
115             }
116         } 
117     }
118 }
119
120 void
121 controller_disconnect(struct controller_connection *cc, int error) 
122 {
123     time_t now = time(0);
124     
125     if (cc->vconn) {
126         if (!cc->reliable) {
127             fatal(0, "%s: connection dropped", cc->name);
128         }
129
130         if (error > 0) {
131             VLOG_WARN("%s: connection dropped (%s)",
132                       cc->name, strerror(error)); 
133         } else if (error == EOF) { 
134             VLOG_WARN("%s: connection closed", cc->name); 
135         } else {
136             VLOG_WARN("%s: connection dropped", cc->name); 
137         }
138         vconn_close(cc->vconn);
139         cc->vconn = NULL;
140         queue_clear(&cc->txq);
141     }
142
143     if (now >= cc->backoff_deadline) {
144         cc->backoff = 1;
145     } else {
146         cc->backoff = MIN(60, MAX(1, 2 * cc->backoff));
147         VLOG_WARN("%s: waiting %d seconds before reconnect\n",
148                   cc->name, cc->backoff);
149     }
150     cc->backoff_deadline = now + cc->backoff;
151 }
152
153 void
154 controller_wait(struct controller_connection *cc) 
155 {
156     if (cc->vconn) {
157         vconn_wait(cc->vconn, WAIT_RECV);
158         if (cc->txq.n) {
159             vconn_wait(cc->vconn, WAIT_SEND);
160         }
161     } else {
162         poll_timer_wait((cc->backoff_deadline - time(0)) * 1000);
163     }
164 }
165
166 void
167 controller_send(struct controller_connection *cc, struct buffer *b) 
168 {
169     if (cc->vconn) {
170         if (cc->txq.n < 128) {
171             queue_push_tail(&cc->txq, b);
172             if (cc->txq.n == 1) {
173                 try_send(cc);
174             }
175         } else {
176             VLOG_WARN("%s: controller queue overflow", cc->name);
177             buffer_delete(b);
178         } 
179     }
180 }