cregit-Linux how code gets into the kernel

Release 4.11 net/bridge/br_if.c

Directory: net/bridge
/*
 *      Userspace interface
 *      Linux ethernet bridge
 *
 *      Authors:
 *      Lennert Buytenhek               <buytenh@gnu.org>
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */

#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/netpoll.h>
#include <linux/ethtool.h>
#include <linux/if_arp.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtnetlink.h>
#include <linux/if_ether.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <linux/if_vlan.h>
#include <net/switchdev.h>

#include "br_private.h"

/*
 * Determine initial path cost based on speed.
 * using recommendations from 802.1d standard
 *
 * Since driver might sleep need to not be holding any locks.
 */

static int port_cost(struct net_device *dev) { struct ethtool_link_ksettings ecmd; if (!__ethtool_get_link_ksettings(dev, &ecmd)) { switch (ecmd.base.speed) { case SPEED_10000: return 2; case SPEED_1000: return 4; case SPEED_100: return 19; case SPEED_10: return 100; } } /* Old silly heuristics based on name */ if (!strncmp(dev->name, "lec", 3)) return 7; if (!strncmp(dev->name, "plip", 4)) return 2500; return 100; /* assume old 10Mbps */ }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger4847.52%350.00%
Linus Torvalds (pre-git)4544.55%116.67%
David Decotigny65.94%116.67%
Matthew Wilcox21.98%116.67%
Total101100.00%6100.00%

