|
Packit |
cac203 |
/*
|
|
Packit |
cac203 |
* teamd_lw_psr.c - Team port periodic send/receive link watcher
|
|
Packit |
cac203 |
* Copyright (C) 2012-2015 Jiri Pirko <jiri@resnulli.us>
|
|
Packit |
cac203 |
* Copyright (C) 2014 Erik Hugne <erik.hugne@ericsson.com>
|
|
Packit |
cac203 |
*
|
|
Packit |
cac203 |
* This library is free software; you can redistribute it and/or
|
|
Packit |
cac203 |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
cac203 |
* License as published by the Free Software Foundation; either
|
|
Packit |
cac203 |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
cac203 |
*
|
|
Packit |
cac203 |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
cac203 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
cac203 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
cac203 |
* Lesser General Public License for more details.
|
|
Packit |
cac203 |
*
|
|
Packit |
cac203 |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
cac203 |
* License along with this library; if not, write to the Free Software
|
|
Packit |
cac203 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
cac203 |
*/
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
#include <private/misc.h>
|
|
Packit |
cac203 |
#include "teamd.h"
|
|
Packit |
cac203 |
#include "teamd_link_watch.h"
|
|
Packit |
cac203 |
#include "teamd_config.h"
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
/*
|
|
Packit |
cac203 |
* Generic periodic send/receive link watch "template"
|
|
Packit |
cac203 |
*/
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
static const struct timespec lw_psr_default_init_wait = { 0, 1 };
|
|
Packit |
cac203 |
#define LW_PSR_DEFAULT_INTERVAL 1000
|
|
Packit |
cac203 |
#define LW_PSR_DEFAULT_MISSED_MAX 3
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
#define LW_PERIODIC_CB_NAME "lw_periodic"
|
|
Packit |
cac203 |
static int lw_psr_callback_periodic(struct teamd_context *ctx, int events, void *priv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
struct lw_common_port_priv *common_ppriv = priv;
|
|
Packit |
cac203 |
struct lw_psr_port_priv *psr_ppriv = priv;
|
|
Packit |
cac203 |
struct teamd_port *tdport = common_ppriv->tdport;
|
|
Packit |
cac203 |
bool link_up = common_ppriv->link_up;
|
|
Packit |
cac203 |
int err;
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
if (psr_ppriv->reply_received) {
|
|
Packit |
cac203 |
link_up = true;
|
|
Packit |
cac203 |
psr_ppriv->missed = 0;
|
|
Packit |
cac203 |
} else {
|
|
Packit |
cac203 |
psr_ppriv->missed++;
|
|
Packit |
cac203 |
if (psr_ppriv->missed > psr_ppriv->missed_max && link_up) {
|
|
Packit |
cac203 |
teamd_log_dbg(ctx, "%s: Missed %u replies (max %u).",
|
|
Packit |
cac203 |
tdport->ifname, psr_ppriv->missed,
|
|
Packit |
cac203 |
psr_ppriv->missed_max);
|
|
Packit |
cac203 |
link_up = false;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
err = teamd_link_watch_check_link_up(ctx, tdport,
|
|
Packit |
cac203 |
common_ppriv, link_up);
|
|
Packit |
cac203 |
if (err)
|
|
Packit |
cac203 |
return err;
|
|
Packit |
cac203 |
psr_ppriv->reply_received = false;
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
return psr_ppriv->ops->send(psr_ppriv);
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
#define LW_SOCKET_CB_NAME "lw_socket"
|
|
Packit |
cac203 |
static int lw_psr_callback_socket(struct teamd_context *ctx, int events, void *priv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
struct lw_psr_port_priv *psr_ppriv = priv;
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
return psr_ppriv->ops->receive(psr_ppriv);
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
static int lw_psr_load_options(struct teamd_context *ctx,
|
|
Packit |
cac203 |
struct teamd_port *tdport,
|
|
Packit |
cac203 |
struct lw_psr_port_priv *psr_ppriv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
struct teamd_config_path_cookie *cpcookie = psr_ppriv->common.cpcookie;
|
|
Packit |
cac203 |
int err;
|
|
Packit |
cac203 |
int tmp;
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
err = teamd_config_int_get(ctx, &tmp, "@.interval", cpcookie);
|
|
Packit |
cac203 |
if (!err) {
|
|
Packit |
cac203 |
if (tmp < 0) {
|
|
Packit |
cac203 |
teamd_log_err("\"interval\" must not be negative number.");
|
|
Packit |
cac203 |
return -EINVAL;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
} else {
|
|
Packit |
cac203 |
tmp = LW_PSR_DEFAULT_INTERVAL;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
teamd_log_dbg(ctx, "interval \"%d\".", tmp);
|
|
Packit |
cac203 |
ms_to_timespec(&psr_ppriv->interval, tmp);
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
err = teamd_config_int_get(ctx, &tmp, "@.init_wait", cpcookie);
|
|
Packit |
cac203 |
if (!err)
|
|
Packit |
cac203 |
ms_to_timespec(&psr_ppriv->init_wait, tmp);
|
|
Packit |
cac203 |
/* if init_wait is set to 0, use default_init_wait */
|
|
Packit |
cac203 |
if (err || !tmp)
|
|
Packit |
cac203 |
psr_ppriv->init_wait = lw_psr_default_init_wait;
|
|
Packit |
cac203 |
teamd_log_dbg(ctx, "init_wait \"%d\".", timespec_to_ms(&psr_ppriv->init_wait));
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
err = teamd_config_int_get(ctx, &tmp, "@.missed_max", cpcookie);
|
|
Packit |
cac203 |
if (!err) {
|
|
Packit |
cac203 |
if (tmp < 0) {
|
|
Packit |
cac203 |
teamd_log_err("\"missed_max\" must not be negative number.");
|
|
Packit |
cac203 |
return -EINVAL;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
} else {
|
|
Packit |
cac203 |
tmp = LW_PSR_DEFAULT_MISSED_MAX;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
teamd_log_dbg(ctx, "missed_max \"%d\".", tmp);
|
|
Packit |
cac203 |
psr_ppriv->missed_max = tmp;
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
return 0;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
struct lw_psr_port_priv *
|
|
Packit |
cac203 |
lw_psr_ppriv_get(struct lw_common_port_priv *common_ppriv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
return (struct lw_psr_port_priv *) common_ppriv;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
int lw_psr_port_added(struct teamd_context *ctx, struct teamd_port *tdport,
|
|
Packit |
cac203 |
void *priv, void *creator_priv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
struct lw_psr_port_priv *psr_ppriv = priv;
|
|
Packit |
cac203 |
int err;
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
err = lw_psr_load_options(ctx, tdport, psr_ppriv);
|
|
Packit |
cac203 |
if (err) {
|
|
Packit |
cac203 |
teamd_log_err("Failed to load options.");
|
|
Packit |
cac203 |
return err;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
err = psr_ppriv->ops->load_options(ctx, tdport, psr_ppriv);
|
|
Packit |
cac203 |
if (err) {
|
|
Packit |
cac203 |
teamd_log_err("Failed to load options.");
|
|
Packit |
cac203 |
return err;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
err = psr_ppriv->ops->sock_open(psr_ppriv);
|
|
Packit |
cac203 |
if (err) {
|
|
Packit |
cac203 |
teamd_log_err("Failed to create socket.");
|
|
Packit |
cac203 |
return err;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
err = teamd_loop_callback_fd_add(ctx, LW_SOCKET_CB_NAME, psr_ppriv,
|
|
Packit |
cac203 |
lw_psr_callback_socket,
|
|
Packit |
cac203 |
psr_ppriv->sock,
|
|
Packit |
cac203 |
TEAMD_LOOP_FD_EVENT_READ);
|
|
Packit |
cac203 |
if (err) {
|
|
Packit |
cac203 |
teamd_log_err("Failed add socket callback.");
|
|
Packit |
cac203 |
goto close_sock;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
err = teamd_loop_callback_timer_add_set(ctx, LW_PERIODIC_CB_NAME,
|
|
Packit |
cac203 |
psr_ppriv,
|
|
Packit |
cac203 |
lw_psr_callback_periodic,
|
|
Packit |
cac203 |
&psr_ppriv->interval,
|
|
Packit |
cac203 |
&psr_ppriv->init_wait);
|
|
Packit |
cac203 |
if (err) {
|
|
Packit |
cac203 |
teamd_log_err("Failed add callback timer");
|
|
Packit |
cac203 |
goto socket_callback_del;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
err = team_set_port_user_linkup_enabled(ctx->th, tdport->ifindex, true);
|
|
Packit |
cac203 |
if (err) {
|
|
Packit |
cac203 |
teamd_log_err("%s: Failed to enable user linkup.",
|
|
Packit |
cac203 |
tdport->ifname);
|
|
Packit |
cac203 |
goto periodic_callback_del;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
teamd_loop_callback_enable(ctx, LW_SOCKET_CB_NAME, psr_ppriv);
|
|
Packit |
cac203 |
teamd_loop_callback_enable(ctx, LW_PERIODIC_CB_NAME, psr_ppriv);
|
|
Packit |
cac203 |
return 0;
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
periodic_callback_del:
|
|
Packit |
cac203 |
teamd_loop_callback_del(ctx, LW_PERIODIC_CB_NAME, psr_ppriv);
|
|
Packit |
cac203 |
socket_callback_del:
|
|
Packit |
cac203 |
teamd_loop_callback_del(ctx, LW_SOCKET_CB_NAME, psr_ppriv);
|
|
Packit |
cac203 |
close_sock:
|
|
Packit |
cac203 |
psr_ppriv->ops->sock_close(psr_ppriv);
|
|
Packit |
cac203 |
return err;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
void lw_psr_port_removed(struct teamd_context *ctx, struct teamd_port *tdport,
|
|
Packit |
cac203 |
void *priv, void *creator_priv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
struct lw_psr_port_priv *psr_ppriv = priv;
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
teamd_loop_callback_del(ctx, LW_PERIODIC_CB_NAME, psr_ppriv);
|
|
Packit |
cac203 |
teamd_loop_callback_del(ctx, LW_SOCKET_CB_NAME, psr_ppriv);
|
|
Packit |
cac203 |
psr_ppriv->ops->sock_close(psr_ppriv);
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
int lw_psr_state_interval_get(struct teamd_context *ctx,
|
|
Packit |
cac203 |
struct team_state_gsc *gsc,
|
|
Packit |
cac203 |
void *priv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
struct lw_common_port_priv *common_ppriv = priv;
|
|
Packit |
cac203 |
struct lw_psr_port_priv *psr_ppriv = lw_psr_ppriv_get(common_ppriv);
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
gsc->data.int_val = timespec_to_ms(&psr_ppriv->interval);
|
|
Packit |
cac203 |
return 0;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
int lw_psr_state_init_wait_get(struct teamd_context *ctx,
|
|
Packit |
cac203 |
struct team_state_gsc *gsc,
|
|
Packit |
cac203 |
void *priv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
struct lw_common_port_priv *common_ppriv = priv;
|
|
Packit |
cac203 |
struct lw_psr_port_priv *psr_ppriv = lw_psr_ppriv_get(common_ppriv);
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
gsc->data.int_val = timespec_to_ms(&psr_ppriv->init_wait);
|
|
Packit |
cac203 |
return 0;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
int lw_psr_state_missed_max_get(struct teamd_context *ctx,
|
|
Packit |
cac203 |
struct team_state_gsc *gsc,
|
|
Packit |
cac203 |
void *priv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
struct lw_common_port_priv *common_ppriv = priv;
|
|
Packit |
cac203 |
struct lw_psr_port_priv *psr_ppriv = lw_psr_ppriv_get(common_ppriv);
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
gsc->data.int_val = psr_ppriv->missed_max;
|
|
Packit |
cac203 |
return 0;
|
|
Packit |
cac203 |
}
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
int lw_psr_state_missed_get(struct teamd_context *ctx,
|
|
Packit |
cac203 |
struct team_state_gsc *gsc,
|
|
Packit |
cac203 |
void *priv)
|
|
Packit |
cac203 |
{
|
|
Packit |
cac203 |
struct lw_common_port_priv *common_ppriv = priv;
|
|
Packit |
cac203 |
struct lw_psr_port_priv *psr_ppriv = lw_psr_ppriv_get(common_ppriv);
|
|
Packit |
cac203 |
|
|
Packit |
cac203 |
gsc->data.int_val = psr_ppriv->missed;
|
|
Packit |
cac203 |
return 0;
|
|
Packit |
cac203 |
}
|