mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 13:43:22 +00:00 
			
		
		
		
	The way thread.c is written, a caller who wishes to be able to cancel a
thread or avoid scheduling it twice must keep a reference to the thread.
Typically this is done with a long lived pointer whose value is checked
for null in order to know if the thread is currently scheduled.  The
check-and-schedule idiom is so common that several wrapper macros in
thread.h existed solely to provide it.
This patch removes those macros and adds a new parameter to all
thread_add_* functions which is a pointer to the struct thread * to
store the result of a scheduling call. If the value passed is non-null,
the thread will only be scheduled if the value is null. This helps with
consistency.
A Coccinelle spatch has been used to transform code of the form:
  if (t == NULL)
    t = thread_add_* (...)
to the form
  thread_add_* (..., &t)
The THREAD_ON macros have also been transformed to the underlying
thread.c calls.
Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
		
	
			
		
			
				
	
	
		
			340 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			340 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This is an implementation of the IETF SPF delay algorithm
 | 
						|
 * as explained in draft-ietf-rtgwg-backoff-algo-04
 | 
						|
 *
 | 
						|
 * Created: 25-01-2017 by S. Litkowski
 | 
						|
 *
 | 
						|
 * Copyright (C) 2017 Orange Labs http://www.orange.com/
 | 
						|
 * Copyright (C) 2017 by Christian Franke, Open Source Routing / NetDEF Inc.
 | 
						|
 *
 | 
						|
 * This file is part of FreeRangeRouting (FRR)
 | 
						|
 *
 | 
						|
 * FRR is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms of the GNU General Public License as published by the
 | 
						|
 * Free Software Foundation; either version 2, or (at your option) any
 | 
						|
 * later version.
 | 
						|
 *
 | 
						|
 * FRR is distributed in the hope that it will be useful, but
 | 
						|
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with FRR; see the file COPYING.  If not, write to the Free
 | 
						|
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
						|
 * 02111-1307, USA.
 | 
						|
 */
 | 
						|
 | 
						|
#include <zebra.h>
 | 
						|
 | 
						|
#include "spf_backoff.h"
 | 
						|
 | 
						|
#include "command.h"
 | 
						|
#include "memory.h"
 | 
						|
#include "thread.h"
 | 
						|
#include "vty.h"
 | 
						|
 | 
						|
DEFINE_MTYPE_STATIC(LIB, SPF_BACKOFF, "SPF backoff")
 | 
						|
DEFINE_MTYPE_STATIC(LIB, SPF_BACKOFF_NAME, "SPF backoff name")
 | 
						|
 | 
						|
static bool debug_spf_backoff = false;
 | 
						|
#define backoff_debug(...) \
 | 
						|
  do \
 | 
						|
    { \
 | 
						|
      if (debug_spf_backoff) \
 | 
						|
        zlog_debug(__VA_ARGS__); \
 | 
						|
    } \
 | 
						|
  while (0)
 | 
						|
 | 
						|
enum spf_backoff_state {
 | 
						|
  SPF_BACKOFF_QUIET,
 | 
						|
  SPF_BACKOFF_SHORT_WAIT,
 | 
						|
  SPF_BACKOFF_LONG_WAIT
 | 
						|
};
 | 
						|
 | 
						|
struct spf_backoff {
 | 
						|
  struct thread_master *m;
 | 
						|
 | 
						|
  /* Timers as per draft */
 | 
						|
  long init_delay;
 | 
						|
  long short_delay;
 | 
						|
  long long_delay;
 | 
						|
  long holddown;
 | 
						|
  long timetolearn;
 | 
						|
 | 
						|
  /* State machine */
 | 
						|
  enum spf_backoff_state state;
 | 
						|
  struct thread *t_holddown;
 | 
						|
  struct thread *t_timetolearn;
 | 
						|
 | 
						|
  /* For debugging */
 | 
						|
  char *name;
 | 
						|
  struct timeval first_event_time;
 | 
						|
  struct timeval last_event_time;
 | 
						|
};
 | 
						|
 | 
						|