/* Check for port carrier transitions. */
void br_port_carrier_check(struct net_bridge_port *p) { struct net_device *dev = p->dev; struct net_bridge *br = p->br; if (!(p->flags & BR_ADMIN_COST) && netif_running(dev) && netif_oper_up(dev)) p->path_cost = port_cost(dev); if (!netif_running(br->dev)) return; spin_lock_bh(&br->lock); if (netif_running(dev) && netif_oper_up(dev)) { if (p->state == BR_STATE_DISABLED) br_stp_enable_port(p); } else { if (p->state != BR_STATE_DISABLED) br_stp_disable_port(p); } spin_unlock_bh(&br->lock); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger12396.09%787.50%
David Howells53.91%112.50%
Total128100.00%8100.00%


static void br_port_set_promisc(struct net_bridge_port *p) { int err = 0; if (br_promisc_port(p)) return; err = dev_set_promiscuity(p->dev, 1); if (err) return; br_fdb_unsync_static(p->br, p); p->flags |= BR_PROMISC; }

Contributors

PersonTokensPropCommitsCommitProp
Vlad Yasevich55100.00%1100.00%
Total55100.00%1100.00%


static void br_port_clear_promisc(struct net_bridge_port *p) { int err; /* Check if the port is already non-promisc or if it doesn't * support UNICAST filtering. Without unicast filtering support * we'll end up re-enabling promisc mode anyway, so just check for * it here. */ if (!br_promisc_port(p) || !(p->dev->priv_flags & IFF_UNICAST_FLT)) return; /* Since we'll be clearing the promisc mode, program the port * first so that we don't have interruption in traffic. */ err = br_fdb_sync_static(p->br, p); if (err) return; dev_set_promiscuity(p->dev, -1); p->flags &= ~BR_PROMISC; }

Contributors

PersonTokensPropCommitsCommitProp
Vlad Yasevich69100.00%1100.00%
Total69100.00%1100.00%

/* When a port is added or removed or when certain port flags * change, this function is called to automatically manage * promiscuity setting of all the bridge ports. We are always called * under RTNL so can skip using rcu primitives. */
void br_manage_promisc(struct net_bridge *br) { struct net_bridge_port *p; bool set_all = false; /* If vlan filtering is disabled or bridge interface is placed * into promiscuous mode, place all ports in promiscuous mode. */ if ((br->dev->flags & IFF_PROMISC) || !br_vlan_enabled(br)) set_all = true; list_for_each_entry(p, &br->port_list, list) { if (set_all) { br_port_set_promisc(p); } else { /* If the number of auto-ports is <= 1, then all other * ports will have their output configuration * statically specified through fdbs. Since ingress * on the auto-port becomes forwarding/egress to other * ports and egress configuration is statically known, * we can say that ingress configuration of the * auto-port is also statically known. * This lets us disable promiscuous mode and write * this config to hw. */ if (br->auto_cnt == 0 || (br->auto_cnt == 1 && br_auto_port(p))) br_port_clear_promisc(p); else br_port_set_promisc(p); } } }

Contributors

PersonTokensPropCommitsCommitProp
Vlad Yasevich8989.00%150.00%
Toshiaki Makita1111.00%150.00%
Total100100.00%2100.00%


static void nbp_update_port_count(struct net_bridge *br) { struct net_bridge_port *p; u32 cnt = 0; list_for_each_entry(p, &br->port_list, list) { if (br_auto_port(p)) cnt++; } if (br->auto_cnt != cnt) { br->auto_cnt = cnt; br_manage_promisc(br); } }

Contributors

PersonTokensPropCommitsCommitProp
Vlad Yasevich62100.00%2100.00%
Total62100.00%2100.00%


static void nbp_delete_promisc(struct net_bridge_port *p) { /* If port is currently promiscuous, unset promiscuity. * Otherwise, it is a static port so remove all addresses * from it. */ dev_set_allmulti(p->dev, -1); if (br_promisc_port(p)) dev_set_promiscuity(p->dev, -1); else br_fdb_unsync_static(p->br, p); }

Contributors

PersonTokensPropCommitsCommitProp
Vlad Yasevich4897.96%266.67%
Stephen Hemminger12.04%133.33%
Total49100.00%3100.00%


static void release_nbp(struct kobject *kobj) { struct net_bridge_port *p = container_of(kobj, struct net_bridge_port, kobj); kfree(p); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger31100.00%1100.00%
Total31100.00%1100.00%

static struct kobj_type brport_ktype = { #ifdef CONFIG_SYSFS .sysfs_ops = &brport_sysfs_ops, #endif .release = release_nbp, };
static void destroy_nbp(struct net_bridge_port *p) { struct net_device *dev = p->dev; p->br = NULL; p->dev = NULL; dev_put(dev); kobject_put(&p->kobj); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger4395.56%583.33%
Andrew Morton24.44%116.67%
Total45100.00%6100.00%


static void destroy_nbp_rcu(struct rcu_head *head) { struct net_bridge_port *p = container_of(head, struct net_bridge_port, rcu); destroy_nbp(p); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton31100.00%1100.00%
Total31100.00%1100.00%


static unsigned get_max_headroom(struct net_bridge *br) { unsigned max_headroom = 0; struct net_bridge_port *p; list_for_each_entry(p, &br->port_list, list) { unsigned dev_headroom = netdev_get_fwd_headroom(p->dev); if (dev_headroom > max_headroom) max_headroom = dev_headroom; } return max_headroom; }

Contributors

PersonTokensPropCommitsCommitProp
Paolo Abeni54100.00%1100.00%
Total54100.00%1100.00%


static void update_headroom(struct net_bridge *br, int new_hr) { struct net_bridge_port *p; list_for_each_entry(p, &br->port_list, list) netdev_set_rx_headroom(p->dev, new_hr); br->dev->needed_headroom = new_hr; }

Contributors

PersonTokensPropCommitsCommitProp
Paolo Abeni44100.00%1100.00%
Total44100.00%1100.00%

/* Delete port(interface) from bridge is done in two steps. * via RCU. First step, marks device as down. That deletes * all the timers and stops new packets from flowing through. * * Final cleanup doesn't occur until after all CPU's finished * processing packets. * * Protected from multiple admin operations by RTNL mutex */
static void del_nbp(struct net_bridge_port *p) { struct net_bridge *br = p->br; struct net_device *dev = p->dev; sysfs_remove_link(br->ifobj, p->dev->name); nbp_delete_promisc(p); spin_lock_bh(&br->lock); br_stp_disable_port(p); spin_unlock_bh(&br->lock); br_ifinfo_notify(RTM_DELLINK, p); list_del_rcu(&p->list); if (netdev_get_fwd_headroom(dev) == br->dev->needed_headroom) update_headroom(br, get_max_headroom(br)); netdev_reset_rx_headroom(dev); nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 0, 1); switchdev_deferred_process(); nbp_update_port_count(br); netdev_upper_dev_unlink(dev, br->dev); dev->priv_flags &= ~IFF_BRIDGE_PORT; netdev_rx_handler_unregister(dev); br_multicast_del_port(p); kobject_uevent(&p->kobj, KOBJ_REMOVE); kobject_del(&p->kobj); br_netpoll_disable(p); call_rcu(&p->rcu, destroy_nbp_rcu); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger8443.30%936.00%
Paolo Abeni2814.43%14.00%
Linus Torvalds (pre-git)2613.40%14.00%
Jiri Pirko2010.31%416.00%
Vlad Yasevich157.73%28.00%
Nikolay Aleksandrov73.61%28.00%
Herbert Xu63.09%28.00%
Américo Wang31.55%14.00%
Randy Dunlap31.55%14.00%
Simon Arlott10.52%14.00%
Andrew Morton10.52%14.00%
Total194100.00%25100.00%

/* Delete bridge device */
void br_dev_delete(struct net_device *dev, struct list_head *head) { struct net_bridge *br = netdev_priv(dev); struct net_bridge_port *p, *n; list_for_each_entry_safe(p, n, &br->port_list, list) { del_nbp(p); } br_fdb_delete_by_port(br, NULL, 0, 1); cancel_delayed_work_sync(&br->gc_work); br_sysfs_delbr(br->dev); unregister_netdevice_queue(br->dev, head); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger5362.35%444.44%
Linus Torvalds (pre-git)1112.94%111.11%
Ding Tianhong910.59%111.11%
Eric Dumazet89.41%111.11%
Nikolay Aleksandrov44.71%222.22%
Total85100.00%9100.00%

/* find an available port number */
static int find_portno(struct net_bridge *br) { int index; struct net_bridge_port *p; unsigned long *inuse; inuse = kcalloc(BITS_TO_LONGS(BR_MAX_PORTS), sizeof(unsigned long), GFP_KERNEL); if (!inuse) return -ENOMEM; set_bit(0, inuse); /* zero is reserved */ list_for_each_entry(p, &br->port_list, list) { set_bit(p->port_no, inuse); } index = find_first_zero_bit(inuse, BR_MAX_PORTS); kfree(inuse); return (index >= BR_MAX_PORTS) ? -EXFULL : index; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger104100.00%4100.00%
Total104100.00%4100.00%

/* called with RTNL but without bridge lock */
static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device *dev) { struct net_bridge_port *p; int index, err; index = find_portno(br); if (index < 0) return ERR_PTR(index); p = kzalloc(sizeof(*p), GFP_KERNEL); if (p == NULL) return ERR_PTR(-ENOMEM); p->br = br; dev_hold(dev); p->dev = dev; p->path_cost = port_cost(dev); p->priority = 0x8000 >> BR_PORT_BITS; p->port_no = index; p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD; br_init_port(p); br_set_state(p, BR_STATE_DISABLED); br_stp_port_timer_init(p); err = br_multicast_add_port(p); if (err) { dev_put(dev); kfree(p); p = ERR_PTR(err); } return p; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8046.24%16.67%
Stephen Hemminger4425.43%746.67%
Nikolay Aleksandrov3218.50%213.33%
Herbert Xu52.89%16.67%
Fischer, Anna52.89%16.67%
Florian Fainelli42.31%16.67%
Vlad Yasevich31.73%213.33%
Total173100.00%15100.00%


int br_add_bridge(struct net *net, const char *name) { struct net_device *dev; int res; dev = alloc_netdev(sizeof(struct net_bridge), name, NET_NAME_UNKNOWN, br_dev_setup); if (!dev) return -ENOMEM; dev_net_set(dev, net); dev->rtnl_link_ops = &br_link_ops; res = register_netdev(dev); if (res) free_netdev(dev); return res; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger3036.59%545.45%
Linus Torvalds (pre-git)2125.61%19.09%
Eric Dumazet1720.73%19.09%
Alexey Dobriyan67.32%19.09%
Marcel Holtmann33.66%19.09%
Pavel Emelyanov33.66%19.09%
Tom Gundersen22.44%19.09%
Total82100.00%11100.00%


int br_del_bridge(struct net *net, const char *name) { struct net_device *dev; int ret = 0; rtnl_lock(); dev = __dev_get_by_name(net, name); if (dev == NULL) ret = -ENXIO; /* Could not find device */ else if (!(dev->priv_flags & IFF_EBRIDGE)) { /* Attempt to delete non bridge device! */ ret = -EPERM; } else if (dev->flags & IFF_UP) { /* Not shutdown yet. */ ret = -EBUSY; } else br_dev_delete(dev, NULL); rtnl_unlock(); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger5757.00%555.56%
Linus Torvalds (pre-git)3434.00%111.11%
Alexey Dobriyan66.00%111.11%
Eric Dumazet22.00%111.11%
Eric W. Biedermann11.00%111.11%
Total100100.00%9100.00%

/* MTU of the bridge pseudo-device: ETH_DATA_LEN or the minimum of the ports */
int br_min_mtu(const struct net_bridge *br) { const struct net_bridge_port *p; int mtu = 0; ASSERT_RTNL(); if (list_empty(&br->port_list)) mtu = ETH_DATA_LEN; else { list_for_each_entry(p, &br->port_list, list) { if (!mtu || p->dev->mtu < mtu) mtu = p->dev->mtu; } } return mtu; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger7598.68%150.00%
Kris Katterjohn11.32%150.00%
Total76100.00%2100.00%


static void br_set_gso_limits(struct net_bridge *br) { unsigned int gso_max_size = GSO_MAX_SIZE; u16 gso_max_segs = GSO_MAX_SEGS; const struct net_bridge_port *p; list_for_each_entry(p, &br->port_list, list) { gso_max_size = min(gso_max_size, p->dev->gso_max_size); gso_max_segs = min(gso_max_segs, p->dev->gso_max_segs); } br->dev->gso_max_size = gso_max_size; br->dev->gso_max_segs = gso_max_segs; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet80100.00%1100.00%
Total80100.00%1100.00%

/* * Recomputes features using slave's features */
netdev_features_t br_features_recompute(struct net_bridge *br, netdev_features_t features) { struct net_bridge_port *p; netdev_features_t mask; if (list_empty(&br->port_list)) return features; mask = features; features &= ~NETIF_F_ONE_FOR_ALL; list_for_each_entry(p, &br->port_list, list) { features = netdev_increment_features(features, p->dev->features, mask); } features = netdev_add_tso_features(features, mask); return features; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu3037.50%337.50%
Stephen Hemminger2632.50%112.50%
Michał Mirosław1518.75%337.50%
Toshiaki Makita911.25%112.50%
Total80100.00%8100.00%

/* called with RTNL */
int br_add_if(struct net_bridge *br, struct net_device *dev) { struct net_bridge_port *p; int err = 0; unsigned br_hr, dev_hr; bool changed_addr; /* Don't allow bridging non-ethernet like devices, or DSA-enabled * master network devices since the bridge layer rx_handler prevents * the DSA fake ethertype handler to be invoked, so we do not strip off * the DSA switch tag protocol header and the bridge layer just return * RX_HANDLER_CONSUMED, stopping RX processing for these frames. */ if ((dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr) || netdev_uses_dsa(dev)) return -EINVAL; /* No bridging of bridges */ if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit) return -ELOOP; /* Device is already being bridged */ if (br_port_exists(dev)) return -EBUSY; /* No bridging devices that dislike that (e.g. wireless) */ if (dev->priv_flags & IFF_DONT_BRIDGE) return -EOPNOTSUPP; p = new_nbp(br, dev); if (IS_ERR(p)) return PTR_ERR(p); call_netdevice_notifiers(NETDEV_JOIN, dev); err = dev_set_allmulti(dev, 1); if (err) goto put_back; err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj), SYSFS_BRIDGE_PORT_ATTR); if (err) goto err1; err = br_sysfs_addif(p); if (err) goto err2; err = br_netpoll_enable(p); if (err) goto err3; err = netdev_rx_handler_register(dev, br_handle_frame, p); if (err) goto err4; dev->priv_flags |= IFF_BRIDGE_PORT; err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL); if (err) goto err5; err = nbp_switchdev_mark_set(p); if (err) goto err6; dev_disable_lro(dev); list_add_rcu(&p->list, &br->port_list); nbp_update_port_count(br); netdev_update_features(br->dev); br_hr = br->dev->needed_headroom; dev_hr = netdev_get_fwd_headroom(dev); if (br_hr < dev_hr) update_headroom(br, dev_hr); else netdev_set_rx_headroom(dev, br_hr); if (br_fdb_insert(br, p, dev->dev_addr, 0)) netdev_err(dev, "failed insert local address bridge forwarding table\n"); err = nbp_vlan_init(p); if (err) { netdev_err(dev, "failed to initialize vlan filtering on this port\n"); goto err7; } spin_lock_bh(&br->lock); changed_addr = br_stp_recalculate_bridge_id(br); if (netif_running(dev) && netif_oper_up(dev) && (br->dev->flags & IFF_UP)) br_stp_enable_port(p); spin_unlock_bh(&br->lock); br_ifinfo_notify(RTM_NEWLINK, p); if (changed_addr) call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); dev_set_mtu(br->dev, br_min_mtu(br)); br_set_gso_limits(br); kobject_uevent(&p->kobj, KOBJ_ADD); return 0; err7: list_del_rcu(&p->list); br_fdb_delete_by_port(br, p, 0, 1); nbp_update_port_count(br); err6: netdev_upper_dev_unlink(dev, br->dev); err5: dev->priv_flags &= ~IFF_BRIDGE_PORT; netdev_rx_handler_unregister(dev); err4: br_netpoll_disable(p); err3: sysfs_remove_link(br->ifobj, p->dev->name); err2: kobject_put(&p->kobj); p = NULL; /* kobject_put frees */ err1: dev_set_allmulti(dev, -1); put_back: dev_put(dev); kfree(p); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger18230.28%1632.65%
Jiri Pirko609.98%714.29%
Linus Torvalds (pre-git)487.99%24.08%
Elad Raz447.32%12.04%
Paolo Abeni315.16%12.04%
Wang Chen274.49%12.04%
Herbert Xu254.16%12.04%
Aji Srinivas233.83%12.04%
Toshiaki Makita223.66%12.04%
Ido Schimmel183.00%12.04%
Vlad Yasevich183.00%36.12%
Florian Fainelli162.66%24.08%
Greg Kroah-Hartman152.50%12.04%
Johannes Berg132.16%12.04%
Linus Torvalds111.83%12.04%
Gao Feng101.66%12.04%
Volodymyr G. Lukiianyk71.16%12.04%
Américo Wang71.16%12.04%
Michał Mirosław71.16%12.04%
Eric Dumazet50.83%12.04%
Ben Hutchings50.83%12.04%
Jeff Hansen50.83%12.04%
Xiaotian Feng10.17%12.04%
Wang Weidong10.17%12.04%
Total601100.00%49100.00%

/* called with RTNL */
int br_del_if(struct net_bridge *br, struct net_device *dev) { struct net_bridge_port *p; bool changed_addr; p = br_port_get_rtnl(dev); if (!p || p->br != br) return -EINVAL; /* Since more than one interface can be attached to a bridge, * there still maybe an alternate path for netconsole to use; * therefore there is no reason for a NETDEV_RELEASE event. */ del_nbp(p); dev_set_mtu(br->dev, br_min_mtu(br)); br_set_gso_limits(br); spin_lock_bh(&br->lock); changed_addr = br_stp_recalculate_bridge_id(br); spin_unlock_bh(&br->lock); if (changed_addr) call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); netdev_update_features(br->dev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger3934.21%538.46%
Linus Torvalds (pre-git)2521.93%17.69%
Andrei Warkentin1815.79%17.69%
Venkat Venkatsubra1210.53%17.69%
Jiri Pirko76.14%17.69%
Eric Dumazet65.26%215.38%
Michał Mirosław65.26%17.69%
Américo Wang10.88%17.69%
Total114100.00%13100.00%


void br_port_flags_change(struct net_bridge_port *p, unsigned long mask) { struct net_bridge *br = p->br; if (mask & BR_AUTO_MASK) nbp_update_port_count(br); }

Contributors

PersonTokensPropCommitsCommitProp
Vlad Yasevich34100.00%1100.00%
Total34100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger98538.30%4436.67%
Vlad Yasevich39715.44%65.00%
Linus Torvalds (pre-git)30211.74%21.67%
Paolo Abeni1576.10%10.83%
Eric Dumazet1184.59%43.33%
Jiri Pirko903.50%86.67%
Herbert Xu662.57%54.17%
Elad Raz441.71%10.83%
Nikolay Aleksandrov431.67%54.17%
Toshiaki Makita421.63%32.50%
Andrew Morton341.32%10.83%
Michał Mirosław281.09%32.50%
Wang Chen271.05%10.83%
Aji Srinivas230.89%10.83%
Florian Fainelli200.78%32.50%
Ido Schimmel180.70%10.83%
Andrei Warkentin180.70%10.83%
Greg Kroah-Hartman150.58%10.83%
Américo Wang140.54%32.50%
Johannes Berg130.51%10.83%
Venkat Venkatsubra120.47%10.83%
Alexey Dobriyan120.47%10.83%
Linus Torvalds110.43%10.83%
Gao Feng100.39%10.83%
Ding Tianhong90.35%10.83%
Volodymyr G. Lukiianyk70.27%10.83%
David Decotigny60.23%10.83%
Jeff Hansen50.19%10.83%
Kris Katterjohn50.19%10.83%
Fischer, Anna50.19%10.83%
Ben Hutchings50.19%10.83%
David Howells50.19%10.83%
David S. Miller30.12%10.83%
Marcel Holtmann30.12%10.83%
Randy Dunlap30.12%10.83%
Matthew Wilcox30.12%10.83%
Pavel Emelyanov30.12%10.83%
Tejun Heo30.12%10.83%
Tom Gundersen20.08%10.83%
Xiaotian Feng10.04%10.83%
Simon Arlott10.04%10.83%
Wang Weidong10.04%10.83%
Tan Xiaojun10.04%10.83%
Adrian Bunk10.04%10.83%
Eric W. Biedermann10.04%10.83%
Total2572100.00%120100.00%
Directory: net/bridge
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.