diff options
Diffstat (limited to 'drivers/net/bonding/bond_procfs.c')
-rw-r--r-- | drivers/net/bonding/bond_procfs.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c new file mode 100644 index 000000000000..c32ff55a34c1 --- /dev/null +++ b/drivers/net/bonding/bond_procfs.c @@ -0,0 +1,275 @@ +#include <linux/proc_fs.h> +#include <net/net_namespace.h> +#include <net/netns/generic.h> +#include "bonding.h" + + +extern const char *bond_mode_name(int mode); + +static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(RCU) + __acquires(&bond->lock) +{ + struct bonding *bond = seq->private; + loff_t off = 0; + struct slave *slave; + int i; + + /* make sure the bond won't be taken away */ + rcu_read_lock(); + read_lock(&bond->lock); + + if (*pos == 0) + return SEQ_START_TOKEN; + + bond_for_each_slave(bond, slave, i) { + if (++off == *pos) + return slave; + } + + return NULL; +} + +static void *bond_info_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct bonding *bond = seq->private; + struct slave *slave = v; + + ++*pos; + if (v == SEQ_START_TOKEN) + return bond->first_slave; + + slave = slave->next; + + return (slave == bond->first_slave) ? NULL : slave; +} + +static void bond_info_seq_stop(struct seq_file *seq, void *v) + __releases(&bond->lock) + __releases(RCU) +{ + struct bonding *bond = seq->private; + + read_unlock(&bond->lock); + rcu_read_unlock(); +} + +static void bond_info_show_master(struct seq_file *seq) +{ + struct bonding *bond = seq->private; + struct slave *curr; + int i; + + read_lock(&bond->curr_slave_lock); + curr = bond->curr_active_slave; + read_unlock(&bond->curr_slave_lock); + + seq_printf(seq, "Bonding Mode: %s", + bond_mode_name(bond->params.mode)); + + if (bond->params.mode == BOND_MODE_ACTIVEBACKUP && + bond->params.fail_over_mac) + seq_printf(seq, " (fail_over_mac %s)", + fail_over_mac_tbl[bond->params.fail_over_mac].modename); + + seq_printf(seq, "\n"); + + if (bond->params.mode == BOND_MODE_XOR || + bond->params.mode == BOND_MODE_8023AD) { + seq_printf(seq, "Transmit Hash Policy: %s (%d)\n", + xmit_hashtype_tbl[bond->params.xmit_policy].modename, + bond->params.xmit_policy); + } + + if (USES_PRIMARY(bond->params.mode)) { + seq_printf(seq, "Primary Slave: %s", + (bond->primary_slave) ? + bond->primary_slave->dev->name : "None"); + if (bond->primary_slave) + seq_printf(seq, " (primary_reselect %s)", + pri_reselect_tbl[bond->params.primary_reselect].modename); + + seq_printf(seq, "\nCurrently Active Slave: %s\n", + (curr) ? curr->dev->name : "None"); + } + + seq_printf(seq, "MII Status: %s\n", netif_carrier_ok(bond->dev) ? + "up" : "down"); + seq_printf(seq, "MII Polling Interval (ms): %d\n", bond->params.miimon); + seq_printf(seq, "Up Delay (ms): %d\n", + bond->params.updelay * bond->params.miimon); + seq_printf(seq, "Down Delay (ms): %d\n", + bond->params.downdelay * bond->params.miimon); + + + /* ARP information */ + if (bond->params.arp_interval > 0) { + int printed = 0; + seq_printf(seq, "ARP Polling Interval (ms): %d\n", + bond->params.arp_interval); + + seq_printf(seq, "ARP IP target/s (n.n.n.n form):"); + + for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) { + if (!bond->params.arp_targets[i]) + break; + if (printed) + seq_printf(seq, ","); + seq_printf(seq, " %pI4", &bond->params.arp_targets[i]); + printed = 1; + } + seq_printf(seq, "\n"); + } + + if (bond->params.mode == BOND_MODE_8023AD) { + struct ad_info ad_info; + + seq_puts(seq, "\n802.3ad info\n"); + seq_printf(seq, "LACP rate: %s\n", + (bond->params.lacp_fast) ? "fast" : "slow"); + seq_printf(seq, "Aggregator selection policy (ad_select): %s\n", + ad_select_tbl[bond->params.ad_select].modename); + + if (bond_3ad_get_active_agg_info(bond, &ad_info)) { + seq_printf(seq, "bond %s has no active aggregator\n", + bond->dev->name); + } else { + seq_printf(seq, "Active Aggregator Info:\n"); + + seq_printf(seq, "\tAggregator ID: %d\n", + ad_info.aggregator_id); + seq_printf(seq, "\tNumber of ports: %d\n", + ad_info.ports); + seq_printf(seq, "\tActor Key: %d\n", + ad_info.actor_key); + seq_printf(seq, "\tPartner Key: %d\n", + ad_info.partner_key); + seq_printf(seq, "\tPartner Mac Address: %pM\n", + ad_info.partner_system); + } + } +} + +static void bond_info_show_slave(struct seq_file *seq, + const struct slave *slave) +{ + struct bonding *bond = seq->private; + + seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name); + seq_printf(seq, "MII Status: %s\n", + (slave->link == BOND_LINK_UP) ? "up" : "down"); + seq_printf(seq, "Speed: %d Mbps\n", slave->speed); + seq_printf(seq, "Duplex: %s\n", slave->duplex ? "full" : "half"); + seq_printf(seq, "Link Failure Count: %u\n", + slave->link_failure_count); + + seq_printf(seq, "Permanent HW addr: %pM\n", slave->perm_hwaddr); + + if (bond->params.mode == BOND_MODE_8023AD) { + const struct aggregator *agg + = SLAVE_AD_INFO(slave).port.aggregator; + + if (agg) + seq_printf(seq, "Aggregator ID: %d\n", + agg->aggregator_identifier); + else + seq_puts(seq, "Aggregator ID: N/A\n"); + } + seq_printf(seq, "Slave queue ID: %d\n", slave->queue_id); +} + +static int bond_info_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) { + seq_printf(seq, "%s\n", bond_version); + bond_info_show_master(seq); + } else + bond_info_show_slave(seq, v); + + return 0; +} + +static const struct seq_operations bond_info_seq_ops = { + .start = bond_info_seq_start, + .next = bond_info_seq_next, + .stop = bond_info_seq_stop, + .show = bond_info_seq_show, +}; + +static int bond_info_open(struct inode *inode, struct file *file) +{ + struct seq_file *seq; + struct proc_dir_entry *proc; + int res; + + res = seq_open(file, &bond_info_seq_ops); + if (!res) { + /* recover the pointer buried in proc_dir_entry data */ + seq = file->private_data; + proc = PDE(inode); + seq->private = proc->data; + } + + return res; +} + +static const struct file_operations bond_info_fops = { + .owner = THIS_MODULE, + .open = bond_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +void bond_create_proc_entry(struct bonding *bond) +{ + struct net_device *bond_dev = bond->dev; + struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id); + + if (bn->proc_dir) { + bond->proc_entry = proc_create_data(bond_dev->name, + S_IRUGO, bn->proc_dir, + &bond_info_fops, bond); + if (bond->proc_entry == NULL) + pr_warning("Warning: Cannot create /proc/net/%s/%s\n", + DRV_NAME, bond_dev->name); + else + memcpy(bond->proc_file_name, bond_dev->name, IFNAMSIZ); + } +} + +void bond_remove_proc_entry(struct bonding *bond) +{ + struct net_device *bond_dev = bond->dev; + struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id); + + if (bn->proc_dir && bond->proc_entry) { + remove_proc_entry(bond->proc_file_name, bn->proc_dir); + memset(bond->proc_file_name, 0, IFNAMSIZ); + bond->proc_entry = NULL; + } +} + +/* Create the bonding directory under /proc/net, if doesn't exist yet. + * Caller must hold rtnl_lock. + */ +void __net_init bond_create_proc_dir(struct bond_net *bn) +{ + if (!bn->proc_dir) { + bn->proc_dir = proc_mkdir(DRV_NAME, bn->net->proc_net); + if (!bn->proc_dir) + pr_warning("Warning: cannot create /proc/net/%s\n", + DRV_NAME); + } +} + +/* Destroy the bonding directory under /proc/net, if empty. + * Caller must hold rtnl_lock. + */ +void __net_exit bond_destroy_proc_dir(struct bond_net *bn) +{ + if (bn->proc_dir) { + remove_proc_entry(DRV_NAME, bn->net->proc_net); + bn->proc_dir = NULL; + } +} |