1/*
2 * Copyright (c) 2017 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#define _GNU_SOURCE
17
18#include <vnet/bonding/node.h>
19#include <lacp/node.h>
20
21/*
22 *  LACP State = NO_PERIODIC
23 */
24static lacp_fsm_state_t lacp_ptx_state_no_periodic[] = {
25  {LACP_ACTION_NO_PERIODIC, LACP_PTX_STATE_NO_PERIODIC},	// event 0 NO_PERIODIC
26  {LACP_ACTION_SLOW_PERIODIC, LACP_PTX_STATE_SLOW_PERIODIC},	// event 1 LONG_TIMEOUT
27  {LACP_ACTION_NO_PERIODIC, LACP_PTX_STATE_NO_PERIODIC},	// event 2 TIMER_EXPIRED
28  {LACP_ACTION_FAST_PERIODIC, LACP_PTX_STATE_FAST_PERIODIC},	// event 3 SHORT_TIMEOUT
29};
30
31/*
32 *  LACP State = FAST_PERIODIC
33 */
34static lacp_fsm_state_t lacp_ptx_state_fast_periodic[] = {
35  {LACP_ACTION_NO_PERIODIC, LACP_PTX_STATE_NO_PERIODIC},	// event 0 NO_PERIODIC
36  {LACP_ACTION_SLOW_PERIODIC, LACP_PTX_STATE_SLOW_PERIODIC},	// event 1 LONG_TIMEOUT
37  {LACP_ACTION_TIMER_EXPIRED, LACP_PTX_STATE_PERIODIC_TX},	// event 2 TIMER_EXPIRED
38  {LACP_ACTION_FAST_PERIODIC, LACP_PTX_STATE_FAST_PERIODIC},	// event 3 SHORT_TIMEOUT
39};
40
41/*
42 *  LACP State = SLOW_PERIODIC
43 */
44static lacp_fsm_state_t lacp_ptx_state_slow_periodic[] = {
45  {LACP_ACTION_NO_PERIODIC, LACP_PTX_STATE_NO_PERIODIC},	// event 0 NO_PERIODIC
46  {LACP_ACTION_SLOW_PERIODIC, LACP_PTX_STATE_SLOW_PERIODIC},	// event 1 LONG_TIMEOUT
47  {LACP_ACTION_TIMER_EXPIRED, LACP_PTX_STATE_PERIODIC_TX},	// event 2 TIMER_EXPIRED
48  {LACP_ACTION_FAST_PERIODIC, LACP_PTX_STATE_FAST_PERIODIC},	// event 3 SHORT_TIMEOUT
49};
50
51/*
52 *  LACP State = PERIODIC_TX
53 */
54static lacp_fsm_state_t lacp_ptx_state_periodic_tx[] = {
55  {LACP_ACTION_NO_PERIODIC, LACP_PTX_STATE_NO_PERIODIC},	// event 0 NO_PERIODIC
56  {LACP_NOACTION, LACP_PTX_STATE_PERIODIC_TX},	// event 1 LONG_TIMEOUT
57  {LACP_ACTION_TIMER_EXPIRED, LACP_PTX_STATE_PERIODIC_TX},	// event 2 TIMER_EXPIRED
58  {LACP_NOACTION, LACP_PTX_STATE_PERIODIC_TX},	// event 3 SHORT_TIMEOUT
59};
60
61
62static lacp_fsm_machine_t lacp_ptx_fsm_table[] = {
63  {lacp_ptx_state_no_periodic},
64  {lacp_ptx_state_fast_periodic},
65  {lacp_ptx_state_slow_periodic},
66  {lacp_ptx_state_periodic_tx},
67};
68
69lacp_machine_t lacp_ptx_machine = {
70  lacp_ptx_fsm_table,
71  lacp_ptx_debug_func,
72};
73
74int
75lacp_ptx_action_no_periodic (void *p1, void *p2)
76{
77  vlib_main_t *vm = p1;
78  slave_if_t *sif = p2;
79
80  lacp_stop_timer (&sif->periodic_timer);
81  lacp_ptx_post_short_timeout_event (vm, sif);
82  return 0;
83}
84
85int
86lacp_ptx_action_slow_periodic (void *p1, void *p2)
87{
88  vlib_main_t *vm = p1;
89  slave_if_t *sif = p2;
90  u8 timer_expired;
91
92  if (!(sif->partner.state & LACP_STATE_LACP_ACTIVITY) &&
93      !(sif->actor.state & LACP_STATE_LACP_ACTIVITY))
94    lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
95			   LACP_PTX_EVENT_NO_PERIODIC, &sif->ptx_state);
96  else
97    {
98      if (lacp_timer_is_running (sif->periodic_timer) &&
99	  lacp_timer_is_expired (vm, sif->periodic_timer))
100	timer_expired = 1;
101      else
102	timer_expired = 0;
103
104      lacp_schedule_periodic_timer (vm, sif);
105
106      if (timer_expired || (sif->partner.state & LACP_STATE_LACP_TIMEOUT))
107	lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
108			       LACP_PTX_EVENT_TIMER_EXPIRED, &sif->ptx_state);
109    }
110
111  return 0;
112}
113
114int
115lacp_ptx_action_fast_periodic (void *p1, void *p2)
116{
117  vlib_main_t *vm = p1;
118  slave_if_t *sif = p2;
119  u8 timer_expired;
120
121  if (!(sif->partner.state & LACP_STATE_LACP_ACTIVITY) &&
122      !(sif->actor.state & LACP_STATE_LACP_ACTIVITY))
123    lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
124			   LACP_PTX_EVENT_NO_PERIODIC, &sif->ptx_state);
125  else
126    {
127      if (lacp_timer_is_running (sif->periodic_timer) &&
128	  lacp_timer_is_expired (vm, sif->periodic_timer))
129	timer_expired = 1;
130      else
131	timer_expired = 0;
132
133      lacp_start_periodic_timer (vm, sif, LACP_FAST_PERIODIC_TIMER);
134
135      if (timer_expired)
136	lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
137			       LACP_PTX_EVENT_TIMER_EXPIRED, &sif->ptx_state);
138
139      if (!(sif->partner.state & LACP_STATE_LACP_TIMEOUT))
140	lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
141			       LACP_PTX_EVENT_LONG_TIMEOUT, &sif->ptx_state);
142    }
143
144  return 0;
145}
146
147int
148lacp_ptx_action_timer_expired (void *p1, void *p2)
149{
150  vlib_main_t *vm = p1;
151  slave_if_t *sif = p2;
152
153  if (!(sif->partner.state & LACP_STATE_LACP_ACTIVITY) &&
154      !(sif->actor.state & LACP_STATE_LACP_ACTIVITY))
155    lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
156			   LACP_PTX_EVENT_NO_PERIODIC, &sif->ptx_state);
157  else
158    {
159      sif->ntt = 1;
160      lacp_machine_dispatch (&lacp_tx_machine, vm, sif, LACP_TX_EVENT_NTT,
161			     &sif->tx_state);
162      if (sif->partner.state & LACP_STATE_LACP_TIMEOUT)
163	lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
164			       LACP_PTX_EVENT_SHORT_TIMEOUT, &sif->ptx_state);
165      else
166	lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
167			       LACP_PTX_EVENT_LONG_TIMEOUT, &sif->ptx_state);
168    }
169
170  return 0;
171}
172
173static u8 *
174format_ptx_event (u8 * s, va_list * args)
175{
176  static lacp_event_struct lacp_ptx_event_array[] = {
177#define _(b, s, n) {.bit = b, .str = #s, },
178    foreach_lacp_ptx_event
179#undef _
180    {.str = NULL}
181  };
182  int e = va_arg (*args, int);
183  lacp_event_struct *event_entry = lacp_ptx_event_array;
184
185  if (e >= (sizeof (lacp_ptx_event_array) / sizeof (*event_entry)))
186    s = format (s, "Bad event %d", e);
187  else
188    s = format (s, "%s", event_entry[e].str);
189
190  return s;
191}
192
193void
194lacp_ptx_debug_func (slave_if_t * sif, int event, int state,
195		     lacp_fsm_state_t * transition)
196{
197  vlib_worker_thread_t *w = vlib_worker_threads + os_get_thread_index ();
198  /* *INDENT-OFF* */
199  ELOG_TYPE_DECLARE (e) =
200    {
201      .format = "%s",
202      .format_args = "T4",
203    };
204  /* *INDENT-ON* */
205  struct
206  {
207    u32 event;
208  } *ed = 0;
209
210  ed = ELOG_TRACK_DATA (&vlib_global_main.elog_main, e, w->elog_track);
211  ed->event =
212    elog_string (&vlib_global_main.elog_main, "%U-PTX: %U, %U->%U%c",
213		 format_vnet_sw_if_index_name, vnet_get_main (),
214		 sif->sw_if_index, format_ptx_event, event,
215		 format_ptx_sm_state, state, format_ptx_sm_state,
216		 transition->next_state, 0);
217}
218
219void
220lacp_init_ptx_machine (vlib_main_t * vm, slave_if_t * sif)
221{
222  lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
223			 LACP_PTX_EVENT_NO_PERIODIC, &sif->ptx_state);
224}
225
226/*
227 * fd.io coding-style-patch-verification: ON
228 *
229 * Local Variables:
230 * eval: (c-set-style "gnu")
231 * End:
232 */
233