mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 17:41:44 +00:00
e53665c6ea
Bonding in balance-alb mode records information from ARP packets passing through the bond in a hash table (rx_hashtbl). At certain situations (e.g. link change of a slave), rlb_update_rx_clients() will send out ARP packets to update ARP caches of other hosts on the network to achieve RX load balancing. The problem is that once an IP address is recorded in the hash table, it stays there indefinitely. If this IP address is migrated to a different host in the network, bonding still sends out ARP packets that poison other systems' ARP caches with invalid information. This patch solves this by looking at all incoming ARP packets, and checking if the source IP address is one of the source addresses stored in the rx_hashtbl. If it is, but the MAC addresses differ, the corresponding hash table entries are removed. Thus, when an IP address is migrated, the first ARP broadcast by its new owner will purge the offending entries of rx_hashtbl. The hash table is hashed by ip_dst. To be able to do the above check efficiently (not walking the whole hash table), we need a reverse mapping (by ip_src). I added three new members in struct rlb_client_info: rx_hashtbl[x].src_first will point to the start of a list of entries for which hash(ip_src) == x. The list is linked with src_next and src_prev. When an incoming ARP packet arrives at rlb_arp_recv() rlb_purge_src_ip() can quickly walk only the entries on the corresponding lists, i.e. the entries that are likely to contain the offending IP address. To avoid confusion, I renamed these existing fields of struct rlb_client_info: next -> used_next prev -> used_prev rx_hashtbl_head -> rx_hashtbl_used_head (The current linked list is _not_ a list of hash table entries with colliding ip_dst. It's a list of entries that are being used; its purpose is to avoid walking the whole hash table when looking for used entries.) Signed-off-by: Jiri Bohac <jbohac@suse.cz> Signed-off-by: Jay Vosburgh <fubar@us.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
148 lines
3.0 KiB
C
148 lines
3.0 KiB
C
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/netdevice.h>
|
|
|
|
#include "bonding.h"
|
|
#include "bond_alb.h"
|
|
|
|
#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_NET_NS)
|
|
|
|
#include <linux/debugfs.h>
|
|
#include <linux/seq_file.h>
|
|
|
|
static struct dentry *bonding_debug_root;
|
|
|
|
/*
|
|
* Show RLB hash table
|
|
*/
|
|
static int bond_debug_rlb_hash_show(struct seq_file *m, void *v)
|
|
{
|
|
struct bonding *bond = m->private;
|
|
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
|
struct rlb_client_info *client_info;
|
|
u32 hash_index;
|
|
|
|
if (bond->params.mode != BOND_MODE_ALB)
|
|
return 0;
|
|
|
|
seq_printf(m, "SourceIP DestinationIP "
|
|
"Destination MAC DEV\n");
|
|
|
|
spin_lock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
|
|
|
|
hash_index = bond_info->rx_hashtbl_used_head;
|
|
for (; hash_index != RLB_NULL_INDEX;
|
|
hash_index = client_info->used_next) {
|
|
client_info = &(bond_info->rx_hashtbl[hash_index]);
|
|
seq_printf(m, "%-15pI4 %-15pI4 %-17pM %s\n",
|
|
&client_info->ip_src,
|
|
&client_info->ip_dst,
|
|
&client_info->mac_dst,
|
|
client_info->slave->dev->name);
|
|
}
|
|
|
|
spin_unlock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bond_debug_rlb_hash_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, bond_debug_rlb_hash_show, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations bond_debug_rlb_hash_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = bond_debug_rlb_hash_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
void bond_debug_register(struct bonding *bond)
|
|
{
|
|
if (!bonding_debug_root)
|
|
return;
|
|
|
|
bond->debug_dir =
|
|
debugfs_create_dir(bond->dev->name, bonding_debug_root);
|
|
|
|
if (!bond->debug_dir) {
|
|
pr_warning("%s: Warning: failed to register to debugfs\n",
|
|
bond->dev->name);
|
|
return;
|
|
}
|
|
|
|
debugfs_create_file("rlb_hash_table", 0400, bond->debug_dir,
|
|
bond, &bond_debug_rlb_hash_fops);
|
|
}
|
|
|
|
void bond_debug_unregister(struct bonding *bond)
|
|
{
|
|
if (!bonding_debug_root)
|
|
return;
|
|
|
|
debugfs_remove_recursive(bond->debug_dir);
|
|
}
|
|
|
|
void bond_debug_reregister(struct bonding *bond)
|
|
{
|
|
struct dentry *d;
|
|
|
|
if (!bonding_debug_root)
|
|
return;
|
|
|
|
d = debugfs_rename(bonding_debug_root, bond->debug_dir,
|
|
bonding_debug_root, bond->dev->name);
|
|
if (d) {
|
|
bond->debug_dir = d;
|
|
} else {
|
|
pr_warning("%s: Warning: failed to reregister, "
|
|
"so just unregister old one\n",
|
|
bond->dev->name);
|
|
bond_debug_unregister(bond);
|
|
}
|
|
}
|
|
|
|
void bond_create_debugfs(void)
|
|
{
|
|
bonding_debug_root = debugfs_create_dir("bonding", NULL);
|
|
|
|
if (!bonding_debug_root) {
|
|
pr_warning("Warning: Cannot create bonding directory"
|
|
" in debugfs\n");
|
|
}
|
|
}
|
|
|
|
void bond_destroy_debugfs(void)
|
|
{
|
|
debugfs_remove_recursive(bonding_debug_root);
|
|
bonding_debug_root = NULL;
|
|
}
|
|
|
|
|
|
#else /* !CONFIG_DEBUG_FS */
|
|
|
|
void bond_debug_register(struct bonding *bond)
|
|
{
|
|
}
|
|
|
|
void bond_debug_unregister(struct bonding *bond)
|
|
{
|
|
}
|
|
|
|
void bond_debug_reregister(struct bonding *bond)
|
|
{
|
|
}
|
|
|
|
void bond_create_debugfs(void)
|
|
{
|
|
}
|
|
|
|
void bond_destroy_debugfs(void)
|
|
{
|
|
}
|
|
|
|
#endif /* CONFIG_DEBUG_FS */
|