/* Searches for an option by name */ conststruct bond_option *bond_opt_get_by_name(constchar *name)
{ conststruct bond_option *opt; int option;
for (option = 0; option < BOND_OPT_LAST; option++) {
opt = bond_opt_get(option); if (opt && !strcmp(opt->name, name)) return opt;
}
return NULL;
}
/* Searches for a value in opt's values[] table */ conststruct bond_opt_value *bond_opt_get_val(unsignedint option, u64 val)
{ conststruct bond_option *opt; int i;
opt = bond_opt_get(option); if (WARN_ON(!opt)) return NULL; for (i = 0; opt->values && opt->values[i].string; i++) if (opt->values[i].value == val) return &opt->values[i];
return NULL;
}
/* Searches for a value in opt's values[] table which matches the flagmask */ staticconststruct bond_opt_value *bond_opt_get_flags(conststruct bond_option *opt,
u32 flagmask)
{ int i;
for (i = 0; opt->values && opt->values[i].string; i++) if (opt->values[i].flags & flagmask) return &opt->values[i];
return NULL;
}
/* If maxval is missing then there's no range to check. In case minval is * missing then it's considered to be 0.
*/ staticbool bond_opt_check_range(conststruct bond_option *opt, u64 val)
{ conststruct bond_opt_value *minval, *maxval;
minval = bond_opt_get_flags(opt, BOND_VALFLAG_MIN);
maxval = bond_opt_get_flags(opt, BOND_VALFLAG_MAX); if (!maxval || (minval && val < minval->value) || val > maxval->value) returnfalse;
returntrue;
}
/** * bond_opt_parse - parse option value * @opt: the option to parse against * @val: value to parse * * This function tries to extract the value from @val and check if it's * a possible match for the option and returns NULL if a match isn't found, * or the struct_opt_value that matched. It also strips the new line from * @val->string if it's present.
*/ conststruct bond_opt_value *bond_opt_parse(conststruct bond_option *opt, struct bond_opt_value *val)
{ char *p, valstr[BOND_OPT_MAX_NAMELEN + 1] = { 0, }; conststruct bond_opt_value *tbl; conststruct bond_opt_value *ret = NULL; bool checkval; int i, rv;
/* No parsing if the option wants a raw val */ if (opt->flags & BOND_OPTFLAG_RAWVAL) return val;
tbl = opt->values; if (!tbl) goto out;
/* ULLONG_MAX is used to bypass string processing */
checkval = val->value != ULLONG_MAX; if (!checkval) { if (!val->string) goto out;
p = strchr(val->string, '\n'); if (p)
*p = '\0'; for (p = val->string; *p; p++) if (!(isdigit(*p) || isspace(*p))) break; /* The following code extracts the string to match or the value * and sets checkval appropriately
*/ if (*p) {
rv = sscanf(val->string, "%32s", valstr);
} else {
rv = sscanf(val->string, "%llu", &val->value);
checkval = true;
} if (!rv) goto out;
}
for (i = 0; tbl[i].string; i++) { /* Check for exact match */ if (checkval) { if (val->value == tbl[i].value)
ret = &tbl[i];
} else { if (!strcmp(valstr, "default") &&
(tbl[i].flags & BOND_VALFLAG_DEFAULT))
ret = &tbl[i];
if (!strcmp(valstr, tbl[i].string))
ret = &tbl[i];
} /* Found an exact match */ if (ret) goto out;
} /* Possible range match */ if (checkval && bond_opt_check_range(opt, val->value))
ret = val;
out: return ret;
}
/* Check opt's dependencies against bond mode and currently set options */ staticint bond_opt_check_deps(struct bonding *bond, conststruct bond_option *opt)
{ struct bond_params *params = &bond->params;
if (test_bit(params->mode, &opt->unsuppmodes)) return -EACCES; if ((opt->flags & BOND_OPTFLAG_NOSLAVES) && bond_has_slaves(bond)) return -ENOTEMPTY; if ((opt->flags & BOND_OPTFLAG_IFDOWN) && (bond->dev->flags & IFF_UP)) return -EBUSY;
switch (error) { case -EINVAL:
NL_SET_ERR_MSG_ATTR(extack, bad_attr, "invalid option value"); if (val) { if (val->string) { /* sometimes RAWVAL opts may have new lines */
p = strchr(val->string, '\n'); if (p)
*p = '\0';
netdev_err(bond->dev, "option %s: invalid value (%s)\n",
opt->name, val->string);
} else {
netdev_err(bond->dev, "option %s: invalid value (%llu)\n",
opt->name, val->value);
}
}
minval = bond_opt_get_flags(opt, BOND_VALFLAG_MIN);
maxval = bond_opt_get_flags(opt, BOND_VALFLAG_MAX); if (!maxval) break;
netdev_err(bond->dev, "option %s: allowed values %llu - %llu\n",
opt->name, minval ? minval->value : 0, maxval->value); break; case -EACCES:
bond_opt_dep_print(bond, opt, bad_attr, extack); break; case -ENOTEMPTY:
NL_SET_ERR_MSG_ATTR(extack, bad_attr, "unable to set option because the bond device has slaves");
netdev_err(bond->dev, "option %s: unable to set because the bond device has slaves\n",
opt->name); break; case -EBUSY:
NL_SET_ERR_MSG_ATTR(extack, bad_attr, "unable to set option because the bond is up");
netdev_err(bond->dev, "option %s: unable to set because the bond device is up\n",
opt->name); break; case -ENODEV: if (val && val->string) {
p = strchr(val->string, '\n'); if (p)
*p = '\0';
netdev_err(bond->dev, "option %s: interface %s does not exist!\n",
opt->name, val->string);
NL_SET_ERR_MSG_ATTR(extack, bad_attr, "interface does not exist");
} break; default: break;
}
}
/** * __bond_opt_set - set a bonding option * @bond: target bond device * @option: option to set * @val: value to set it to * @bad_attr: netlink attribue that caused the error * @extack: extended netlink error structure, used when an error message * needs to be returned to the caller via netlink * * This function is used to change the bond's option value, it can be * used for both enabling/changing an option and for disabling it. RTNL lock * must be obtained before calling this function.
*/ int __bond_opt_set(struct bonding *bond, unsignedint option, struct bond_opt_value *val, struct nlattr *bad_attr, struct netlink_ext_ack *extack)
{ conststruct bond_opt_value *retval = NULL; conststruct bond_option *opt; int ret = -ENOENT;
ASSERT_RTNL();
opt = bond_opt_get(option); if (WARN_ON(!val) || WARN_ON(!opt)) goto out;
ret = bond_opt_check_deps(bond, opt); if (ret) goto out;
retval = bond_opt_parse(opt, val); if (!retval) {
ret = -EINVAL; goto out;
}
ret = opt->set(bond, retval);
out: if (ret)
bond_opt_error_interpret(bond, opt, ret, val, bad_attr, extack);
return ret;
} /** * __bond_opt_set_notify - set a bonding option * @bond: target bond device * @option: option to set * @val: value to set it to * * This function is used to change the bond's option value and trigger * a notification to user sapce. It can be used for both enabling/changing * an option and for disabling it. RTNL lock must be obtained before calling * this function.
*/ int __bond_opt_set_notify(struct bonding *bond, unsignedint option, struct bond_opt_value *val)
{ int ret;
ASSERT_RTNL();
ret = __bond_opt_set(bond, option, val, NULL, NULL);
if (!ret && (bond->dev->reg_state == NETREG_REGISTERED))
call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
return ret;
}
/** * bond_opt_tryset_rtnl - try to acquire rtnl and call __bond_opt_set * @bond: target bond device * @option: option to set * @buf: value to set it to * * This function tries to acquire RTNL without blocking and if successful * calls __bond_opt_set. It is mainly used for sysfs option manipulation.
*/ int bond_opt_tryset_rtnl(struct bonding *bond, unsignedint option, char *buf)
{ struct bond_opt_value optval; int ret;
if (!rtnl_trylock()) return restart_syscall();
bond_opt_initstr(&optval, buf);
ret = __bond_opt_set_notify(bond, option, &optval);
rtnl_unlock();
return ret;
}
/** * bond_opt_get - get a pointer to an option * @option: option for which to return a pointer * * This function checks if option is valid and if so returns a pointer * to its entry in the bond_opts[] option array.
*/ conststruct bond_option *bond_opt_get(unsignedint option)
{ if (!BOND_OPT_VALID(option)) return NULL;
return &bond_opts[option];
}
staticbool bond_set_xfrm_features(struct bonding *bond)
{ if (!IS_ENABLED(CONFIG_XFRM_OFFLOAD)) returnfalse;
if (!bond_mode_uses_arp(newval->value)) { if (bond->params.arp_interval) {
netdev_dbg(bond->dev, "%s mode is incompatible with arp monitoring, start mii monitoring\n",
newval->string); /* disable arp monitoring */
bond->params.arp_interval = 0;
}
if (!bond->params.miimon) { /* set miimon to default value */
bond->params.miimon = BOND_DEFAULT_MIIMON;
netdev_dbg(bond->dev, "Setting MII monitoring interval to %d\n",
bond->params.miimon);
}
}
if (newval->value == BOND_MODE_ALB)
bond->params.tlb_dynamic_lb = 1;
/* When changing mode, the bond device is down, we may reduce * the bond_bcast_neigh_enabled in bond_close() if broadcast_neighbor * enabled in 8023ad mode. Therefore, only clear broadcast_neighbor * to 0.
*/
bond->params.broadcast_neighbor = 0;
if (bond->dev->reg_state == NETREG_REGISTERED) { bool update = false;
if (slave_dev) { if (!netif_is_bond_slave(slave_dev)) {
slave_err(bond->dev, slave_dev, "Device is not bonding slave\n"); return -EINVAL;
}
if (bond->dev != netdev_master_upper_dev_get(slave_dev)) {
slave_err(bond->dev, slave_dev, "Device is not our slave\n"); return -EINVAL;
}
}
block_netpoll_tx(); /* check to see if we are clearing active */ if (!slave_dev) {
netdev_dbg(bond->dev, "Clearing current active slave\n");
bond_change_active_slave(bond, NULL);
bond_select_active_slave(bond);
} else { struct slave *old_active = rtnl_dereference(bond->curr_active_slave); struct slave *new_active = bond_slave_get_rtnl(slave_dev);
BUG_ON(!new_active);
if (new_active == old_active) { /* do nothing */
slave_dbg(bond->dev, new_active->dev, "is already the current active slave\n");
} else { if (old_active && (new_active->link == BOND_LINK_UP) &&
bond_slave_is_up(new_active)) {
slave_dbg(bond->dev, new_active->dev, "Setting as active slave\n");
bond_change_active_slave(bond, new_active);
} else {
slave_err(bond->dev, new_active->dev, "Could not set as active slave; either %s is down or the link is down\n",
new_active->dev->name);
ret = -EINVAL;
}
}
}
unblock_netpoll_tx();
return ret;
}
/* There are two tricky bits here. First, if MII monitoring is activated, then * we must disable ARP monitoring. Second, if the timer isn't running, we must * start it.
*/ staticint bond_option_miimon_set(struct bonding *bond, conststruct bond_opt_value *newval)
{
netdev_dbg(bond->dev, "Setting MII monitoring interval to %llu\n",
newval->value);
bond->params.miimon = newval->value; if (bond->params.updelay)
netdev_dbg(bond->dev, "Note: Updating updelay (to %d) since it is a multiple of the miimon value\n",
bond->params.updelay * bond->params.miimon); if (bond->params.downdelay)
netdev_dbg(bond->dev, "Note: Updating downdelay (to %d) since it is a multiple of the miimon value\n",
bond->params.downdelay * bond->params.miimon); if (bond->params.peer_notif_delay)
netdev_dbg(bond->dev, "Note: Updating peer_notif_delay (to %d) since it is a multiple of the miimon value\n",
bond->params.peer_notif_delay * bond->params.miimon); if (newval->value && bond->params.arp_interval) {
netdev_dbg(bond->dev, "MII monitoring cannot be used with ARP monitoring - disabling ARP monitoring...\n");
bond->params.arp_interval = 0; if (bond->params.arp_validate)
bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
} if (bond->dev->flags & IFF_UP) { /* If the interface is up, we may need to fire off * the MII timer. If the interface is down, the * timer will get fired off when the open function * is called.
*/ if (!newval->value) {
cancel_delayed_work_sync(&bond->mii_work);
} else {
cancel_delayed_work_sync(&bond->arp_work);
queue_delayed_work(bond->wq, &bond->mii_work, 0);
}
}
return 0;
}
/* Set up, down and peer notification delays. These must be multiples * of the MII monitoring value, and are stored internally as the * multiplier. Thus, we must translate to MS for the real world.
*/ staticint _bond_option_delay_set(struct bonding *bond, conststruct bond_opt_value *newval, constchar *name, int *target)
{ int value = newval->value;
if (!bond->params.miimon) {
netdev_err(bond->dev, "Unable to set %s as MII monitoring is disabled\n",
name); return -EPERM;
} if ((value % bond->params.miimon) != 0) {
netdev_warn(bond->dev, "%s (%d) is not a multiple of miimon (%d), value rounded to %d ms\n",
name,
value, bond->params.miimon,
(value / bond->params.miimon) *
bond->params.miimon);
}
*target = value / bond->params.miimon;
netdev_dbg(bond->dev, "Setting %s to %d\n",
name,
*target * bond->params.miimon);
/* There are two tricky bits here. First, if ARP monitoring is activated, then * we must disable MII monitoring. Second, if the ARP timer isn't running, * we must start it.
*/ staticint bond_option_arp_interval_set(struct bonding *bond, conststruct bond_opt_value *newval)
{
netdev_dbg(bond->dev, "Setting ARP monitoring interval to %llu\n",
newval->value);
bond->params.arp_interval = newval->value; if (newval->value) { if (bond->params.miimon) {
netdev_dbg(bond->dev, "ARP monitoring cannot be used with MII monitoring. Disabling MII monitoring\n");
bond->params.miimon = 0;
} if (!bond->params.arp_targets[0])
netdev_dbg(bond->dev, "ARP monitoring has been set up, but no ARP targets have been specified\n");
} if (bond->dev->flags & IFF_UP) { /* If the interface is up, we may need to fire off * the ARP timer. If the interface is down, the * timer will get fired off when the open function * is called.
*/ if (!newval->value) { if (bond->params.arp_validate)
bond->recv_probe = NULL;
cancel_delayed_work_sync(&bond->arp_work);
} else { /* arp_validate can be set only in active-backup mode */
bond->recv_probe = bond_rcv_validate;
cancel_delayed_work_sync(&bond->mii_work);
queue_delayed_work(bond->wq, &bond->arp_work, 0);
}
}
/** * slave_set_ns_maddrs - add/del all NS mac addresses for slave * @bond: bond device * @slave: slave device * @add: add or remove all the NS mac addresses * * This function tries to add or delete all the NS mac addresses on the slave * * Note, the IPv6 NS target address is the unicast address in Neighbor * Solicitation (NS) message. The dest address of NS message should be * solicited-node multicast address of the target. The dest mac of NS message * is converted from the solicited-node multicast address. * * This function is called when * * arp_validate changes * * enslaving, releasing new slaves
*/ staticvoid slave_set_ns_maddrs(struct bonding *bond, struct slave *slave, bool add)
{ struct in6_addr *targets = bond->params.ns_targets; char slot_maddr[MAX_ADDR_LEN]; struct in6_addr mcaddr; int i;
if (!slave_can_set_ns_maddr(bond, slave)) return;
for (i = 0; i < BOND_MAX_NS_TARGETS; i++) { if (ipv6_addr_any(&targets[i])) break;
addrconf_addr_solict_mult(&targets[i], &mcaddr); if (!ndisc_mc_map(&mcaddr, slot_maddr, slave->dev, 0)) { if (add)
dev_mc_add(slave->dev, slot_maddr); else
dev_mc_del(slave->dev, slot_maddr);
}
}
}
/** * slave_set_ns_maddr - set new NS mac address for slave * @bond: bond device * @slave: slave device * @target: the new IPv6 target * @slot: the old IPv6 target in the slot * * This function tries to replace the old mac address to new one on the slave. * * Note, the target/slot IPv6 address is the unicast address in Neighbor * Solicitation (NS) message. The dest address of NS message should be * solicited-node multicast address of the target. The dest mac of NS message * is converted from the solicited-node multicast address. * * This function is called when * * An IPv6 NS target is added or removed.
*/ staticvoid slave_set_ns_maddr(struct bonding *bond, struct slave *slave, struct in6_addr *target, struct in6_addr *slot)
{ char mac_addr[MAX_ADDR_LEN]; struct in6_addr mcast_addr;
if (!bond->params.arp_validate || !slave_can_set_ns_maddr(bond, slave)) return;
/* remove the previous mac addr from slave */
addrconf_addr_solict_mult(slot, &mcast_addr); if (!ipv6_addr_any(slot) &&
!ndisc_mc_map(&mcast_addr, mac_addr, slave->dev, 0))
dev_mc_del(slave->dev, mac_addr);
/* add new mac addr on slave if target is set */
addrconf_addr_solict_mult(target, &mcast_addr); if (!ipv6_addr_any(target) &&
!ndisc_mc_map(&mcast_addr, mac_addr, slave->dev, 0))
dev_mc_add(slave->dev, mac_addr);
}
slave = bond_slave_get_rtnl(newval->slave_dev); if (!slave) {
netdev_dbg(newval->slave_dev, "%s called on NULL slave\n", __func__); return -ENODEV;
}
slave->prio = newval->value;
if (rtnl_dereference(bond->primary_slave))
slave_warn(bond->dev, slave->dev, "prio updated, but will not affect failover re-selection as primary slave have been set\n"); else
bond_select_active_slave(bond);
p = strchr(primary, '\n'); if (p)
*p = '\0'; /* check to see if we are clearing primary */ if (!strlen(primary)) {
netdev_dbg(bond->dev, "Setting primary slave to None\n");
RCU_INIT_POINTER(bond->primary_slave, NULL);
memset(bond->params.primary, 0, sizeof(bond->params.primary));
bond_select_active_slave(bond); goto out;
}
/* delim will point to queue id if successful */
delim = strchr(newval->string, ':'); if (!delim) goto err_no_cmd;
/* Terminate string that points to device name and bump it * up one, so we can read the queue id there.
*/
*delim = '\0'; if (sscanf(++delim, "%hd\n", &qid) != 1) goto err_no_cmd;
/* Check buffer length, valid ifname and queue id */ if (!dev_valid_name(newval->string) ||
qid > bond->dev->real_num_tx_queues) goto err_no_cmd;
/* Get the pointer to that interface if it exists */
sdev = __dev_get_by_name(dev_net(bond->dev), newval->string); if (!sdev) goto err_no_cmd;
/* Search for thes slave and check for duplicate qids */
update_slave = NULL;
bond_for_each_slave(bond, slave, iter) { if (sdev == slave->dev) /* We don't need to check the matching * slave for dups, since we're overwriting it
*/
update_slave = slave; elseif (qid && qid == slave->queue_id) { goto err_no_cmd;
}
}
if (!update_slave) goto err_no_cmd;
/* Actually set the qids for the slave */
WRITE_ONCE(update_slave->queue_id, qid);
out: return ret;
err_no_cmd:
netdev_dbg(bond->dev, "invalid input for queue_id set\n");
ret = -EPERM; goto out;
dev = __dev_get_by_name(dev_net(bond->dev), ifname); if (!dev) {
netdev_dbg(bond->dev, "interface %s does not exist!\n",
ifname);
ret = -ENODEV; goto out;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.