#include <linux/in.h>
#include <linux/jhash.h>
#include <linux/random.h>
-#include <trace/napi.h>
+#include <trace/events/napi.h>
#include "net-sysfs.h"
ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
ARPHRD_FCFABRIC, ARPHRD_IEEE802_TR, ARPHRD_IEEE80211,
ARPHRD_IEEE80211_PRISM, ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET,
- ARPHRD_PHONET_PIPE, ARPHRD_VOID, ARPHRD_NONE};
+ ARPHRD_PHONET_PIPE, ARPHRD_IEEE802154, ARPHRD_IEEE802154_PHY,
+ ARPHRD_VOID, ARPHRD_NONE};
static const char *netdev_lock_name[] =
{"_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
"_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
"_xmit_FCFABRIC", "_xmit_IEEE802_TR", "_xmit_IEEE80211",
"_xmit_IEEE80211_PRISM", "_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET",
- "_xmit_PHONET_PIPE", "_xmit_VOID", "_xmit_NONE"};
+ "_xmit_PHONET_PIPE", "_xmit_IEEE802154", "_xmit_IEEE802154_PHY",
+ "_xmit_VOID", "_xmit_NONE"};
static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
static struct lock_class_key netdev_addr_lock_key[ARRAY_SIZE(netdev_lock_type)];
int dev_open(struct net_device *dev)
{
const struct net_device_ops *ops = dev->netdev_ops;
- int ret = 0;
+ int ret;
ASSERT_RTNL();
if (!netif_device_present(dev))
return -ENODEV;
+ ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev);
+ ret = notifier_to_errno(ret);
+ if (ret)
+ return ret;
+
/*
* Call device private open method
*/
* If device doesnt need skb->dst, release it right now while
* its hot in this cpu cache
*/
- if ((dev->priv_flags & IFF_XMIT_DST_RELEASE) && skb->dst) {
- dst_release(skb->dst);
- skb->dst = NULL;
- }
+ if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
+ skb_dst_drop(skb);
+
rc = ops->ndo_start_xmit(skb, dev);
if (rc == 0)
txq_trans_update(txq);
if (netif_needs_gso(dev, skb))
goto gso;
- if (skb_shinfo(skb)->frag_list &&
+ if (skb_has_frags(skb) &&
!(dev->features & NETIF_F_FRAGLIST) &&
__skb_linearize(skb))
goto out_kfree_skb;
}
#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
-/* These hooks defined here for ATM */
-struct net_bridge;
-struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
- unsigned char *addr);
-void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent) __read_mostly;
+
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+/* This hook is defined here for ATM LANE */
+int (*br_fdb_test_addr_hook)(struct net_device *dev,
+ unsigned char *addr) __read_mostly;
+EXPORT_SYMBOL(br_fdb_test_addr_hook);
+#endif
/*
* If bridge module is loaded call bridging hook.
*/
struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p,
struct sk_buff *skb) __read_mostly;
+EXPORT_SYMBOL(br_handle_frame_hook);
+
static inline struct sk_buff *handle_bridge(struct sk_buff *skb,
struct packet_type **pt_prev, int *ret,
struct net_device *orig_dev)
if (!(skb->dev->features & NETIF_F_GRO))
goto normal;
- if (skb_is_gso(skb) || skb_shinfo(skb)->frag_list)
+ if (skb_is_gso(skb) || skb_has_frags(skb))
goto normal;
rcu_read_lock();
ret = GRO_HELD;
pull:
- if (unlikely(!pskb_may_pull(skb, skb_gro_offset(skb)))) {
- if (napi->gro_list == skb)
- napi->gro_list = skb->next;
- ret = GRO_DROP;
+ if (skb_headlen(skb) < skb_gro_offset(skb)) {
+ int grow = skb_gro_offset(skb) - skb_headlen(skb);
+
+ BUG_ON(skb->end - skb->tail < grow);
+
+ memcpy(skb_tail_pointer(skb), NAPI_GRO_CB(skb)->frag0, grow);
+
+ skb->tail += grow;
+ skb->data_len -= grow;
+
+ skb_shinfo(skb)->frags[0].page_offset += grow;
+ skb_shinfo(skb)->frags[0].size -= grow;
+
+ if (unlikely(!skb_shinfo(skb)->frags[0].size)) {
+ put_page(skb_shinfo(skb)->frags[0].page);
+ memmove(skb_shinfo(skb)->frags,
+ skb_shinfo(skb)->frags + 1,
+ --skb_shinfo(skb)->nr_frags);
+ }
}
ok:
{
NAPI_GRO_CB(skb)->data_offset = 0;
NAPI_GRO_CB(skb)->frag0 = NULL;
+ NAPI_GRO_CB(skb)->frag0_len = 0;
- if (!skb_headlen(skb) && !PageHighMem(skb_shinfo(skb)->frags[0].page))
+ if (skb->mac_header == skb->tail &&
+ !PageHighMem(skb_shinfo(skb)->frags[0].page)) {
NAPI_GRO_CB(skb)->frag0 =
page_address(skb_shinfo(skb)->frags[0].page) +
skb_shinfo(skb)->frags[0].page_offset;
+ NAPI_GRO_CB(skb)->frag0_len = skb_shinfo(skb)->frags[0].size;
+ }
}
EXPORT_SYMBOL(skb_gro_reset_offset);
{
struct sk_buff *skb = napi->skb;
struct ethhdr *eth;
+ unsigned int hlen;
+ unsigned int off;
napi->skb = NULL;
skb_reset_mac_header(skb);
skb_gro_reset_offset(skb);
- eth = skb_gro_header(skb, sizeof(*eth));
- if (!eth) {
- napi_reuse_skb(napi, skb);
- skb = NULL;
- goto out;
+ off = skb_gro_offset(skb);
+ hlen = off + sizeof(*eth);
+ eth = skb_gro_header_fast(skb, off);
+ if (skb_gro_header_hard(skb, hlen)) {
+ eth = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!eth)) {
+ napi_reuse_skb(napi, skb);
+ skb = NULL;
+ goto out;
+ }
}
skb_gro_pull(skb, sizeof(*eth));
/* hw addresses list handling functions */
-static int __hw_addr_add(struct list_head *list, unsigned char *addr,
- int addr_len, unsigned char addr_type)
+static int __hw_addr_add(struct list_head *list, int *delta,
+ unsigned char *addr, int addr_len,
+ unsigned char addr_type)
{
struct netdev_hw_addr *ha;
int alloc_size;
if (addr_len > MAX_ADDR_LEN)
return -EINVAL;
+ list_for_each_entry(ha, list, list) {
+ if (!memcmp(ha->addr, addr, addr_len) &&
+ ha->type == addr_type) {
+ ha->refcount++;
+ return 0;
+ }
+ }
+
+
alloc_size = sizeof(*ha);
if (alloc_size < L1_CACHE_BYTES)
alloc_size = L1_CACHE_BYTES;
return -ENOMEM;
memcpy(ha->addr, addr, addr_len);
ha->type = addr_type;
+ ha->refcount = 1;
+ ha->synced = false;
list_add_tail_rcu(&ha->list, list);
+ if (delta)
+ (*delta)++;
return 0;
}
kfree(ha);
}
-static int __hw_addr_del_ii(struct list_head *list, unsigned char *addr,
- int addr_len, unsigned char addr_type,
- int ignore_index)
+static int __hw_addr_del(struct list_head *list, int *delta,
+ unsigned char *addr, int addr_len,
+ unsigned char addr_type)
{
struct netdev_hw_addr *ha;
- int i = 0;
list_for_each_entry(ha, list, list) {
- if (i++ != ignore_index &&
- !memcmp(ha->addr, addr, addr_len) &&
+ if (!memcmp(ha->addr, addr, addr_len) &&
(ha->type == addr_type || !addr_type)) {
+ if (--ha->refcount)
+ return 0;
list_del_rcu(&ha->list);
call_rcu(&ha->rcu_head, ha_rcu_free);
+ if (delta)
+ (*delta)--;
return 0;
}
}
return -ENOENT;
}
-static int __hw_addr_add_multiple_ii(struct list_head *to_list,
- struct list_head *from_list,
- int addr_len, unsigned char addr_type,
- int ignore_index)
+static int __hw_addr_add_multiple(struct list_head *to_list, int *to_delta,
+ struct list_head *from_list, int addr_len,
+ unsigned char addr_type)
{
int err;
struct netdev_hw_addr *ha, *ha2;
list_for_each_entry(ha, from_list, list) {
type = addr_type ? addr_type : ha->type;
- err = __hw_addr_add(to_list, ha->addr, addr_len, type);
+ err = __hw_addr_add(to_list, to_delta, ha->addr,
+ addr_len, type);
if (err)
goto unroll;
}
if (ha2 == ha)
break;
type = addr_type ? addr_type : ha2->type;
- __hw_addr_del_ii(to_list, ha2->addr, addr_len, type,
- ignore_index);
+ __hw_addr_del(to_list, to_delta, ha2->addr,
+ addr_len, type);
}
return err;
}
-static void __hw_addr_del_multiple_ii(struct list_head *to_list,
- struct list_head *from_list,
- int addr_len, unsigned char addr_type,
- int ignore_index)
+static void __hw_addr_del_multiple(struct list_head *to_list, int *to_delta,
+ struct list_head *from_list, int addr_len,
+ unsigned char addr_type)
{
struct netdev_hw_addr *ha;
unsigned char type;
list_for_each_entry(ha, from_list, list) {
type = addr_type ? addr_type : ha->type;
- __hw_addr_del_ii(to_list, ha->addr, addr_len, addr_type,
- ignore_index);
+ __hw_addr_del(to_list, to_delta, ha->addr,
+ addr_len, addr_type);
+ }
+}
+
+static int __hw_addr_sync(struct list_head *to_list, int *to_delta,
+ struct list_head *from_list, int *from_delta,
+ int addr_len)
+{
+ int err = 0;
+ struct netdev_hw_addr *ha, *tmp;
+
+ list_for_each_entry_safe(ha, tmp, from_list, list) {
+ if (!ha->synced) {
+ err = __hw_addr_add(to_list, to_delta, ha->addr,
+ addr_len, ha->type);
+ if (err)
+ break;
+ ha->synced = true;
+ ha->refcount++;
+ } else if (ha->refcount == 1) {
+ __hw_addr_del(to_list, to_delta, ha->addr,
+ addr_len, ha->type);
+ __hw_addr_del(from_list, from_delta, ha->addr,
+ addr_len, ha->type);
+ }
+ }
+ return err;
+}
+
+static void __hw_addr_unsync(struct list_head *to_list, int *to_delta,
+ struct list_head *from_list, int *from_delta,
+ int addr_len)
+{
+ struct netdev_hw_addr *ha, *tmp;
+
+ list_for_each_entry_safe(ha, tmp, from_list, list) {
+ if (ha->synced) {
+ __hw_addr_del(to_list, to_delta, ha->addr,
+ addr_len, ha->type);
+ ha->synced = false;
+ __hw_addr_del(from_list, from_delta, ha->addr,
+ addr_len, ha->type);
+ }
}
}
+
static void __hw_addr_flush(struct list_head *list)
{
struct netdev_hw_addr *ha, *tmp;
/* rtnl_mutex must be held here */
INIT_LIST_HEAD(&dev->dev_addr_list);
- memset(addr, 0, sizeof(*addr));
- err = __hw_addr_add(&dev->dev_addr_list, addr, sizeof(*addr),
+ memset(addr, 0, sizeof(addr));
+ err = __hw_addr_add(&dev->dev_addr_list, NULL, addr, sizeof(addr),
NETDEV_HW_ADDR_T_LAN);
if (!err) {
/*
ASSERT_RTNL();
- err = __hw_addr_add(&dev->dev_addr_list, addr, dev->addr_len,
+ err = __hw_addr_add(&dev->dev_addr_list, NULL, addr, dev->addr_len,
addr_type);
if (!err)
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
unsigned char addr_type)
{
int err;
+ struct netdev_hw_addr *ha;
ASSERT_RTNL();
- err = __hw_addr_del_ii(&dev->dev_addr_list, addr, dev->addr_len,
- addr_type, 0);
+ /*
+ * We can not remove the first address from the list because
+ * dev->dev_addr points to that.
+ */
+ ha = list_first_entry(&dev->dev_addr_list, struct netdev_hw_addr, list);
+ if (ha->addr == dev->dev_addr && ha->refcount == 1)
+ return -ENOENT;
+
+ err = __hw_addr_del(&dev->dev_addr_list, NULL, addr, dev->addr_len,
+ addr_type);
if (!err)
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
return err;
if (from_dev->addr_len != to_dev->addr_len)
return -EINVAL;
- err = __hw_addr_add_multiple_ii(&to_dev->dev_addr_list,
- &from_dev->dev_addr_list,
- to_dev->addr_len, addr_type, 0);
+ err = __hw_addr_add_multiple(&to_dev->dev_addr_list, NULL,
+ &from_dev->dev_addr_list,
+ to_dev->addr_len, addr_type);
if (!err)
call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
return err;
if (from_dev->addr_len != to_dev->addr_len)
return -EINVAL;
- __hw_addr_del_multiple_ii(&to_dev->dev_addr_list,
- &from_dev->dev_addr_list,
- to_dev->addr_len, addr_type, 0);
+ __hw_addr_del_multiple(&to_dev->dev_addr_list, NULL,
+ &from_dev->dev_addr_list,
+ to_dev->addr_len, addr_type);
call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
return 0;
}
* dev_unicast_delete - Release secondary unicast address.
* @dev: device
* @addr: address to delete
- * @alen: length of @addr
*
* Release reference to a secondary unicast address and remove it
* from the device if the reference count drops to zero.
*
* The caller must hold the rtnl_mutex.
*/
-int dev_unicast_delete(struct net_device *dev, void *addr, int alen)
+int dev_unicast_delete(struct net_device *dev, void *addr)
{
int err;
ASSERT_RTNL();
- netif_addr_lock_bh(dev);
- err = __dev_addr_delete(&dev->uc_list, &dev->uc_count, addr, alen, 0);
+ err = __hw_addr_del(&dev->uc_list, &dev->uc_count, addr,
+ dev->addr_len, NETDEV_HW_ADDR_T_UNICAST);
if (!err)
__dev_set_rx_mode(dev);
- netif_addr_unlock_bh(dev);
return err;
}
EXPORT_SYMBOL(dev_unicast_delete);
* dev_unicast_add - add a secondary unicast address
* @dev: device
* @addr: address to add
- * @alen: length of @addr
*
* Add a secondary unicast address to the device or increase
* the reference count if it already exists.
*
* The caller must hold the rtnl_mutex.
*/
-int dev_unicast_add(struct net_device *dev, void *addr, int alen)
+int dev_unicast_add(struct net_device *dev, void *addr)
{
int err;
ASSERT_RTNL();
- netif_addr_lock_bh(dev);
- err = __dev_addr_add(&dev->uc_list, &dev->uc_count, addr, alen, 0);
+ err = __hw_addr_add(&dev->uc_list, &dev->uc_count, addr,
+ dev->addr_len, NETDEV_HW_ADDR_T_UNICAST);
if (!err)
__dev_set_rx_mode(dev);
- netif_addr_unlock_bh(dev);
return err;
}
EXPORT_SYMBOL(dev_unicast_add);
* @from: source device
*
* Add newly added addresses to the destination device and release
- * addresses that have no users left. The source device must be
- * locked by netif_tx_lock_bh.
+ * addresses that have no users left.
*
* This function is intended to be called from the dev->set_rx_mode
* function of layered software devices.
{
int err = 0;
- netif_addr_lock_bh(to);
- err = __dev_addr_sync(&to->uc_list, &to->uc_count,
- &from->uc_list, &from->uc_count);
+ ASSERT_RTNL();
+
+ if (to->addr_len != from->addr_len)
+ return -EINVAL;
+
+ err = __hw_addr_sync(&to->uc_list, &to->uc_count,
+ &from->uc_list, &from->uc_count, to->addr_len);
if (!err)
__dev_set_rx_mode(to);
- netif_addr_unlock_bh(to);
return err;
}
EXPORT_SYMBOL(dev_unicast_sync);
*/
void dev_unicast_unsync(struct net_device *to, struct net_device *from)
{
- netif_addr_lock_bh(from);
- netif_addr_lock(to);
+ ASSERT_RTNL();
- __dev_addr_unsync(&to->uc_list, &to->uc_count,
- &from->uc_list, &from->uc_count);
- __dev_set_rx_mode(to);
+ if (to->addr_len != from->addr_len)
+ return;
- netif_addr_unlock(to);
- netif_addr_unlock_bh(from);
+ __hw_addr_unsync(&to->uc_list, &to->uc_count,
+ &from->uc_list, &from->uc_count, to->addr_len);
+ __dev_set_rx_mode(to);
}
EXPORT_SYMBOL(dev_unicast_unsync);
+static void dev_unicast_flush(struct net_device *dev)
+{
+ /* rtnl_mutex must be held here */
+
+ __hw_addr_flush(&dev->uc_list);
+ dev->uc_count = 0;
+}
+
+static void dev_unicast_init(struct net_device *dev)
+{
+ /* rtnl_mutex must be held here */
+
+ INIT_LIST_HEAD(&dev->uc_list);
+}
+
+
static void __dev_addr_discard(struct dev_addr_list **list)
{
struct dev_addr_list *tmp;
{
netif_addr_lock_bh(dev);
- __dev_addr_discard(&dev->uc_list);
- dev->uc_count = 0;
-
__dev_addr_discard(&dev->mc_list);
dev->mc_count = 0;
switch (cmd) {
case SIOCGIFFLAGS: /* Get interface flags */
- ifr->ifr_flags = dev_get_flags(dev);
+ ifr->ifr_flags = (short) dev_get_flags(dev);
return 0;
case SIOCGIFMETRIC: /* Get the metric on the interface
/*
* Flush the unicast and multicast chains
*/
+ dev_unicast_flush(dev);
dev_addr_discard(dev);
if (dev->netdev_ops->ndo_uninit)
struct netdev_queue *tx;
struct net_device *dev;
size_t alloc_size;
- void *p;
+ struct net_device *p;
BUG_ON(strlen(name) >= sizeof(dev->name));
alloc_size = sizeof(struct net_device);
if (sizeof_priv) {
/* ensure 32-byte alignment of private area */
- alloc_size = (alloc_size + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST;
+ alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
alloc_size += sizeof_priv;
}
/* ensure 32-byte alignment of whole construct */
- alloc_size += NETDEV_ALIGN_CONST;
+ alloc_size += NETDEV_ALIGN - 1;
p = kzalloc(alloc_size, GFP_KERNEL);
if (!p) {
goto free_p;
}
- dev = (struct net_device *)
- (((long)p + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
+ dev = PTR_ALIGN(p, NETDEV_ALIGN);
dev->padded = (char *)dev - (char *)p;
if (dev_addr_init(dev))
goto free_tx;
+ dev_unicast_init(dev);
+
dev_net_set(dev, &init_net);
dev->_tx = tx;
/*
* Flush the unicast and multicast chains
*/
+ dev_unicast_flush(dev);
dev_addr_discard(dev);
netdev_unregister_kobject(dev);
EXPORT_SYMBOL(net_disable_timestamp);
EXPORT_SYMBOL(dev_get_flags);
-#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
-EXPORT_SYMBOL(br_handle_frame_hook);
-EXPORT_SYMBOL(br_fdb_get_hook);
-EXPORT_SYMBOL(br_fdb_put_hook);
-#endif
-
EXPORT_SYMBOL(dev_load);
EXPORT_PER_CPU_SYMBOL(softnet_data);