+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 NetDEF, Inc.
- *
- * This program 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 of the License, or (at your option)
- * any later version.
- *
- * This program 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 this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#include "command.h"
#include "libfrr.h"
#include "printfrr.h"
-#include "lib/version.h"
#include "northbound.h"
#include "frr_pthread.h"
#include "jhash.h"
#define MAX_RECONNECT_DELAY 120
-#define min(a, b) \
- ({ \
- __typeof__(a) _a = (a); \
- __typeof__(b) _b = (b); \
- _a <= _b ? _a : _b; \
- })
-
-
/* Event handling data structures */
enum pcep_ctrl_event_type {
EV_UPDATE_PCC_OPTS = 1,
EV_PCEPLIB_EVENT,
EV_RESET_PCC_SESSION,
EV_SEND_REPORT,
+ EV_SEND_ERROR,
EV_PATH_REFINED
};
/* Internal Functions Called From Main Thread */
static int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res);
-static int pcep_refine_path_event_cb(struct thread *thread);
+static void pcep_refine_path_event_cb(struct event *thread);
/* Internal Functions Called From Controller Thread */
-static int pcep_thread_finish_event_handler(struct thread *thread);
+static void pcep_thread_finish_event_handler(struct event *thread);
/* Controller Thread Timer Handler */
static int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id,
enum pcep_ctrl_timer_type timer_type,
enum pcep_ctrl_timeout_type timeout_type,
uint32_t delay, void *payload,
- struct thread **thread);
+ struct event **thread);
static int schedule_thread_timer_with_cb(
struct ctrl_state *ctrl_state, int pcc_id,
enum pcep_ctrl_timer_type timer_type,
enum pcep_ctrl_timeout_type timeout_type, uint32_t delay, void *payload,
- struct thread **thread, pcep_ctrl_thread_callback timer_cb);
-static int pcep_thread_timer_handler(struct thread *thread);
+ struct event **thread, pcep_ctrl_thread_callback timer_cb);
+static void pcep_thread_timer_handler(struct event *thread);
/* Controller Thread Socket read/write Handler */
static int schedule_thread_socket(struct ctrl_state *ctrl_state, int pcc_id,
enum pcep_ctrl_socket_type type, bool is_read,
- void *payload, int fd, struct thread **thread,
+ void *payload, int fd, struct event **thread,
pcep_ctrl_thread_callback cb);
/* Controller Thread Event Handler */
enum pcep_ctrl_event_type type,
uint32_t sub_type, void *payload,
pcep_ctrl_thread_callback event_cb);
-static int pcep_thread_event_handler(struct thread *thread);
+static void pcep_thread_event_handler(struct event *thread);
static int pcep_thread_event_update_pcc_options(struct ctrl_state *ctrl_state,
struct pcc_opts *opts);
static int pcep_thread_event_update_pce_options(struct ctrl_state *ctrl_state,
/* Main Thread Event Handler */
static int send_to_main(struct ctrl_state *ctrl_state, int pcc_id,
enum pcep_main_event_type type, void *payload);
-static int pcep_main_event_handler(struct thread *thread);
+static void pcep_main_event_handler(struct event *thread);
/* Helper functions */
static void set_ctrl_state(struct frr_pthread *fpt,
}
+int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id,
+ struct pcep_error *error)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, pcc_id, EV_SEND_ERROR, 0, error);
+}
+
+
/* ------------ Internal Functions Called from Main Thread ------------ */
int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
{
- thread_add_event(fpt->master, pcep_thread_finish_event_handler,
- (void *)fpt, 0, NULL);
+ event_add_event(fpt->master, pcep_thread_finish_event_handler,
+ (void *)fpt, 0, NULL);
pthread_join(fpt->thread, res);
return 0;
}
-int pcep_refine_path_event_cb(struct thread *thread)
+void pcep_refine_path_event_cb(struct event *thread)
{
struct pcep_refine_path_event_data *data = THREAD_ARG(thread);
assert(data != NULL);
path_pcep_refine_path(path);
- return send_to_thread(ctrl_state, pcc_id, EV_PATH_REFINED, 0, data);
+ send_to_thread(ctrl_state, pcc_id, EV_PATH_REFINED, 0, data);
}
path);
}
+void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path)
+{
+ send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_INITIATE_CANDIDATE,
+ path);
+}
+
void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state)
{
void pcep_thread_schedule_sync_best_pce(struct ctrl_state *ctrl_state,
int pcc_id, int delay,
- struct thread **thread)
+ struct event **thread)
{
schedule_thread_timer(ctrl_state, pcc_id, TM_CALCULATE_BEST_PCE,
TO_UNDEFINED, delay, NULL, thread);
}
-void pcep_thread_cancel_timer(struct thread **thread)
+void pcep_thread_cancel_timer(struct event **thread)
{
if (thread == NULL || *thread == NULL) {
return;
}
if ((*thread)->master->owner == pthread_self()) {
- thread_cancel(thread);
+ event_cancel(thread);
} else {
- thread_cancel_async((*thread)->master, thread, NULL);
+ event_cancel_async((*thread)->master, thread, NULL);
}
}
void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id,
- int retry_count, struct thread **thread)
+ int retry_count, struct event **thread)
{
uint32_t delay = backoff_delay(MAX_RECONNECT_DELAY, 1, retry_count);
PCEP_DEBUG("Schedule RECONNECT_PCC for %us (retry %u)", delay,
void pcep_thread_schedule_timeout(struct ctrl_state *ctrl_state, int pcc_id,
enum pcep_ctrl_timeout_type timeout_type,
uint32_t delay, void *param,
- struct thread **thread)
+ struct event **thread)
{
assert(timeout_type > TO_UNDEFINED);
assert(timeout_type < TO_MAX);
void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state,
int delay, void *payload,
- struct thread **thread,
+ struct event **thread,
pcep_ctrl_thread_callback timer_cb)
{
PCEP_DEBUG("Schedule PCEPLIB_TIMER for %us", delay);
void pcep_thread_schedule_session_timeout(struct ctrl_state *ctrl_state,
int pcc_id, int delay,
- struct thread **thread)
+ struct event **thread)
{
PCEP_DEBUG("Schedule session_timeout interval for %us", delay);
schedule_thread_timer(ctrl_state, pcc_id, TM_SESSION_TIMEOUT_PCC,
data->continue_lsp_update_handler = cb;
data->payload = payload;
- thread_add_event(ctrl_state->main, pcep_refine_path_event_cb,
- (void *)data, 0, NULL);
+ event_add_event(ctrl_state->main, pcep_refine_path_event_cb,
+ (void *)data, 0, NULL);
return 0;
}
/* ------------ Internal Functions Called From Controller Thread ------------ */
-int pcep_thread_finish_event_handler(struct thread *thread)
+void pcep_thread_finish_event_handler(struct event *thread)
{
int i;
struct frr_pthread *fpt = THREAD_ARG(thread);
fpt->data = NULL;
atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
- return 0;
}
/* ------------ Controller Thread Timer Handler ------------ */
enum pcep_ctrl_timer_type timer_type,
enum pcep_ctrl_timeout_type timeout_type,
uint32_t delay, void *payload,
- struct thread **thread,
+ struct event **thread,
pcep_ctrl_thread_callback timer_cb)
{
assert(thread != NULL);
data->pcc_id = pcc_id;
data->payload = payload;
- thread_add_timer(ctrl_state->self, timer_cb, (void *)data, delay,
- thread);
+ event_add_timer(ctrl_state->self, timer_cb, (void *)data, delay,
+ thread);
return 0;
}
int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id,
enum pcep_ctrl_timer_type timer_type,
enum pcep_ctrl_timeout_type timeout_type,
- uint32_t delay, void *payload, struct thread **thread)
+ uint32_t delay, void *payload, struct event **thread)
{
return schedule_thread_timer_with_cb(ctrl_state, pcc_id, timer_type,
timeout_type, delay, payload,
thread, pcep_thread_timer_handler);
}
-int pcep_thread_timer_handler(struct thread *thread)
+void pcep_thread_timer_handler(struct event *thread)
{
/* data unpacking */
struct pcep_ctrl_timer_data *data = THREAD_ARG(thread);
void *param = data->payload;
XFREE(MTYPE_PCEP, data);
- int ret = 0;
struct pcc_state *pcc_state = NULL;
switch (timer_type) {
case TM_RECONNECT_PCC:
pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
if (!pcc_state)
- return ret;
+ return;
pcep_pcc_reconnect(ctrl_state, pcc_state);
break;
case TM_TIMEOUT:
pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
if (!pcc_state)
- return ret;
+ return;
pcep_pcc_timeout_handler(ctrl_state, pcc_state, timeout_type,
param);
break;
case TM_CALCULATE_BEST_PCE:
/* Previous best disconnect so new best should be synced */
- ret = pcep_pcc_timer_update_best_pce(ctrl_state, pcc_id);
+ pcep_pcc_timer_update_best_pce(ctrl_state, pcc_id);
break;
case TM_SESSION_TIMEOUT_PCC:
pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
pcep_thread_remove_candidate_path_segments(ctrl_state,
pcc_state);
break;
- default:
+ case TM_PCEPLIB_TIMER:
+ case TM_UNDEFINED:
+ case TM_MAX:
flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
"Unknown controller timer triggered: %u", timer_type);
break;
}
-
- return ret;
}
-int pcep_thread_pcep_event(struct thread *thread)
+void pcep_thread_pcep_event(struct event *thread)
{
struct pcep_ctrl_event_data *data = THREAD_ARG(thread);
assert(data != NULL);
}
}
destroy_pcep_event(event);
-
- return 0;
}
/* ------------ Controller Thread Socket Functions ------------ */
int schedule_thread_socket(struct ctrl_state *ctrl_state, int pcc_id,
enum pcep_ctrl_socket_type type, bool is_read,
- void *payload, int fd, struct thread **thread,
+ void *payload, int fd, struct event **thread,
pcep_ctrl_thread_callback socket_cb)
{
assert(thread != NULL);
data->payload = payload;
if (is_read) {
- thread_add_read(ctrl_state->self, socket_cb, (void *)data, fd,
- thread);
+ event_add_read(ctrl_state->self, socket_cb, (void *)data, fd,
+ thread);
} else {
- thread_add_write(ctrl_state->self, socket_cb, (void *)data, fd,
- thread);
+ event_add_write(ctrl_state->self, socket_cb, (void *)data, fd,
+ thread);
}
return 0;
struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data;
return schedule_thread_socket(ctrl_state, 0, SOCK_PCEPLIB, false,
- payload, fd, (struct thread **)thread,
+ payload, fd, (struct event **)thread,
socket_cb);
}
struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data;
return schedule_thread_socket(ctrl_state, 0, SOCK_PCEPLIB, true,
- payload, fd, (struct thread **)thread,
+ payload, fd, (struct event **)thread,
socket_cb);
}
data->pcc_id = pcc_id;
data->payload = payload;
- thread_add_event(ctrl_state->self, event_cb, (void *)data, 0, NULL);
+ event_add_event(ctrl_state->self, event_cb, (void *)data, 0, NULL);
return 0;
}
-int pcep_thread_event_handler(struct thread *thread)
+void pcep_thread_event_handler(struct event *thread)
{
/* data unpacking */
struct pcep_ctrl_event_data *data = THREAD_ARG(thread);
void *payload = data->payload;
XFREE(MTYPE_PCEP, data);
- int ret = 0;
-
/* Possible sub-type values */
enum pcep_pathd_event_type path_event_type = PCEP_PATH_UNDEFINED;
struct pcep_refine_path_event_data *refine_data = NULL;
struct path *path_copy = NULL;
+ struct pcep_error *error = NULL;
switch (type) {
case EV_UPDATE_PCC_OPTS:
assert(payload != NULL);
pcc_opts = (struct pcc_opts *)payload;
- ret = pcep_thread_event_update_pcc_options(ctrl_state,
- pcc_opts);
+ pcep_thread_event_update_pcc_options(ctrl_state, pcc_opts);
break;
case EV_UPDATE_PCE_OPTS:
assert(payload != NULL);
pce_opts = (struct pce_opts *)payload;
- ret = pcep_thread_event_update_pce_options(ctrl_state, pcc_id,
- pce_opts);
+ pcep_thread_event_update_pce_options(ctrl_state, pcc_id,
+ pce_opts);
break;
case EV_REMOVE_PCC:
pce_opts = (struct pce_opts *)payload;
- ret = pcep_thread_event_remove_pcc(ctrl_state, pce_opts);
- if (ret == 0) {
- ret = pcep_pcc_multi_pce_remove_pcc(ctrl_state,
- ctrl_state->pcc);
- }
+ if (pcep_thread_event_remove_pcc(ctrl_state, pce_opts) == 0)
+ pcep_pcc_multi_pce_remove_pcc(ctrl_state,
+ ctrl_state->pcc);
break;
case EV_PATHD_EVENT:
assert(payload != NULL);
path_event_type = (enum pcep_pathd_event_type)sub_type;
path = (struct path *)payload;
- ret = pcep_thread_event_pathd_event(ctrl_state, path_event_type,
- path);
+ pcep_thread_event_pathd_event(ctrl_state, path_event_type,
+ path);
break;
case EV_SYNC_PATH:
assert(payload != NULL);
pcep_thread_event_sync_path(ctrl_state, pcc_id, path);
break;
case EV_SYNC_DONE:
- ret = pcep_thread_event_sync_done(ctrl_state, pcc_id);
+ pcep_thread_event_sync_done(ctrl_state, pcc_id);
break;
case EV_RESET_PCC_SESSION:
pcc_state = pcep_pcc_get_pcc_by_name(ctrl_state->pcc,
(const char *)payload);
if (pcc_state) {
pcep_pcc_disable(ctrl_state, pcc_state);
- ret = pcep_pcc_enable(ctrl_state, pcc_state);
+ pcep_pcc_enable(ctrl_state, pcc_state);
} else {
flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
"Cannot reset state for PCE: %s",
refine_data = (struct pcep_refine_path_event_data *)payload;
pcep_thread_path_refined_event(ctrl_state, refine_data);
break;
- default:
+ case EV_SEND_ERROR:
+ assert(payload != NULL);
+ error = (struct pcep_error *)payload;
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_send_error(ctrl_state, pcc_state, error,
+ (bool)sub_type);
+ break;
+ case EV_PCEPLIB_EVENT:
flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
"Unexpected event received in controller thread: %u",
type);
break;
}
-
- return ret;
}
int pcep_thread_event_update_pcc_options(struct ctrl_state *ctrl_state,
data->pcc_id = pcc_id;
data->payload = payload;
- thread_add_event(ctrl_state->main, pcep_main_event_handler,
- (void *)data, 0, NULL);
+ event_add_event(ctrl_state->main, pcep_main_event_handler, (void *)data,
+ 0, NULL);
return 0;
}
-int pcep_main_event_handler(struct thread *thread)
+void pcep_main_event_handler(struct event *thread)
{
/* data unpacking */
struct pcep_main_event_data *data = THREAD_ARG(thread);
void *payload = data->payload;
XFREE(MTYPE_PCEP, data);
- return handler(type, pcc_id, payload);
+ handler(type, pcc_id, payload);
}
uint32_t backoff_delay(uint32_t max, uint32_t base, uint32_t retry_count)
{
- uint32_t a = min(max, base * (1 << retry_count));
+ uint32_t a = MIN(max, base * (1 << retry_count));
uint64_t r = frr_weak_random(), m = RAND_MAX;
uint32_t b = (a / 2) + (r * (a / 2)) / m;
return b;
return "PCEPLIB_TIMER";
case TM_TIMEOUT:
return "TIMEOUT";
- default:
+ case TM_CALCULATE_BEST_PCE:
+ return "BEST_PCE";
+ case TM_SESSION_TIMEOUT_PCC:
+ return "TIMEOUT_PCC";
+ case TM_MAX:
return "UNKNOWN";
}
-};
+
+ assert(!"Reached end of function where we did not expect to");
+}
const char *timeout_type_name(enum pcep_ctrl_timeout_type type)
{
return "UNDEFINED";
case TO_COMPUTATION_REQUEST:
return "COMPUTATION_REQUEST";
- default:
+ case TO_MAX:
return "UNKNOWN";
}
+
+ assert(!"Reached end of function where we did not expect to");
}