static const char *
 | 
						|
spf_backoff_state2str(enum spf_backoff_state state)
 | 
						|
{
 | 
						|
  switch (state)
 | 
						|
    {
 | 
						|
    case SPF_BACKOFF_QUIET:
 | 
						|
      return "QUIET";
 | 
						|
    case SPF_BACKOFF_SHORT_WAIT:
 | 
						|
      return "SHORT_WAIT";
 | 
						|
    case SPF_BACKOFF_LONG_WAIT:
 | 
						|
      return "LONG_WAIT";
 | 
						|
    }
 | 
						|
  return "???";
 | 
						|
}
 | 
						|
 | 
						|
struct spf_backoff *
 | 
						|
spf_backoff_new(struct thread_master *m,
 | 
						|
                const char *name,
 | 
						|
                long init_delay,
 | 
						|
                long short_delay,
 | 
						|
                long long_delay,
 | 
						|
                long holddown,
 | 
						|
                long timetolearn)
 | 
						|
{
 | 
						|
  struct spf_backoff *rv;
 | 
						|
 | 
						|
  rv = XCALLOC(MTYPE_SPF_BACKOFF, sizeof(*rv));
 | 
						|
  rv->m = m;
 | 
						|
 | 
						|
  rv->init_delay = init_delay;
 | 
						|
  rv->short_delay = short_delay;
 | 
						|
  rv->long_delay = long_delay;
 | 
						|
  rv->holddown = holddown;
 | 
						|
  rv->timetolearn = timetolearn;
 | 
						|
 | 
						|
  rv->state = SPF_BACKOFF_QUIET;
 | 
						|
 | 
						|
  rv->name = XSTRDUP(MTYPE_SPF_BACKOFF_NAME, name);
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
spf_backoff_free(struct spf_backoff *backoff)
 | 
						|
{
 | 
						|
  if (!backoff)
 | 
						|
    return;
 | 
						|
 | 
						|
  THREAD_TIMER_OFF(backoff->t_holddown);
 | 
						|
  THREAD_TIMER_OFF(backoff->t_timetolearn);
 | 
						|
  XFREE(MTYPE_SPF_BACKOFF_NAME, backoff->name);
 | 
						|
 | 
						|
  XFREE(MTYPE_SPF_BACKOFF, backoff);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
spf_backoff_timetolearn_elapsed(struct thread *thread)
 | 
						|
{
 | 
						|
  struct spf_backoff *backoff = THREAD_ARG(thread);
 | 
						|
 | 
						|
  backoff->t_timetolearn = NULL;
 | 
						|
  backoff->state = SPF_BACKOFF_LONG_WAIT;
 | 
						|
  backoff_debug("SPF Back-off(%s) TIMETOLEARN elapsed, move to state %s",
 | 
						|
                backoff->name, spf_backoff_state2str(backoff->state));
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
spf_backoff_holddown_elapsed(struct thread *thread)
 | 
						|
{
 | 
						|
  struct spf_backoff *backoff = THREAD_ARG(thread);
 | 
						|
 | 
						|
  backoff->t_holddown = NULL;
 | 
						|
  THREAD_TIMER_OFF(backoff->t_timetolearn);
 | 
						|
  timerclear(&backoff->first_event_time);
 | 
						|
  backoff->state = SPF_BACKOFF_QUIET;
 | 
						|
  backoff_debug("SPF Back-off(%s) HOLDDOWN elapsed, move to state %s",
 | 
						|
                backoff->name, spf_backoff_state2str(backoff->state));
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
long spf_backoff_schedule(struct spf_backoff *backoff)
 | 
						|
{
 | 
						|
  long rv;
 | 
						|
  struct timeval now;
 | 
						|
 | 
						|
  gettimeofday(&now, NULL);
 | 
						|
 | 
						|
  backoff_debug("SPF Back-off(%s) schedule called in state %s",
 | 
						|
                backoff->name, spf_backoff_state2str(backoff->state));
 | 
						|
 | 
						|
  backoff->last_event_time = now;
 | 
						|
 | 
						|
  switch (backoff->state)
 | 
						|
    {
 | 
						|
    case SPF_BACKOFF_QUIET:
 | 
						|
      backoff->state = SPF_BACKOFF_SHORT_WAIT;
 | 
						|
      thread_add_timer_msec(backoff->m, spf_backoff_timetolearn_elapsed,
 | 
						|
                            backoff, backoff->timetolearn,
 | 
						|
                            &backoff->t_timetolearn);
 | 
						|
      thread_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed, backoff,
 | 
						|
                            backoff->holddown, &backoff->t_holddown);
 | 
						|
      backoff->first_event_time = now;
 | 
						|
      rv = backoff->init_delay;
 | 
						|
      break;
 | 
						|
    case SPF_BACKOFF_SHORT_WAIT:
 | 
						|
    case SPF_BACKOFF_LONG_WAIT:
 | 
						|
      THREAD_TIMER_OFF(backoff->t_holddown);
 | 
						|
      thread_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed, backoff,
 | 
						|
                            backoff->holddown, &backoff->t_holddown);
 | 
						|
      if (backoff->state == SPF_BACKOFF_SHORT_WAIT)
 | 
						|
        rv = backoff->short_delay;
 | 
						|
      else
 | 
						|
        rv = backoff->long_delay;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      zlog_warn("SPF Back-off(%s) in unknown state", backoff->name);
 | 
						|
      rv = backoff->init_delay;
 | 
						|
    }
 | 
						|
 | 
						|
  backoff_debug("SPF Back-off(%s) changed state to %s and returned %ld delay",
 | 
						|
                backoff->name, spf_backoff_state2str(backoff->state), rv);
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
static const char *
 | 
						|
timeval_format(struct timeval *tv)
 | 
						|
{
 | 
						|
  struct tm tm_store;
 | 
						|
  struct tm *tm;
 | 
						|
  static char timebuf[256];
 | 
						|
 | 
						|
  if (!tv->tv_sec && !tv->tv_usec)
 | 
						|
    return "(never)";
 | 
						|
 | 
						|
  tm = localtime_r(&tv->tv_sec, &tm_store);
 | 
						|
  if (!tm || strftime(timebuf, sizeof(timebuf),
 | 
						|
                      "%Z %a %Y-%m-%d %H:%M:%S", tm) == 0)
 | 
						|
    {
 | 
						|
      return "???";
 | 
						|
    }
 | 
						|
 | 
						|
  size_t offset = strlen(timebuf);
 | 
						|
  snprintf(timebuf + offset, sizeof(timebuf) - offset, ".%ld", tv->tv_usec);
 | 
						|
 | 
						|
  return timebuf;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
spf_backoff_show(struct spf_backoff *backoff, struct vty *vty,
 | 
						|
                 const char *prefix)
 | 
						|
{
 | 
						|
  vty_out(vty, "%sCurrent state:     %s%s", prefix,
 | 
						|
          spf_backoff_state2str(backoff->state), VTY_NEWLINE);
 | 
						|
  vty_out(vty, "%sInit timer:        %ld msec%s", prefix,
 | 
						|
          backoff->init_delay, VTY_NEWLINE);
 | 
						|
  vty_out(vty, "%sShort timer:       %ld msec%s", prefix,
 | 
						|
          backoff->short_delay, VTY_NEWLINE);
 | 
						|
  vty_out(vty, "%sLong timer:        %ld msec%s", prefix,
 | 
						|
          backoff->long_delay, VTY_NEWLINE);
 | 
						|
  vty_out(vty, "%sHolddown timer:    %ld msec%s", prefix,
 | 
						|
          backoff->holddown, VTY_NEWLINE);
 | 
						|
  if (backoff->t_holddown)
 | 
						|
    {
 | 
						|
      struct timeval remain = thread_timer_remain(backoff->t_holddown);
 | 
						|
      vty_out(vty, "%s                   Still runs for %ld msec%s",
 | 
						|
              prefix, remain.tv_sec * 1000 + remain.tv_usec/1000, VTY_NEWLINE);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      vty_out(vty, "%s                   Inactive%s", prefix, VTY_NEWLINE);
 | 
						|
    }
 | 
						|
 | 
						|
  vty_out(vty, "%sTimeToLearn timer: %ld msec%s", prefix,
 | 
						|
          backoff->timetolearn, VTY_NEWLINE);
 | 
						|
  if (backoff->t_timetolearn)
 | 
						|
    {
 | 
						|
      struct timeval remain = thread_timer_remain(backoff->t_timetolearn);
 | 
						|
      vty_out(vty, "%s                   Still runs for %ld msec%s",
 | 
						|
              prefix, remain.tv_sec * 1000 + remain.tv_usec/1000, VTY_NEWLINE);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      vty_out(vty, "%s                   Inactive%s", prefix, VTY_NEWLINE);
 | 
						|
    }
 | 
						|
 | 
						|
  vty_out(vty, "%sFirst event:       %s%s", prefix,
 | 
						|
          timeval_format(&backoff->first_event_time), VTY_NEWLINE);
 | 
						|
  vty_out(vty, "%sLast event:        %s%s", prefix,
 | 
						|
          timeval_format(&backoff->last_event_time), VTY_NEWLINE);
 | 
						|
}
 | 
						|
 | 
						|
DEFUN(spf_backoff_debug,
 | 
						|
      spf_backoff_debug_cmd,
 | 
						|
      "debug spf-delay-ietf",
 | 
						|
      DEBUG_STR
 | 
						|
      "SPF Back-off Debugging\n")
 | 
						|
{
 | 
						|
  debug_spf_backoff = true;
 | 
						|
  return CMD_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
DEFUN(no_spf_backoff_debug,
 | 
						|
      no_spf_backoff_debug_cmd,
 | 
						|
      "no debug spf-delay-ietf",
 | 
						|
      NO_STR
 | 
						|
      DEBUG_STR
 | 
						|
      "SPF Back-off Debugging\n")
 | 
						|
{
 | 
						|
  debug_spf_backoff = false;
 | 
						|
  return CMD_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
spf_backoff_write_config(struct vty *vty)
 | 
						|
{
 | 
						|
  int written = 0;
 | 
						|
 | 
						|
  if (debug_spf_backoff)
 | 
						|
    {
 | 
						|
      vty_out(vty, "debug spf-delay-ietf%s", VTY_NEWLINE);
 | 
						|
      written++;
 | 
						|
    }
 | 
						|
 | 
						|
  return written;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
spf_backoff_cmd_init(void)
 | 
						|
{
 | 
						|
  install_element(ENABLE_NODE, &spf_backoff_debug_cmd);
 | 
						|
  install_element(CONFIG_NODE, &spf_backoff_debug_cmd);
 | 
						|
  install_element(ENABLE_NODE, &no_spf_backoff_debug_cmd);
 | 
						|
  install_element(CONFIG_NODE, &no_spf_backoff_debug_cmd);
 | 
						|
}
 | 
						|
 | 
						|
long
 | 
						|
spf_backoff_init_delay(struct spf_backoff *backoff)
 | 
						|
{
 | 
						|
  return backoff->init_delay;
 | 
						|
}
 | 
						|
 | 
						|
long
 | 
						|
spf_backoff_short_delay(struct spf_backoff *backoff)
 | 
						|
{
 | 
						|
  return backoff->short_delay;
 | 
						|
}
 | 
						|
 | 
						|
long
 | 
						|
spf_backoff_long_delay(struct spf_backoff *backoff)
 | 
						|
{
 | 
						|
  return backoff->long_delay;
 | 
						|
}
 | 
						|
 | 
						|
long
 | 
						|
spf_backoff_holddown(struct spf_backoff *backoff)
 | 
						|
{
 | 
						|
  return backoff->holddown;
 | 
						|
}
 | 
						|
 | 
						|
long
 | 
						|
spf_backoff_timetolearn(struct spf_backoff *backoff)
 | 
						|
{
 | 
						|
  return backoff->timetolearn;
 | 
						|
}
 |