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