--- zzzz-none-000/linux-2.6.32.61/net/core/rtnetlink.c 2013-06-10 09:43:48.000000000 +0000 +++ ar9-7330-650/linux-2.6.32.61/net/core/rtnetlink.c 2014-09-24 15:12:05.000000000 +0000 @@ -34,8 +34,10 @@ #include #include #include +#include #include - +#include +#include #include #include #include @@ -59,15 +61,72 @@ rtnl_dumpit_func dumpit; }; +static DECLARE_RWSEM(rtnl_offload_rwsem); + +void rtnl_offload_read_lock(void) +{ + down_read(&rtnl_offload_rwsem); +} +EXPORT_SYMBOL(rtnl_offload_read_lock); + +void rtnl_offload_read_unlock(void) +{ + up_read(&rtnl_offload_rwsem); +} +EXPORT_SYMBOL(rtnl_offload_read_unlock); + + +void rtnl_offload_write_lock(void) +{ + down_write(&rtnl_offload_rwsem); +} +EXPORT_SYMBOL(rtnl_offload_write_lock); + +void rtnl_offload_write_unlock(void) +{ + up_write(&rtnl_offload_rwsem); +} +EXPORT_SYMBOL(rtnl_offload_write_unlock); + +void (*wait_for_link_to_offload_cpu_hook)(void) = NULL; +EXPORT_SYMBOL(wait_for_link_to_offload_cpu_hook); + +void wait_for_link_to_offload_cpu(void){ + if(wait_for_link_to_offload_cpu_hook != NULL) + wait_for_link_to_offload_cpu_hook(); +} + + +// --------------------------------- +// +/*--- #define DEBUG_RTNL_LOCK ---*/ +#ifdef DEBUG_RTNL_LOCK +void *current_rtnl_owner = NULL; +atomic_t current_rtnl_count; +#endif /*--- #ifdef DEBUG_RTNL_LOCK ---*/ + static DEFINE_MUTEX(rtnl_mutex); void rtnl_lock(void) { +#ifdef DEBUG_RTNL_LOCK + if(rtnl_is_locked()) { + printk(KERN_ERR "[RTNL] '%s' will block\n", current->comm); + } + atomic_inc(¤t_rtnl_count); +#endif /*--- #ifdef DEBUG_RTNL_LOCK ---*/ mutex_lock(&rtnl_mutex); +#ifdef DEBUG_RTNL_LOCK + current_rtnl_owner = current; +#endif /*--- #ifdef DEBUG_RTNL_LOCK ---*/ } void __rtnl_unlock(void) { +#ifdef DEBUG_RTNL_LOCK + current_rtnl_owner = NULL; + atomic_dec(¤t_rtnl_count); +#endif /*--- #ifdef DEBUG_RTNL_LOCK ---*/ mutex_unlock(&rtnl_mutex); } @@ -79,7 +138,15 @@ int rtnl_trylock(void) { - return mutex_trylock(&rtnl_mutex); + int ret = mutex_trylock(&rtnl_mutex); +#ifdef DEBUG_RTNL_LOCK + if(!ret) { + /*--- printk(KERN_ERR "[RTNL] '%s' would block\n", current->comm); ---*/ + return 0; + } + atomic_inc(¤t_rtnl_count); +#endif /*--- #ifdef DEBUG_RTNL_LOCK ---*/ + return ret; } int rtnl_is_locked(void) @@ -998,9 +1065,14 @@ if (tb[IFLA_MTU]) dev->mtu = nla_get_u32(tb[IFLA_MTU]); - if (tb[IFLA_ADDRESS]) + if (tb[IFLA_ADDRESS]) { memcpy(dev->dev_addr, nla_data(tb[IFLA_ADDRESS]), nla_len(tb[IFLA_ADDRESS])); + /* AVM: backport: 2afb9b533423a9b97f84181e773cf9361d98fed6 + * + * ethtool: set addr_assign_type to NET_ADDR_SET when addr is passed on create */ + dev->addr_assign_type = NET_ADDR_SET; + } if (tb[IFLA_BROADCAST]) memcpy(dev->broadcast, nla_data(tb[IFLA_BROADCAST]), nla_len(tb[IFLA_BROADCAST])); @@ -1241,6 +1313,152 @@ rtnl_set_sk_err(net, RTNLGRP_LINK, err); } +static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +{ + struct net *net = sock_net(skb->sk); + struct net_device *master = NULL; + struct ndmsg *ndm; + struct nlattr *tb[NDA_MAX+1]; + struct net_device *dev; + u8 *addr; + int err; + + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); + if (err < 0) + return err; + + ndm = nlmsg_data(nlh); + if (ndm->ndm_ifindex == 0) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ifindex\n"); + return -EINVAL; + } + + dev = __dev_get_by_index(net, ndm->ndm_ifindex); + if (dev == NULL) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with unknown ifindex\n"); + return -ENODEV; + } + + if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid address\n"); + return -EINVAL; + } + + addr = nla_data(tb[NDA_LLADDR]); + if (!is_valid_ether_addr(addr)) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ether address\n"); + return -EINVAL; + } + + err = -EOPNOTSUPP; + + /* Support fdb on master device the net/bridge default case */ + if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && + (dev->priv_flags & IFF_BRIDGE_PORT)) { + master = dev->master; + err = master->netdev_ops->ndo_fdb_add(ndm, dev, addr, + nlh->nlmsg_flags); + if (err) + goto out; + else + ndm->ndm_flags &= ~NTF_MASTER; + } + + /* Embedded bridge, macvlan, and any other device support */ + if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_add) { + err = dev->netdev_ops->ndo_fdb_add(ndm, dev, addr, + nlh->nlmsg_flags); + + if (!err) + ndm->ndm_flags &= ~NTF_SELF; + } +out: + return err; +} + +static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +{ + struct net *net = sock_net(skb->sk); + struct ndmsg *ndm; + struct nlattr *llattr; + struct net_device *dev; + int err = -EINVAL; + __u8 *addr; + + if (nlmsg_len(nlh) < sizeof(*ndm)) + return -EINVAL; + + ndm = nlmsg_data(nlh); + if (ndm->ndm_ifindex == 0) { + pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid ifindex\n"); + return -EINVAL; + } + + dev = __dev_get_by_index(net, ndm->ndm_ifindex); + if (dev == NULL) { + pr_info("PF_BRIDGE: RTM_DELNEIGH with unknown ifindex\n"); + return -ENODEV; + } + + llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR); + if (llattr == NULL || nla_len(llattr) != ETH_ALEN) { + pr_info("PF_BRIGDE: RTM_DELNEIGH with invalid address\n"); + return -EINVAL; + } + + addr = nla_data(llattr); + err = -EOPNOTSUPP; + + /* Support fdb on master device the net/bridge default case */ + if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && + (dev->priv_flags & IFF_BRIDGE_PORT)) { + struct net_device *master = dev->master; + + if (master->netdev_ops->ndo_fdb_del) + err = master->netdev_ops->ndo_fdb_del(ndm, dev, addr); + + if (err) + goto out; + else + ndm->ndm_flags &= ~NTF_MASTER; + } + + /* Embedded bridge, macvlan, and any other device support */ + if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) { + err = dev->netdev_ops->ndo_fdb_del(ndm, dev, addr); + + if (!err) + ndm->ndm_flags &= ~NTF_SELF; + } +out: + return err; +} + +static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx = 0; + struct net *net = sock_net(skb->sk); + struct net_device *dev; + + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + if (dev->priv_flags & IFF_BRIDGE_PORT) { + struct net_device *master = dev->master; + const struct net_device_ops *ops = master->netdev_ops; + + if (ops->ndo_fdb_dump) + idx = ops->ndo_fdb_dump(skb, cb, dev, idx); + } + + if (dev->netdev_ops->ndo_fdb_dump) + idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx); + } + rcu_read_unlock(); + + cb->args[0] = idx; + return skb->len; +} + /* Protected by RTNL sempahore. */ static struct rtattr **rta_buf; static int rtattr_max; @@ -1402,6 +1620,20 @@ rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all); rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all); + +#if 0 + /* AVM rtnl_calcit_func parameter not present in2 2.6.32, not used anyway + * + * Introduced by c7ac8679bec9397afe8918f788cbcef88c38da54, around v3.0 + */ + rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL); + rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL); + rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); +#else + rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL); + rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL); + rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump); +#endif } EXPORT_SYMBOL(__rta_fill);