blob: a5814728440c1c83fb91df1088308d7f0dc0fc7c [file] [log] [blame]
/*
* teamd_runner_loadbalance.c - Load-balancing runners
* Copyright (C) 2012-2015 Jiri Pirko <jiri@resnulli.us>
*
* 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 <sys/socket.h>
#include <linux/netdevice.h>
#include <team.h>
#include "teamd.h"
struct lb {
struct teamd_balancer *tb;
};
static int lb_event_watch_port_added(struct teamd_context *ctx,
struct teamd_port *tdport, void *priv)
{
struct lb *lb = priv;
int err;
err = team_hwaddr_set(ctx->th, tdport->ifindex, ctx->hwaddr,
ctx->hwaddr_len);
if (err) {
teamd_log_err("Failed to set port \"%s\" hardware address. ",
tdport->ifname);
return err;
}
if (!team_is_port_link_up(tdport->team_port)) {
err = team_set_port_enabled(ctx->th, tdport->ifindex, false);
if (err) {
teamd_log_err("%s: Failed to disable port.",
tdport->ifname);
return TEAMD_ENOENT(err) ? 0 : err;
}
}
return teamd_balancer_port_added(lb->tb, tdport);
}
static void lb_event_watch_port_removed(struct teamd_context *ctx,
struct teamd_port *tdport, void *priv)
{
struct lb *lb = priv;
teamd_balancer_port_removed(lb->tb, tdport);
}
static int lb_event_watch_port_link_changed(struct teamd_context *ctx,
struct teamd_port *tdport,
void *priv)
{
bool port_up = teamd_link_watch_port_up(ctx, tdport);
return teamd_port_check_enable(ctx, tdport, port_up, !port_up);
}
static int lb_event_watch_hwaddr_changed(struct teamd_context *ctx, void *priv)
{
struct teamd_port *tdport;
int err;
teamd_for_each_tdport(tdport, ctx) {
err = team_hwaddr_set(ctx->th, tdport->ifindex, ctx->hwaddr,
ctx->hwaddr_len);
if (err) {
teamd_log_err("%s: Failed to set port hardware address.",
tdport->ifname);
return err;
}
}
return 0;
}
static int lb_event_watch_port_hwaddr_changed(struct teamd_context *ctx,
struct teamd_port *tdport,
void *priv)
{
int err;
if (!teamd_port_present(ctx, tdport))
return 0;
if (!memcmp(team_get_ifinfo_hwaddr(tdport->team_ifinfo),
ctx->hwaddr, ctx->hwaddr_len))
return 0;
err = team_hwaddr_set(ctx->th, tdport->ifindex, ctx->hwaddr,
ctx->hwaddr_len);
if (err)
teamd_log_err("%s: Failed to set port hardware address.",
tdport->ifname);
return err;
}
static int lb_event_watch_enabled_option_changed(struct teamd_context *ctx,
struct team_option *option,
void *priv)
{
struct teamd_port *tdport;
tdport = teamd_get_port(ctx, team_get_option_port_ifindex(option));
if (!tdport)
return 0;
return lb_event_watch_port_link_changed(ctx, tdport, priv);
}
static const struct teamd_event_watch_ops lb_port_watch_ops = {
.hwaddr_changed = lb_event_watch_hwaddr_changed,
.port_hwaddr_changed = lb_event_watch_port_hwaddr_changed,
.port_added = lb_event_watch_port_added,
.port_removed = lb_event_watch_port_removed,
.port_link_changed = lb_event_watch_port_link_changed,
.option_changed = lb_event_watch_enabled_option_changed,
.option_changed_match_name = "enabled",
};
static int lb_init(struct teamd_context *ctx, void *priv)
{
struct lb *lb = priv;
int err;
err = teamd_hash_func_set(ctx);
if (err)
return err;
err = teamd_event_watch_register(ctx, &lb_port_watch_ops, lb);
if (err) {
teamd_log_err("Failed to register event watch.");
return err;
}
err = teamd_balancer_init(ctx, &lb->tb);
if (err) {
teamd_log_err("Failed to init balanced.");
goto event_watch_unregister;
}
return 0;
event_watch_unregister:
teamd_event_watch_unregister(ctx, &lb_port_watch_ops, lb);
return err;
}
static void lb_fini(struct teamd_context *ctx, void *priv)
{
struct lb *lb = priv;
teamd_balancer_fini(lb->tb);
teamd_event_watch_unregister(ctx, &lb_port_watch_ops, lb);
}
const struct teamd_runner teamd_runner_loadbalance = {
.name = "loadbalance",
.team_mode_name = "loadbalance",
.init = lb_init,
.fini = lb_fini,
.priv_size = sizeof(struct lb),
};