bridge: use rx_handler_data pointer to store net_bridge_port pointer

Register net_bridge_port pointer as rx_handler data pointer. As br_port is
removed from struct net_device, another netdev priv_flag is added to indicate
the device serves as a bridge port. Also rcuized pointers are now correctly
dereferenced in br_fdb.c and in netfilter parts.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Jiri Pirko 2010-06-15 06:50:45 +00:00 committed by David S. Miller
parent a35e2c1b6d
commit f350a0a873
20 changed files with 71 additions and 50 deletions

View File

@ -5718,7 +5718,7 @@ static void dev_set_promiscuous(struct net_device *dev, struct dev_priv *priv,
* from the bridge.
*/
if ((hw->features & STP_SUPPORT) && !promiscuous &&
dev->br_port) {
(dev->priv_flags & IFF_BRIDGE_PORT)) {
struct ksz_switch *sw = hw->ksz_switch;
int port = priv->port.first_port;

View File

@ -71,7 +71,7 @@ static int is_valid_iface(struct net_device *net_dev)
#endif
/* Device is being bridged */
/* if (net_dev->br_port != NULL)
/* if (net_dev->priv_flags & IFF_BRIDGE_PORT)
return 0; */
return 1;

View File

@ -74,6 +74,7 @@
#define IFF_IN_NETPOLL 0x1000 /* whether we are processing netpoll */
#define IFF_DISABLE_NETPOLL 0x2000 /* disable netpoll at run-time */
#define IFF_MACVLAN_PORT 0x4000 /* device used as macvlan port */
#define IFF_BRIDGE_PORT 0x8000 /* device used as bridge port */
#define IF_GET_IFACE 0x0001 /* for querying only */
#define IF_GET_PROTO 0x0002

View File

@ -1047,8 +1047,6 @@ struct net_device {
/* mid-layer private */
void *ml_priv;
/* bridge stuff */
struct net_bridge_port *br_port;
/* GARP */
struct garp_port *garp_port;

View File

@ -242,11 +242,11 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
struct net_bridge_fdb_entry *fdb;
int ret;
if (!dev->br_port)
if (!br_port_exists(dev))
return 0;
rcu_read_lock();
fdb = __br_fdb_get(dev->br_port->br, addr);
fdb = __br_fdb_get(br_port_get_rcu(dev)->br, addr);
ret = fdb && fdb->dst->dev != dev &&
fdb->dst->state == BR_STATE_FORWARDING;
rcu_read_unlock();

View File

@ -147,8 +147,9 @@ static void del_nbp(struct net_bridge_port *p)
list_del_rcu(&p->list);
dev->priv_flags &= ~IFF_BRIDGE_PORT;
netdev_rx_handler_unregister(dev);
rcu_assign_pointer(dev->br_port, NULL);
br_multicast_del_port(p);
@ -400,7 +401,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return -ELOOP;
/* Device is already being bridged */
if (dev->br_port != NULL)
if (br_port_exists(dev))
return -EBUSY;
/* No bridging devices that dislike that (e.g. wireless) */
@ -431,11 +432,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (br_netpoll_info(br) && ((err = br_netpoll_enable(p))))
goto err3;
rcu_assign_pointer(dev->br_port, p);
err = netdev_rx_handler_register(dev, br_handle_frame, NULL);
err = netdev_rx_handler_register(dev, br_handle_frame, p);
if (err)
goto err4;
goto err3;
dev->priv_flags |= IFF_BRIDGE_PORT;
dev_disable_lro(dev);
@ -457,8 +458,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
kobject_uevent(&p->kobj, KOBJ_ADD);
return 0;
err4:
rcu_assign_pointer(dev->br_port, NULL);
err3:
sysfs_remove_link(br->ifobj, p->dev->name);
err2:
@ -477,9 +476,13 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
/* called with RTNL */
int br_del_if(struct net_bridge *br, struct net_device *dev)
{
struct net_bridge_port *p = dev->br_port;
struct net_bridge_port *p;
if (!p || p->br != br)
if (!br_port_exists(dev))
return -EINVAL;
p = br_port_get(dev);
if (p->br != br)
return -EINVAL;
del_nbp(p);

View File

@ -41,7 +41,7 @@ static int br_pass_frame_up(struct sk_buff *skb)
int br_handle_frame_finish(struct sk_buff *skb)
{
const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
struct net_bridge *br;
struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst;
@ -111,10 +111,9 @@ int br_handle_frame_finish(struct sk_buff *skb)
/* note: already called with rcu_read_lock (preempt_disabled) */
static int br_handle_local_finish(struct sk_buff *skb)
{
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
if (p)
br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
return 0; /* process further */
}
@ -151,7 +150,7 @@ struct sk_buff *br_handle_frame(struct sk_buff *skb)
if (!skb)
return NULL;
p = rcu_dereference(skb->dev->br_port);
p = br_port_get_rcu(skb->dev);
if (unlikely(is_link_local(dest))) {
/* Pause frames shouldn't be passed up by driver anyway */

View File

@ -127,16 +127,17 @@ void br_netfilter_rtable_init(struct net_bridge *br)
static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
{
struct net_bridge_port *port = rcu_dereference(dev->br_port);
return port ? &port->br->fake_rtable : NULL;
if (!br_port_exists(dev))
return NULL;
return &br_port_get_rcu(dev)->br->fake_rtable;
}
static inline struct net_device *bridge_parent(const struct net_device *dev)
{
struct net_bridge_port *port = rcu_dereference(dev->br_port);
if (!br_port_exists(dev))
return NULL;
return port ? port->br->dev : NULL;
return br_port_get_rcu(dev)->br->dev;
}
static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)

View File

@ -120,10 +120,11 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
idx = 0;
for_each_netdev(net, dev) {
/* not a bridge port */
if (dev->br_port == NULL || idx < cb->args[0])
if (!br_port_exists(dev) || idx < cb->args[0])
goto skip;
if (br_fill_ifinfo(skb, dev->br_port, NETLINK_CB(cb->skb).pid,
if (br_fill_ifinfo(skb, br_port_get(dev),
NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, RTM_NEWLINK,
NLM_F_MULTI) < 0)
break;
@ -168,9 +169,9 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
if (!dev)
return -ENODEV;
p = dev->br_port;
if (!p)
if (!br_port_exists(dev))
return -EINVAL;
p = br_port_get(dev);
/* if kernel STP is running, don't allow changes */
if (p->br->stp_enabled == BR_KERNEL_STP)

View File

@ -32,14 +32,15 @@ struct notifier_block br_device_notifier = {
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{
struct net_device *dev = ptr;
struct net_bridge_port *p = dev->br_port;
struct net_bridge_port *p = br_port_get(dev);
struct net_bridge *br;
int err;
/* not a port of a bridge */
if (p == NULL)
if (!br_port_exists(dev))
return NOTIFY_DONE;
p = br_port_get(dev);
br = p->br;
switch (event) {

View File

@ -150,6 +150,11 @@ struct net_bridge_port
#endif
};
#define br_port_get_rcu(dev) \
((struct net_bridge_port *) rcu_dereference(dev->rx_handler_data))
#define br_port_get(dev) ((struct net_bridge_port *) dev->rx_handler_data)
#define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
struct br_cpu_netstats {
unsigned long rx_packets;
unsigned long rx_bytes;

View File

@ -137,12 +137,13 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
struct net_device *dev)
{
const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = rcu_dereference(dev->br_port);
struct net_bridge_port *p;
struct net_bridge *br;
const unsigned char *buf;
if (!p)
if (!br_port_exists(dev))
goto err;
p = br_port_get_rcu(dev);
if (!pskb_may_pull(skb, 4))
goto err;

View File

@ -24,8 +24,9 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)
return EBT_DROP;
if (par->hooknum != NF_BR_BROUTING)
/* rcu_read_lock()ed by nf_hook_slow */
memcpy(eth_hdr(skb)->h_dest,
par->in->br_port->br->dev->dev_addr, ETH_ALEN);
br_port_get_rcu(par->in)->br->dev->dev_addr, ETH_ALEN);
else
memcpy(eth_hdr(skb)->h_dest, par->in->dev_addr, ETH_ALEN);
skb->pkt_type = PACKET_HOST;

View File

@ -177,8 +177,9 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
if (in) {
strcpy(pm->physindev, in->name);
/* If in isn't a bridge, then physindev==indev */
if (in->br_port)
strcpy(pm->indev, in->br_port->br->dev->name);
if (br_port_exists(in))
/* rcu_read_lock()ed by nf_hook_slow */
strcpy(pm->indev, br_port_get_rcu(in)->br->dev->name);
else
strcpy(pm->indev, in->name);
} else
@ -187,7 +188,8 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
if (out) {
/* If out exists, then out is a bridge port */
strcpy(pm->physoutdev, out->name);
strcpy(pm->outdev, out->br_port->br->dev->name);
/* rcu_read_lock()ed by nf_hook_slow */
strcpy(pm->outdev, br_port_get_rcu(out)->br->dev->name);
} else
pm->outdev[0] = pm->physoutdev[0] = '\0';

View File

@ -140,11 +140,14 @@ ebt_basic_match(const struct ebt_entry *e, const struct ethhdr *h,
return 1;
if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
return 1;
if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
e->logical_in, in->br_port->br->dev), EBT_ILOGICALIN))
/* rcu_read_lock()ed by nf_hook_slow */
if (in && br_port_exists(in) &&
FWINV2(ebt_dev_check(e->logical_in, br_port_get_rcu(in)->br->dev),
EBT_ILOGICALIN))
return 1;
if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
e->logical_out, out->br_port->br->dev), EBT_ILOGICALOUT))
if (out && br_port_exists(out) &&
FWINV2(ebt_dev_check(e->logical_out, br_port_get_rcu(out)->br->dev),
EBT_ILOGICALOUT))
return 1;
if (e->bitmask & EBT_SOURCEMAC) {

View File

@ -2765,7 +2765,8 @@ int __skb_bond_should_drop(struct sk_buff *skb, struct net_device *master)
if (master->priv_flags & IFF_MASTER_ARPMON)
dev->last_rx = jiffies;
if ((master->priv_flags & IFF_MASTER_ALB) && master->br_port) {
if ((master->priv_flags & IFF_MASTER_ALB) &&
(master->priv_flags & IFF_BRIDGE_PORT)) {
/* Do address unmangle. The local destination address
* will be always the one master has. Provides the right
* functionality in a bridge.

View File

@ -403,8 +403,9 @@ __build_packet_message(struct nfulnl_instance *inst,
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSINDEV,
htonl(indev->ifindex));
/* this is the bridge group "brX" */
/* rcu_read_lock()ed by nf_hook_slow or nf_log_packet */
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_INDEV,
htonl(indev->br_port->br->dev->ifindex));
htonl(br_port_get_rcu(indev)->br->dev->ifindex));
} else {
/* Case 2: indev is bridge group, we need to look for
* physical device (when called from ipv4) */
@ -430,8 +431,9 @@ __build_packet_message(struct nfulnl_instance *inst,
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSOUTDEV,
htonl(outdev->ifindex));
/* this is the bridge group "brX" */
/* rcu_read_lock()ed by nf_hook_slow or nf_log_packet */
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_OUTDEV,
htonl(outdev->br_port->br->dev->ifindex));
htonl(br_port_get_rcu(outdev)->br->dev->ifindex));
} else {
/* Case 2: indev is a bridge group, we need to look
* for physical device (when called from ipv4) */

View File

@ -296,8 +296,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSINDEV,
htonl(indev->ifindex));
/* this is the bridge group "brX" */
/* rcu_read_lock()ed by __nf_queue */
NLA_PUT_BE32(skb, NFQA_IFINDEX_INDEV,
htonl(indev->br_port->br->dev->ifindex));
htonl(br_port_get_rcu(indev)->br->dev->ifindex));
} else {
/* Case 2: indev is bridge group, we need to look for
* physical device (when called from ipv4) */
@ -321,8 +322,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSOUTDEV,
htonl(outdev->ifindex));
/* this is the bridge group "brX" */
/* rcu_read_lock()ed by __nf_queue */
NLA_PUT_BE32(skb, NFQA_IFINDEX_OUTDEV,
htonl(outdev->br_port->br->dev->ifindex));
htonl(br_port_get_rcu(outdev)->br->dev->ifindex));
} else {
/* Case 2: outdev is bridge group, we need to look for
* physical output device (when called from ipv4) */

View File

@ -1107,7 +1107,7 @@ static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype)
{
if (!use_4addr) {
if (netdev && netdev->br_port)
if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
return -EBUSY;
return 0;
}

View File

@ -770,8 +770,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
return -EOPNOTSUPP;
/* if it's part of a bridge, reject changing type to station/ibss */
if (dev->br_port && (ntype == NL80211_IFTYPE_ADHOC ||
ntype == NL80211_IFTYPE_STATION))
if ((dev->priv_flags & IFF_BRIDGE_PORT) &&
(ntype == NL80211_IFTYPE_ADHOC || ntype == NL80211_IFTYPE_STATION))
return -EBUSY;
if (ntype != otype) {