From 06c4648d46d1b757d6b9591a86810be79818b60c Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 26 May 2010 00:09:42 +0000 Subject: arp_notify: allow drivers to explicitly request a notification event. Currently such notifications are only generated when the device comes up or the address changes. However one use case for these notifications is to enable faster network recovery after a virtual machine migration (by causing switches to relearn their MAC tables). A migration appears to the network stack as a temporary loss of carrier and therefore does not trigger either of the current conditions. Rather than adding carrier up as a trigger (which can cause issues when interfaces a flapping) simply add an interface which the driver can use to explicitly trigger the notification. Signed-off-by: Ian Campbell Cc: Stephen Hemminger Cc: Jeremy Fitzhardinge Cc: David S. Miller Cc: netdev@vger.kernel.org Cc: stable@kernel.org Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 40291f375024..a24916156f4e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1772,6 +1772,8 @@ extern void netif_carrier_on(struct net_device *dev); extern void netif_carrier_off(struct net_device *dev); +extern void netif_notify_peers(struct net_device *dev); + /** * netif_dormant_on - mark device as dormant. * @dev: network device -- cgit v1.2.3 From c2d9ba9bce8d7323ca96f239e1f505c14d6244fb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 1 Jun 2010 06:51:19 +0000 Subject: net: CONFIG_NET_NS reduction Use read_pnet() and write_pnet() to reduce number of ifdef CONFIG_NET_NS Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 6 +----- include/net/cfg80211.h | 15 ++------------- include/net/genetlink.h | 15 ++------------- include/net/netfilter/nf_conntrack.h | 6 +----- include/net/sock.h | 10 ++-------- net/ipv6/addrlabel.c | 6 +----- net/netfilter/nf_conntrack_core.c | 8 ++------ 7 files changed, 11 insertions(+), 55 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a24916156f4e..bd6b75317d5f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1087,11 +1087,7 @@ static inline void netdev_for_each_tx_queue(struct net_device *dev, static inline struct net *dev_net(const struct net_device *dev) { -#ifdef CONFIG_NET_NS - return dev->nd_net; -#else - return &init_net; -#endif + return read_pnet(&dev->nd_net); } static inline diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b44a2e5321a3..e7ebeb8bdf71 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1330,26 +1330,15 @@ struct wiphy { char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; -#ifdef CONFIG_NET_NS -static inline struct net *wiphy_net(struct wiphy *wiphy) -{ - return wiphy->_net; -} - -static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net) -{ - wiphy->_net = net; -} -#else static inline struct net *wiphy_net(struct wiphy *wiphy) { - return &init_net; + return read_pnet(&wiphy->_net); } static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net) { + write_pnet(&wiphy->_net, net); } -#endif /** * wiphy_priv - return priv from wiphy diff --git a/include/net/genetlink.h b/include/net/genetlink.h index eb551baafc04..f7dcd2c70412 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -68,26 +68,15 @@ struct genl_info { #endif }; -#ifdef CONFIG_NET_NS static inline struct net *genl_info_net(struct genl_info *info) { - return info->_net; + return read_pnet(&info->_net); } static inline void genl_info_net_set(struct genl_info *info, struct net *net) { - info->_net = net; + write_pnet(&info->_net, net); } -#else -static inline struct net *genl_info_net(struct genl_info *info) -{ - return &init_net; -} - -static inline void genl_info_net_set(struct genl_info *info, struct net *net) -{ -} -#endif /** * struct genl_ops - generic netlink operations diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index bde095f7e845..bbfdd9453087 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -152,11 +152,7 @@ extern struct net init_net; static inline struct net *nf_ct_net(const struct nf_conn *ct) { -#ifdef CONFIG_NET_NS - return ct->ct_net; -#else - return &init_net; -#endif + return read_pnet(&ct->ct_net); } /* Alter reply tuple (maybe alter helper). */ diff --git a/include/net/sock.h b/include/net/sock.h index ca241ea14875..3461e5d1e9ad 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1724,19 +1724,13 @@ static inline void sk_eat_skb(struct sock *sk, struct sk_buff *skb, int copied_e static inline struct net *sock_net(const struct sock *sk) { -#ifdef CONFIG_NET_NS - return sk->sk_net; -#else - return &init_net; -#endif + return read_pnet(&sk->sk_net); } static inline void sock_net_set(struct sock *sk, struct net *net) { -#ifdef CONFIG_NET_NS - sk->sk_net = net; -#endif + write_pnet(&sk->sk_net, net); } /* diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index 8c4348cb1950..f0e774cea386 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -53,11 +53,7 @@ static struct ip6addrlbl_table static inline struct net *ip6addrlbl_net(const struct ip6addrlbl_entry *lbl) { -#ifdef CONFIG_NET_NS - return lbl->lbl_net; -#else - return &init_net; -#endif + return read_pnet(&lbl->lbl_net); } /* diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index eeeb8bc73982..77288980fae0 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -619,9 +619,7 @@ struct nf_conn *nf_conntrack_alloc(struct net *net, u16 zone, ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev = NULL; /* Don't set timer yet: wait for confirmation */ setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct); -#ifdef CONFIG_NET_NS - ct->ct_net = net; -#endif + write_pnet(&ct->ct_net, net); #ifdef CONFIG_NF_CONNTRACK_ZONES if (zone) { struct nf_conntrack_zone *nf_ct_zone; @@ -1363,9 +1361,7 @@ static int nf_conntrack_init_init_net(void) goto err_extend; #endif /* Set up fake conntrack: to never be deleted, not in any hashes */ -#ifdef CONFIG_NET_NS - nf_conntrack_untracked.ct_net = &init_net; -#endif + write_pnet(&nf_conntrack_untracked.ct_net, &init_net); atomic_set(&nf_conntrack_untracked.ct_general.use, 1); /* - and look it like as a confirmed connection */ set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status); -- cgit v1.2.3 From ab95bfe01f9872459c8678572ccadbf646badad0 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 1 Jun 2010 21:52:08 +0000 Subject: net: replace hooks in __netif_receive_skb V5 What this patch does is it removes two receive frame hooks (for bridge and for macvlan) from __netif_receive_skb. These are replaced them with a single hook for both. It only supports one hook per device because it makes no sense to do bridging and macvlan on the same device. Then a network driver (of virtual netdev like macvlan or bridge) can register an rx_handler for needed net device. Signed-off-by: Jiri Pirko Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 19 +++++--- include/linux/if_bridge.h | 2 - include/linux/if_macvlan.h | 4 -- include/linux/netdevice.h | 7 +++ net/bridge/br.c | 2 - net/bridge/br_if.c | 8 +++ net/bridge/br_input.c | 12 +++-- net/bridge/br_private.h | 3 +- net/core/dev.c | 119 +++++++++++++++++++++------------------------ 9 files changed, 93 insertions(+), 83 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 87e8d4cb4057..53422ce26f7f 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -145,15 +145,16 @@ static void macvlan_broadcast(struct sk_buff *skb, } /* called under rcu_read_lock() from netif_receive_skb */ -static struct sk_buff *macvlan_handle_frame(struct macvlan_port *port, - struct sk_buff *skb) +static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) { + struct macvlan_port *port; const struct ethhdr *eth = eth_hdr(skb); const struct macvlan_dev *vlan; const struct macvlan_dev *src; struct net_device *dev; unsigned int len; + port = rcu_dereference(skb->dev->macvlan_port); if (is_multicast_ether_addr(eth->h_dest)) { src = macvlan_hash_lookup(port, eth->h_source); if (!src) @@ -515,6 +516,7 @@ static int macvlan_port_create(struct net_device *dev) { struct macvlan_port *port; unsigned int i; + int err; if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) return -EINVAL; @@ -528,13 +530,21 @@ static int macvlan_port_create(struct net_device *dev) for (i = 0; i < MACVLAN_HASH_SIZE; i++) INIT_HLIST_HEAD(&port->vlan_hash[i]); rcu_assign_pointer(dev->macvlan_port, port); - return 0; + + err = netdev_rx_handler_register(dev, macvlan_handle_frame); + if (err) { + rcu_assign_pointer(dev->macvlan_port, NULL); + kfree(port); + } + + return err; } static void macvlan_port_destroy(struct net_device *dev) { struct macvlan_port *port = dev->macvlan_port; + netdev_rx_handler_unregister(dev); rcu_assign_pointer(dev->macvlan_port, NULL); synchronize_rcu(); kfree(port); @@ -767,14 +777,12 @@ static int __init macvlan_init_module(void) int err; register_netdevice_notifier(&macvlan_notifier_block); - macvlan_handle_frame_hook = macvlan_handle_frame; err = macvlan_link_register(&macvlan_link_ops); if (err < 0) goto err1; return 0; err1: - macvlan_handle_frame_hook = NULL; unregister_netdevice_notifier(&macvlan_notifier_block); return err; } @@ -782,7 +790,6 @@ err1: static void __exit macvlan_cleanup_module(void) { rtnl_link_unregister(&macvlan_link_ops); - macvlan_handle_frame_hook = NULL; unregister_netdevice_notifier(&macvlan_notifier_block); } diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 938b7e81df95..0d241a5c4909 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -102,8 +102,6 @@ struct __fdb_entry { #include extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *)); -extern struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p, - struct sk_buff *skb); extern int (*br_should_route_hook)(struct sk_buff *skb); #endif diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 9ea047aca795..c26a0e4f0ce8 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -84,8 +84,4 @@ extern int macvlan_link_register(struct rtnl_link_ops *ops); extern netdev_tx_t macvlan_start_xmit(struct sk_buff *skb, struct net_device *dev); - -extern struct sk_buff *(*macvlan_handle_frame_hook)(struct macvlan_port *, - struct sk_buff *); - #endif /* _LINUX_IF_MACVLAN_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index bd6b75317d5f..5156b806924c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -381,6 +381,8 @@ enum gro_result { }; typedef enum gro_result gro_result_t; +typedef struct sk_buff *rx_handler_func_t(struct sk_buff *skb); + extern void __napi_schedule(struct napi_struct *n); static inline int napi_disable_pending(struct napi_struct *n) @@ -957,6 +959,7 @@ struct net_device { #endif struct netdev_queue rx_queue; + rx_handler_func_t *rx_handler; struct netdev_queue *_tx ____cacheline_aligned_in_smp; @@ -1689,6 +1692,10 @@ static inline void napi_free_frags(struct napi_struct *napi) napi->skb = NULL; } +extern int netdev_rx_handler_register(struct net_device *dev, + rx_handler_func_t *rx_handler); +extern void netdev_rx_handler_unregister(struct net_device *dev); + extern void netif_nit_deliver(struct sk_buff *skb); extern int dev_valid_name(const char *name); extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); diff --git a/net/bridge/br.c b/net/bridge/br.c index 76357b547752..c8436fa31344 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -63,7 +63,6 @@ static int __init br_init(void) goto err_out4; brioctl_set(br_ioctl_deviceless_stub); - br_handle_frame_hook = br_handle_frame; #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) br_fdb_test_addr_hook = br_fdb_test_addr; @@ -100,7 +99,6 @@ static void __exit br_deinit(void) br_fdb_test_addr_hook = NULL; #endif - br_handle_frame_hook = NULL; br_fdb_fini(); } diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 18b245e2c00e..d9242342837e 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -147,6 +147,7 @@ static void del_nbp(struct net_bridge_port *p) list_del_rcu(&p->list); + netdev_rx_handler_unregister(dev); rcu_assign_pointer(dev->br_port, NULL); br_multicast_del_port(p); @@ -429,6 +430,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) goto err2; rcu_assign_pointer(dev->br_port, p); + + err = netdev_rx_handler_register(dev, br_handle_frame); + if (err) + goto err3; + dev_disable_lro(dev); list_add_rcu(&p->list, &br->port_list); @@ -451,6 +457,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) br_netpoll_enable(br, dev); return 0; +err3: + rcu_assign_pointer(dev->br_port, NULL); err2: br_fdb_delete_by_port(br, p, 1); err1: diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index d36e700f7a26..99647d8f95c8 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -131,15 +131,19 @@ static inline int is_link_local(const unsigned char *dest) } /* - * Called via br_handle_frame_hook. * Return NULL if skb is handled - * note: already called with rcu_read_lock (preempt_disabled) + * note: already called with rcu_read_lock (preempt_disabled) from + * netif_receive_skb */ -struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb) +struct sk_buff *br_handle_frame(struct sk_buff *skb) { + struct net_bridge_port *p; const unsigned char *dest = eth_hdr(skb)->h_dest; int (*rhook)(struct sk_buff *skb); + if (skb->pkt_type == PACKET_LOOPBACK) + return skb; + if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) goto drop; @@ -147,6 +151,8 @@ struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb) if (!skb) return NULL; + p = rcu_dereference(skb->dev->br_port); + if (unlikely(is_link_local(dest))) { /* Pause frames shouldn't be passed up by driver anyway */ if (skb->protocol == htons(ETH_P_PAUSE)) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 0f4a74bc6a9b..c83519b555bb 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -331,8 +331,7 @@ extern void br_features_recompute(struct net_bridge *br); /* br_input.c */ extern int br_handle_frame_finish(struct sk_buff *skb); -extern struct sk_buff *br_handle_frame(struct net_bridge_port *p, - struct sk_buff *skb); +extern struct sk_buff *br_handle_frame(struct sk_buff *skb); /* br_ioctl.c */ extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); diff --git a/net/core/dev.c b/net/core/dev.c index ffca5c1066fa..ec01a5998d70 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2604,70 +2604,14 @@ static inline int deliver_skb(struct sk_buff *skb, return pt_prev->func(skb, skb->dev, pt_prev, orig_dev); } -#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE) - -#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) +#if (defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)) && \ + (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_GPL(br_fdb_test_addr_hook); #endif -/* - * If bridge module is loaded call bridging hook. - * returns NULL if packet was consumed. - */ -struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p, - struct sk_buff *skb) __read_mostly; -EXPORT_SYMBOL_GPL(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) -{ - struct net_bridge_port *port; - - if (skb->pkt_type == PACKET_LOOPBACK || - (port = rcu_dereference(skb->dev->br_port)) == NULL) - return skb; - - if (*pt_prev) { - *ret = deliver_skb(skb, *pt_prev, orig_dev); - *pt_prev = NULL; - } - - return br_handle_frame_hook(port, skb); -} -#else -#define handle_bridge(skb, pt_prev, ret, orig_dev) (skb) -#endif - -#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE) -struct sk_buff *(*macvlan_handle_frame_hook)(struct macvlan_port *p, - struct sk_buff *skb) __read_mostly; -EXPORT_SYMBOL_GPL(macvlan_handle_frame_hook); - -static inline struct sk_buff *handle_macvlan(struct sk_buff *skb, - struct packet_type **pt_prev, - int *ret, - struct net_device *orig_dev) -{ - struct macvlan_port *port; - - port = rcu_dereference(skb->dev->macvlan_port); - if (!port) - return skb; - - if (*pt_prev) { - *ret = deliver_skb(skb, *pt_prev, orig_dev); - *pt_prev = NULL; - } - return macvlan_handle_frame_hook(port, skb); -} -#else -#define handle_macvlan(skb, pt_prev, ret, orig_dev) (skb) -#endif - #ifdef CONFIG_NET_CLS_ACT /* TODO: Maybe we should just force sch_ingress to be compiled in * when CONFIG_NET_CLS_ACT is? otherwise some useless instructions @@ -2763,6 +2707,47 @@ void netif_nit_deliver(struct sk_buff *skb) rcu_read_unlock(); } +/** + * netdev_rx_handler_register - register receive handler + * @dev: device to register a handler for + * @rx_handler: receive handler to register + * + * Register a receive hander for a device. This handler will then be + * called from __netif_receive_skb. A negative errno code is returned + * on a failure. + * + * The caller must hold the rtnl_mutex. + */ +int netdev_rx_handler_register(struct net_device *dev, + rx_handler_func_t *rx_handler) +{ + ASSERT_RTNL(); + + if (dev->rx_handler) + return -EBUSY; + + rcu_assign_pointer(dev->rx_handler, rx_handler); + + return 0; +} +EXPORT_SYMBOL_GPL(netdev_rx_handler_register); + +/** + * netdev_rx_handler_unregister - unregister receive handler + * @dev: device to unregister a handler from + * + * Unregister a receive hander from a device. + * + * The caller must hold the rtnl_mutex. + */ +void netdev_rx_handler_unregister(struct net_device *dev) +{ + + ASSERT_RTNL(); + rcu_assign_pointer(dev->rx_handler, NULL); +} +EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister); + static inline void skb_bond_set_mac_by_master(struct sk_buff *skb, struct net_device *master) { @@ -2815,6 +2800,7 @@ EXPORT_SYMBOL(__skb_bond_should_drop); static int __netif_receive_skb(struct sk_buff *skb) { struct packet_type *ptype, *pt_prev; + rx_handler_func_t *rx_handler; struct net_device *orig_dev; struct net_device *master; struct net_device *null_or_orig; @@ -2877,12 +2863,17 @@ static int __netif_receive_skb(struct sk_buff *skb) ncls: #endif - skb = handle_bridge(skb, &pt_prev, &ret, orig_dev); - if (!skb) - goto out; - skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev); - if (!skb) - goto out; + /* Handle special case of bridge or macvlan */ + rx_handler = rcu_dereference(skb->dev->rx_handler); + if (rx_handler) { + if (pt_prev) { + ret = deliver_skb(skb, pt_prev, orig_dev); + pt_prev = NULL; + } + skb = rx_handler(skb); + if (!skb) + goto out; + } /* * Make sure frames received on VLAN interfaces stacked on -- cgit v1.2.3 From bb69ae049fcc986fcd742eb90ca0d44a7a49c9f1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 7 Jun 2010 11:42:13 +0000 Subject: anycast: Some RCU conversions - dev_get_by_flags() changed to dev_get_by_flags_rcu() - ipv6_sock_ac_join() dont touch dev & idev refcounts - ipv6_sock_ac_drop() dont touch dev & idev refcounts - ipv6_sock_ac_close() dont touch dev & idev refcounts - ipv6_dev_ac_dec() dount touch idev refcount - ipv6_chk_acast_addr() dont touch idev refcount Signed-off-by: Eric Dumazet CC: Hideaki YOSHIFUJI Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 +-- net/core/dev.c | 14 +++----- net/ipv6/anycast.c | 90 ++++++++++++++++++++++------------------------- 3 files changed, 49 insertions(+), 59 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5156b806924c..c319f28d699d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1271,8 +1271,8 @@ extern void dev_add_pack(struct packet_type *pt); extern void dev_remove_pack(struct packet_type *pt); extern void __dev_remove_pack(struct packet_type *pt); -extern struct net_device *dev_get_by_flags(struct net *net, unsigned short flags, - unsigned short mask); +extern struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short flags, + unsigned short mask); extern struct net_device *dev_get_by_name(struct net *net, const char *name); extern struct net_device *dev_get_by_name_rcu(struct net *net, const char *name); extern struct net_device *__dev_get_by_name(struct net *net, const char *name); diff --git a/net/core/dev.c b/net/core/dev.c index c8d127718ff1..6f330cee79a6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -803,35 +803,31 @@ struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type) EXPORT_SYMBOL(dev_getfirstbyhwtype); /** - * dev_get_by_flags - find any device with given flags + * dev_get_by_flags_rcu - find any device with given flags * @net: the applicable net namespace * @if_flags: IFF_* values * @mask: bitmask of bits in if_flags to check * * Search for any interface with the given flags. Returns NULL if a device - * is not found or a pointer to the device. The device returned has - * had a reference added and the pointer is safe until the user calls - * dev_put to indicate they have finished with it. + * is not found or a pointer to the device. Must be called inside + * rcu_read_lock(), and result refcount is unchanged. */ -struct net_device *dev_get_by_flags(struct net *net, unsigned short if_flags, +struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short if_flags, unsigned short mask) { struct net_device *dev, *ret; ret = NULL; - rcu_read_lock(); for_each_netdev_rcu(net, dev) { if (((dev->flags ^ if_flags) & mask) == 0) { - dev_hold(dev); ret = dev; break; } } - rcu_read_unlock(); return ret; } -EXPORT_SYMBOL(dev_get_by_flags); +EXPORT_SYMBOL(dev_get_by_flags_rcu); /** * dev_valid_name - check if name is okay for network device diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index b5b07054508a..f058fbd808c8 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -77,41 +77,40 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr) pac->acl_next = NULL; ipv6_addr_copy(&pac->acl_addr, addr); + rcu_read_lock(); if (ifindex == 0) { struct rt6_info *rt; rt = rt6_lookup(net, addr, NULL, 0, 0); if (rt) { dev = rt->rt6i_dev; - dev_hold(dev); dst_release(&rt->u.dst); } else if (ishost) { err = -EADDRNOTAVAIL; - goto out_free_pac; + goto error; } else { /* router, no matching interface: just pick one */ - - dev = dev_get_by_flags(net, IFF_UP, IFF_UP|IFF_LOOPBACK); + dev = dev_get_by_flags_rcu(net, IFF_UP, + IFF_UP | IFF_LOOPBACK); } } else - dev = dev_get_by_index(net, ifindex); + dev = dev_get_by_index_rcu(net, ifindex); if (dev == NULL) { err = -ENODEV; - goto out_free_pac; + goto error; } - idev = in6_dev_get(dev); + idev = __in6_dev_get(dev); if (!idev) { if (ifindex) err = -ENODEV; else err = -EADDRNOTAVAIL; - goto out_dev_put; + goto error; } /* reset ishost, now that we have a specific device */ ishost = !idev->cnf.forwarding; - in6_dev_put(idev); pac->acl_ifindex = dev->ifindex; @@ -124,26 +123,22 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr) if (ishost) err = -EADDRNOTAVAIL; if (err) - goto out_dev_put; + goto error; } err = ipv6_dev_ac_inc(dev, addr); - if (err) - goto out_dev_put; - - write_lock_bh(&ipv6_sk_ac_lock); - pac->acl_next = np->ipv6_ac_list; - np->ipv6_ac_list = pac; - write_unlock_bh(&ipv6_sk_ac_lock); - - dev_put(dev); - - return 0; + if (!err) { + write_lock_bh(&ipv6_sk_ac_lock); + pac->acl_next = np->ipv6_ac_list; + np->ipv6_ac_list = pac; + write_unlock_bh(&ipv6_sk_ac_lock); + pac = NULL; + } -out_dev_put: - dev_put(dev); -out_free_pac: - sock_kfree_s(sk, pac, sizeof(*pac)); +error: + rcu_read_unlock(); + if (pac) + sock_kfree_s(sk, pac, sizeof(*pac)); return err; } @@ -176,11 +171,12 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, struct in6_addr *addr) write_unlock_bh(&ipv6_sk_ac_lock); - dev = dev_get_by_index(net, pac->acl_ifindex); - if (dev) { + rcu_read_lock(); + dev = dev_get_by_index_rcu(net, pac->acl_ifindex); + if (dev) ipv6_dev_ac_dec(dev, &pac->acl_addr); - dev_put(dev); - } + rcu_read_unlock(); + sock_kfree_s(sk, pac, sizeof(*pac)); return 0; } @@ -199,13 +195,12 @@ void ipv6_sock_ac_close(struct sock *sk) write_unlock_bh(&ipv6_sk_ac_lock); prev_index = 0; + rcu_read_lock(); while (pac) { struct ipv6_ac_socklist *next = pac->acl_next; if (pac->acl_ifindex != prev_index) { - if (dev) - dev_put(dev); - dev = dev_get_by_index(net, pac->acl_ifindex); + dev = dev_get_by_index_rcu(net, pac->acl_ifindex); prev_index = pac->acl_ifindex; } if (dev) @@ -213,8 +208,7 @@ void ipv6_sock_ac_close(struct sock *sk) sock_kfree_s(sk, pac, sizeof(*pac)); pac = next; } - if (dev) - dev_put(dev); + rcu_read_unlock(); } #if 0 @@ -363,33 +357,32 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr) return 0; } +/* called with rcu_read_lock() */ static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) { - int ret; - struct inet6_dev *idev = in6_dev_get(dev); + struct inet6_dev *idev = __in6_dev_get(dev); + if (idev == NULL) return -ENODEV; - ret = __ipv6_dev_ac_dec(idev, addr); - in6_dev_put(idev); - return ret; + return __ipv6_dev_ac_dec(idev, addr); } /* * check if the interface has this anycast address + * called with rcu_read_lock() */ static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr) { struct inet6_dev *idev; struct ifacaddr6 *aca; - idev = in6_dev_get(dev); + idev = __in6_dev_get(dev); if (idev) { read_lock_bh(&idev->lock); for (aca = idev->ac_list; aca; aca = aca->aca_next) if (ipv6_addr_equal(&aca->aca_addr, addr)) break; read_unlock_bh(&idev->lock); - in6_dev_put(idev); return aca != NULL; } return 0; @@ -403,14 +396,15 @@ int ipv6_chk_acast_addr(struct net *net, struct net_device *dev, { int found = 0; - if (dev) - return ipv6_chk_acast_dev(dev, addr); rcu_read_lock(); - for_each_netdev_rcu(net, dev) - if (ipv6_chk_acast_dev(dev, addr)) { - found = 1; - break; - } + if (dev) + found = ipv6_chk_acast_dev(dev, addr); + else + for_each_netdev_rcu(net, dev) + if (ipv6_chk_acast_dev(dev, addr)) { + found = 1; + break; + } rcu_read_unlock(); return found; } -- cgit v1.2.3 From be1f3c2c027cc5ad735df6a45a542ed1db7ec48b Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 8 Jun 2010 07:19:54 +0000 Subject: net: Enable 64-bit net device statistics on 32-bit architectures Use struct rtnl_link_stats64 as the statistics structure. On 32-bit architectures, insert 32 bits of padding after/before each field of struct net_device_stats to make its layout compatible with struct rtnl_link_stats64. Add an anonymous union in net_device; move stats into the union and add struct rtnl_link_stats64 stats64. Add net_device_ops::ndo_get_stats64, implementations of which will return a pointer to struct rtnl_link_stats64. Drivers that implement this operation must not update the structure asynchronously. Change dev_get_stats() to call ndo_get_stats64 if available, and to return a pointer to struct rtnl_link_stats64. Change callers of dev_get_stats() accordingly. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 13 +++--- include/linux/if_link.h | 3 +- include/linux/netdevice.h | 91 ++++++++++++++++++++++++----------------- net/8021q/vlanproc.c | 13 +++--- net/core/dev.c | 19 +++++---- net/core/net-sysfs.c | 12 +++--- net/core/rtnetlink.c | 6 +-- 7 files changed, 90 insertions(+), 67 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index ac4f94b7da37..a95a41b74b4e 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3804,20 +3804,21 @@ static int bond_close(struct net_device *bond_dev) return 0; } -static struct net_device_stats *bond_get_stats(struct net_device *bond_dev) +static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); - struct net_device_stats *stats = &bond_dev->stats; - struct net_device_stats local_stats; + struct rtnl_link_stats64 *stats = &bond_dev->stats64; + struct rtnl_link_stats64 local_stats; struct slave *slave; int i; - memset(&local_stats, 0, sizeof(struct net_device_stats)); + memset(&local_stats, 0, sizeof(local_stats)); read_lock_bh(&bond->lock); bond_for_each_slave(bond, slave, i) { - const struct net_device_stats *sstats = dev_get_stats(slave->dev); + const struct rtnl_link_stats64 *sstats = + dev_get_stats(slave->dev); local_stats.rx_packets += sstats->rx_packets; local_stats.rx_bytes += sstats->rx_bytes; @@ -4569,7 +4570,7 @@ static const struct net_device_ops bond_netdev_ops = { .ndo_stop = bond_close, .ndo_start_xmit = bond_start_xmit, .ndo_select_queue = bond_select_queue, - .ndo_get_stats = bond_get_stats, + .ndo_get_stats64 = bond_get_stats, .ndo_do_ioctl = bond_do_ioctl, .ndo_set_multicast_list = bond_set_multicast_list, .ndo_change_mtu = bond_change_mtu, diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 85c812db5a3f..7fcad2e1be3d 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -4,7 +4,7 @@ #include #include -/* The struct should be in sync with struct net_device_stats */ +/* This struct should be in sync with struct rtnl_link_stats64 */ struct rtnl_link_stats { __u32 rx_packets; /* total packets received */ __u32 tx_packets; /* total packets transmitted */ @@ -37,6 +37,7 @@ struct rtnl_link_stats { __u32 tx_compressed; }; +/* The main device statistics structure */ struct rtnl_link_stats64 { __u64 rx_packets; /* total packets received */ __u64 tx_packets; /* total packets transmitted */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c319f28d699d..4fbccc5f609a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -159,45 +159,49 @@ static inline bool dev_xmit_complete(int rc) #define MAX_HEADER (LL_MAX_HEADER + 48) #endif -#endif /* __KERNEL__ */ - /* - * Network device statistics. Akin to the 2.0 ether stats but - * with byte counters. + * Old network device statistics. Fields are native words + * (unsigned long) so they can be read and written atomically. + * Each field is padded to 64 bits for compatibility with + * rtnl_link_stats64. */ +#if BITS_PER_LONG == 64 +#define NET_DEVICE_STATS_DEFINE(name) unsigned long name +#elif defined(__LITTLE_ENDIAN) +#define NET_DEVICE_STATS_DEFINE(name) unsigned long name, pad_ ## name +#else +#define NET_DEVICE_STATS_DEFINE(name) unsigned long pad_ ## name, name +#endif + struct net_device_stats { - unsigned long rx_packets; /* total packets received */ - unsigned long tx_packets; /* total packets transmitted */ - unsigned long rx_bytes; /* total bytes received */ - unsigned long tx_bytes; /* total bytes transmitted */ - unsigned long rx_errors; /* bad packets received */ - unsigned long tx_errors; /* packet transmit problems */ - unsigned long rx_dropped; /* no space in linux buffers */ - unsigned long tx_dropped; /* no space available in linux */ - unsigned long multicast; /* multicast packets received */ - unsigned long collisions; - - /* detailed rx_errors: */ - unsigned long rx_length_errors; - unsigned long rx_over_errors; /* receiver ring buff overflow */ - unsigned long rx_crc_errors; /* recved pkt with crc error */ - unsigned long rx_frame_errors; /* recv'd frame alignment error */ - unsigned long rx_fifo_errors; /* recv'r fifo overrun */ - unsigned long rx_missed_errors; /* receiver missed packet */ - - /* detailed tx_errors */ - unsigned long tx_aborted_errors; - unsigned long tx_carrier_errors; - unsigned long tx_fifo_errors; - unsigned long tx_heartbeat_errors; - unsigned long tx_window_errors; - - /* for cslip etc */ - unsigned long rx_compressed; - unsigned long tx_compressed; + NET_DEVICE_STATS_DEFINE(rx_packets); + NET_DEVICE_STATS_DEFINE(tx_packets); + NET_DEVICE_STATS_DEFINE(rx_bytes); + NET_DEVICE_STATS_DEFINE(tx_bytes); + NET_DEVICE_STATS_DEFINE(rx_errors); + NET_DEVICE_STATS_DEFINE(tx_errors); + NET_DEVICE_STATS_DEFINE(rx_dropped); + NET_DEVICE_STATS_DEFINE(tx_dropped); + NET_DEVICE_STATS_DEFINE(multicast); + NET_DEVICE_STATS_DEFINE(collisions); + NET_DEVICE_STATS_DEFINE(rx_length_errors); + NET_DEVICE_STATS_DEFINE(rx_over_errors); + NET_DEVICE_STATS_DEFINE(rx_crc_errors); + NET_DEVICE_STATS_DEFINE(rx_frame_errors); + NET_DEVICE_STATS_DEFINE(rx_fifo_errors); + NET_DEVICE_STATS_DEFINE(rx_missed_errors); + NET_DEVICE_STATS_DEFINE(tx_aborted_errors); + NET_DEVICE_STATS_DEFINE(tx_carrier_errors); + NET_DEVICE_STATS_DEFINE(tx_fifo_errors); + NET_DEVICE_STATS_DEFINE(tx_heartbeat_errors); + NET_DEVICE_STATS_DEFINE(tx_window_errors); + NET_DEVICE_STATS_DEFINE(rx_compressed); + NET_DEVICE_STATS_DEFINE(tx_compressed); }; +#endif /* __KERNEL__ */ + /* Media selection options. */ enum { @@ -662,10 +666,19 @@ struct netdev_rx_queue { * Callback uses when the transmitter has not made any progress * for dev->watchdog ticks. * + * struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev); * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); * Called when a user wants to get the network device usage - * statistics. If not defined, the counters in dev->stats will - * be used. + * statistics. Drivers must do one of the following: + * 1. Define @ndo_get_stats64 to update a rtnl_link_stats64 structure + * (which should normally be dev->stats64) and return a ponter to + * it. The structure must not be changed asynchronously. + * 2. Define @ndo_get_stats to update a net_device_stats64 structure + * (which should normally be dev->stats) and return a pointer to + * it. The structure may be changed asynchronously only if each + * field is written atomically. + * 3. Update dev->stats asynchronously and atomically, and define + * neither operation. * * void (*ndo_vlan_rx_register)(struct net_device *dev, struct vlan_group *grp); * If device support VLAN receive accleration @@ -720,6 +733,7 @@ struct net_device_ops { struct neigh_parms *); void (*ndo_tx_timeout) (struct net_device *dev); + struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev); struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); void (*ndo_vlan_rx_register)(struct net_device *dev, @@ -869,7 +883,10 @@ struct net_device { int ifindex; int iflink; - struct net_device_stats stats; + union { + struct rtnl_link_stats64 stats64; + struct net_device_stats stats; + }; #ifdef CONFIG_WIRELESS_EXT /* List of functions to handle Wireless Extensions (instead of ioctl). @@ -2121,7 +2138,7 @@ extern void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ extern void dev_load(struct net *net, const char *name); extern void dev_mcast_init(void); -extern const struct net_device_stats *dev_get_stats(struct net_device *dev); +extern const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev); extern void dev_txq_stats_fold(const struct net_device *dev, struct net_device_stats *stats); extern int netdev_max_backlog; diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index afead353e215..df56f5ce887c 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c @@ -278,8 +278,9 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset) { struct net_device *vlandev = (struct net_device *) seq->private; const struct vlan_dev_info *dev_info = vlan_dev_info(vlandev); - const struct net_device_stats *stats; + const struct rtnl_link_stats64 *stats; static const char fmt[] = "%30s %12lu\n"; + static const char fmt64[] = "%30s %12llu\n"; int i; if (!is_vlan_dev(vlandev)) @@ -291,12 +292,12 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset) vlandev->name, dev_info->vlan_id, (int)(dev_info->flags & 1), vlandev->priv_flags); - seq_printf(seq, fmt, "total frames received", stats->rx_packets); - seq_printf(seq, fmt, "total bytes received", stats->rx_bytes); - seq_printf(seq, fmt, "Broadcast/Multicast Rcvd", stats->multicast); + seq_printf(seq, fmt64, "total frames received", stats->rx_packets); + seq_printf(seq, fmt64, "total bytes received", stats->rx_bytes); + seq_printf(seq, fmt64, "Broadcast/Multicast Rcvd", stats->multicast); seq_puts(seq, "\n"); - seq_printf(seq, fmt, "total frames transmitted", stats->tx_packets); - seq_printf(seq, fmt, "total bytes transmitted", stats->tx_bytes); + seq_printf(seq, fmt64, "total frames transmitted", stats->tx_packets); + seq_printf(seq, fmt64, "total bytes transmitted", stats->tx_bytes); seq_printf(seq, fmt, "total headroom inc", dev_info->cnt_inc_headroom_on_tx); seq_printf(seq, fmt, "total encap on xmit", diff --git a/net/core/dev.c b/net/core/dev.c index 277844901ce3..a1abc10db08a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3701,10 +3701,10 @@ void dev_seq_stop(struct seq_file *seq, void *v) static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev) { - const struct net_device_stats *stats = dev_get_stats(dev); + const struct rtnl_link_stats64 *stats = dev_get_stats(dev); - seq_printf(seq, "%6s: %7lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu " - "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n", + seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu " + "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n", dev->name, stats->rx_bytes, stats->rx_packets, stats->rx_errors, stats->rx_dropped + stats->rx_missed_errors, @@ -5281,18 +5281,21 @@ EXPORT_SYMBOL(dev_txq_stats_fold); * @dev: device to get statistics from * * Get network statistics from device. The device driver may provide - * its own method by setting dev->netdev_ops->get_stats; otherwise - * the internal statistics structure is used. + * its own method by setting dev->netdev_ops->get_stats64 or + * dev->netdev_ops->get_stats; otherwise the internal statistics + * structure is used. */ -const struct net_device_stats *dev_get_stats(struct net_device *dev) +const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev) { const struct net_device_ops *ops = dev->netdev_ops; + if (ops->ndo_get_stats64) + return ops->ndo_get_stats64(dev); if (ops->ndo_get_stats) - return ops->ndo_get_stats(dev); + return (struct rtnl_link_stats64 *)ops->ndo_get_stats(dev); dev_txq_stats_fold(dev, &dev->stats); - return &dev->stats; + return &dev->stats64; } EXPORT_SYMBOL(dev_get_stats); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 99e7052d7323..ea3bb4c3b87d 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -29,6 +29,7 @@ static const char fmt_hex[] = "%#x\n"; static const char fmt_long_hex[] = "%#lx\n"; static const char fmt_dec[] = "%d\n"; static const char fmt_ulong[] = "%lu\n"; +static const char fmt_u64[] = "%llu\n"; static inline int dev_isalive(const struct net_device *dev) { @@ -324,14 +325,13 @@ static ssize_t netstat_show(const struct device *d, struct net_device *dev = to_net_dev(d); ssize_t ret = -EINVAL; - WARN_ON(offset > sizeof(struct net_device_stats) || - offset % sizeof(unsigned long) != 0); + WARN_ON(offset > sizeof(struct rtnl_link_stats64) || + offset % sizeof(u64) != 0); read_lock(&dev_base_lock); if (dev_isalive(dev)) { - const struct net_device_stats *stats = dev_get_stats(dev); - ret = sprintf(buf, fmt_ulong, - *(unsigned long *)(((u8 *) stats) + offset)); + const struct rtnl_link_stats64 *stats = dev_get_stats(dev); + ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *) stats) + offset)); } read_unlock(&dev_base_lock); return ret; @@ -343,7 +343,7 @@ static ssize_t show_##name(struct device *d, \ struct device_attribute *attr, char *buf) \ { \ return netstat_show(d, attr, buf, \ - offsetof(struct net_device_stats, name)); \ + offsetof(struct rtnl_link_stats64, name)); \ } \ static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1a2af24e9e3d..e645778e9b7e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -579,7 +579,7 @@ static unsigned int rtnl_dev_combine_flags(const struct net_device *dev, } static void copy_rtnl_link_stats(struct rtnl_link_stats *a, - const struct net_device_stats *b) + const struct rtnl_link_stats64 *b) { a->rx_packets = b->rx_packets; a->tx_packets = b->tx_packets; @@ -610,7 +610,7 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a, a->tx_compressed = b->tx_compressed; } -static void copy_rtnl_link_stats64(void *v, const struct net_device_stats *b) +static void copy_rtnl_link_stats64(void *v, const struct rtnl_link_stats64 *b) { struct rtnl_link_stats64 a; @@ -791,7 +791,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, { struct ifinfomsg *ifm; struct nlmsghdr *nlh; - const struct net_device_stats *stats; + const struct rtnl_link_stats64 *stats; struct nlattr *attr; nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags); -- cgit v1.2.3 From 4247e161b12f8dffb7ee3ee07bc5e61f714ebe2d Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 10 Jun 2010 16:12:47 +0000 Subject: netpoll: Add ndo_netpoll_setup This patch adds ndo_netpoll_setup as the initialisation primitive to complement ndo_netpoll_cleanup. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/netpoll.c | 10 ++++++++++ 2 files changed, 12 insertions(+) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4fbccc5f609a..fb20cc55ba52 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -744,6 +744,8 @@ struct net_device_ops { unsigned short vid); #ifdef CONFIG_NET_POLL_CONTROLLER void (*ndo_poll_controller)(struct net_device *dev); + int (*ndo_netpoll_setup)(struct net_device *dev, + struct netpoll_info *info); void (*ndo_netpoll_cleanup)(struct net_device *dev); #endif int (*ndo_set_vf_mac)(struct net_device *dev, diff --git a/net/core/netpoll.c b/net/core/netpoll.c index d10c249bcc8f..7de6dcad5d79 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -698,6 +698,7 @@ int netpoll_setup(struct netpoll *np) struct net_device *ndev = NULL; struct in_device *in_dev; struct netpoll_info *npinfo; + const struct net_device_ops *ops; unsigned long flags; int err; @@ -797,6 +798,13 @@ int netpoll_setup(struct netpoll *np) INIT_DELAYED_WORK(&npinfo->tx_work, queue_process); atomic_set(&npinfo->refcnt, 1); + + ops = np->dev->netdev_ops; + if (ops->ndo_netpoll_setup) { + err = ops->ndo_netpoll_setup(ndev, npinfo); + if (err) + goto free_npinfo; + } } else { npinfo = ndev->npinfo; atomic_inc(&npinfo->refcnt); @@ -817,6 +825,8 @@ int netpoll_setup(struct netpoll *np) return 0; +free_npinfo: + kfree(npinfo); unlock: rtnl_unlock(); put: -- cgit v1.2.3 From 93e2c32b5cb2ad92ceb1d7a4684f20a0d25bf530 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 10 Jun 2010 03:34:59 +0000 Subject: net: add rx_handler data pointer Add possibility to register rx_handler data pointer along with a rx_handler. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 2 +- include/linux/netdevice.h | 4 +++- net/bridge/br_if.c | 2 +- net/core/dev.c | 6 +++++- 4 files changed, 10 insertions(+), 4 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 59c315556a30..87a3bf69c4a3 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -532,7 +532,7 @@ static int macvlan_port_create(struct net_device *dev) INIT_HLIST_HEAD(&port->vlan_hash[i]); rcu_assign_pointer(dev->macvlan_port, port); - err = netdev_rx_handler_register(dev, macvlan_handle_frame); + err = netdev_rx_handler_register(dev, macvlan_handle_frame, NULL); if (err) { rcu_assign_pointer(dev->macvlan_port, NULL); kfree(port); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fb20cc55ba52..361ff1145cf1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -979,6 +979,7 @@ struct net_device { struct netdev_queue rx_queue; rx_handler_func_t *rx_handler; + void *rx_handler_data; struct netdev_queue *_tx ____cacheline_aligned_in_smp; @@ -1712,7 +1713,8 @@ static inline void napi_free_frags(struct napi_struct *napi) } extern int netdev_rx_handler_register(struct net_device *dev, - rx_handler_func_t *rx_handler); + rx_handler_func_t *rx_handler, + void *rx_handler_data); extern void netdev_rx_handler_unregister(struct net_device *dev); extern void netif_nit_deliver(struct sk_buff *skb); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 97ac9da4d76c..0d142ed0bbe3 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -433,7 +433,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) rcu_assign_pointer(dev->br_port, p); - err = netdev_rx_handler_register(dev, br_handle_frame); + err = netdev_rx_handler_register(dev, br_handle_frame, NULL); if (err) goto err4; diff --git a/net/core/dev.c b/net/core/dev.c index a1abc10db08a..abdb19e547a7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2703,6 +2703,7 @@ void netif_nit_deliver(struct sk_buff *skb) * netdev_rx_handler_register - register receive handler * @dev: device to register a handler for * @rx_handler: receive handler to register + * @rx_handler_data: data pointer that is used by rx handler * * Register a receive hander for a device. This handler will then be * called from __netif_receive_skb. A negative errno code is returned @@ -2711,13 +2712,15 @@ void netif_nit_deliver(struct sk_buff *skb) * The caller must hold the rtnl_mutex. */ int netdev_rx_handler_register(struct net_device *dev, - rx_handler_func_t *rx_handler) + rx_handler_func_t *rx_handler, + void *rx_handler_data) { ASSERT_RTNL(); if (dev->rx_handler) return -EBUSY; + rcu_assign_pointer(dev->rx_handler_data, rx_handler_data); rcu_assign_pointer(dev->rx_handler, rx_handler); return 0; @@ -2737,6 +2740,7 @@ void netdev_rx_handler_unregister(struct net_device *dev) ASSERT_RTNL(); rcu_assign_pointer(dev->rx_handler, NULL); + rcu_assign_pointer(dev->rx_handler_data, NULL); } EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister); -- cgit v1.2.3 From a35e2c1b6d90544b3c688783869817628e5f9607 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 15 Jun 2010 03:27:57 +0000 Subject: macvlan: use rx_handler_data pointer to store macvlan_port pointer V2 Register macvlan_port pointer as rx_handler data pointer. As macvlan_port is removed from struct net_device, another netdev priv_flag is added to indicate the device serves as a macvlan port. Signed-off-by: Jiri Pirko Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 28 ++++++++++++++++------------ include/linux/if.h | 1 + include/linux/netdevice.h | 2 -- 3 files changed, 17 insertions(+), 14 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 87a3bf69c4a3..e096875aa055 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -40,6 +40,11 @@ struct macvlan_port { struct rcu_head rcu; }; +#define macvlan_port_get_rcu(dev) \ + ((struct macvlan_port *) rcu_dereference(dev->rx_handler_data)) +#define macvlan_port_get(dev) ((struct macvlan_port *) dev->rx_handler_data) +#define macvlan_port_exists(dev) (dev->priv_flags & IFF_MACVLAN_PORT) + static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port, const unsigned char *addr) { @@ -155,7 +160,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) struct net_device *dev; unsigned int len; - port = rcu_dereference(skb->dev->macvlan_port); + port = macvlan_port_get_rcu(skb->dev); if (is_multicast_ether_addr(eth->h_dest)) { src = macvlan_hash_lookup(port, eth->h_source); if (!src) @@ -530,14 +535,12 @@ static int macvlan_port_create(struct net_device *dev) INIT_LIST_HEAD(&port->vlans); for (i = 0; i < MACVLAN_HASH_SIZE; i++) INIT_HLIST_HEAD(&port->vlan_hash[i]); - rcu_assign_pointer(dev->macvlan_port, port); - err = netdev_rx_handler_register(dev, macvlan_handle_frame, NULL); - if (err) { - rcu_assign_pointer(dev->macvlan_port, NULL); + err = netdev_rx_handler_register(dev, macvlan_handle_frame, port); + if (err) kfree(port); - } + dev->priv_flags |= IFF_MACVLAN_PORT; return err; } @@ -551,10 +554,10 @@ static void macvlan_port_rcu_free(struct rcu_head *head) static void macvlan_port_destroy(struct net_device *dev) { - struct macvlan_port *port = dev->macvlan_port; + struct macvlan_port *port = macvlan_port_get(dev); + dev->priv_flags &= ~IFF_MACVLAN_PORT; netdev_rx_handler_unregister(dev); - rcu_assign_pointer(dev->macvlan_port, NULL); call_rcu(&port->rcu, macvlan_port_rcu_free); } @@ -633,12 +636,12 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, if (!tb[IFLA_ADDRESS]) random_ether_addr(dev->dev_addr); - if (lowerdev->macvlan_port == NULL) { + if (!macvlan_port_exists(lowerdev)) { err = macvlan_port_create(lowerdev); if (err < 0) return err; } - port = lowerdev->macvlan_port; + port = macvlan_port_get(lowerdev); vlan->lowerdev = lowerdev; vlan->dev = dev; @@ -748,10 +751,11 @@ static int macvlan_device_event(struct notifier_block *unused, struct macvlan_dev *vlan, *next; struct macvlan_port *port; - port = dev->macvlan_port; - if (port == NULL) + if (!macvlan_port_exists(dev)) return NOTIFY_DONE; + port = macvlan_port_get(dev); + switch (event) { case NETDEV_CHANGE: list_for_each_entry(vlan, &port->vlans, list) diff --git a/include/linux/if.h b/include/linux/if.h index be350e62a905..31f2e27ebcd0 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -73,6 +73,7 @@ #define IFF_DONT_BRIDGE 0x800 /* disallow bridging this ether dev */ #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 IF_GET_IFACE 0x0001 /* for querying only */ #define IF_GET_PROTO 0x0002 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 361ff1145cf1..5f231de2032f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1049,8 +1049,6 @@ struct net_device { /* bridge stuff */ struct net_bridge_port *br_port; - /* macvlan */ - struct macvlan_port *macvlan_port; /* GARP */ struct garp_port *garp_port; -- cgit v1.2.3 From f350a0a87374418635689471606454abc7beaa3a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 15 Jun 2010 06:50:45 +0000 Subject: 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 Signed-off-by: David S. Miller --- drivers/net/ksz884x.c | 2 +- drivers/staging/batman-adv/hard-interface.c | 2 +- include/linux/if.h | 1 + include/linux/netdevice.h | 2 -- net/bridge/br_fdb.c | 4 ++-- net/bridge/br_if.c | 23 +++++++++++++---------- net/bridge/br_input.c | 9 ++++----- net/bridge/br_netfilter.c | 11 ++++++----- net/bridge/br_netlink.c | 9 +++++---- net/bridge/br_notify.c | 5 +++-- net/bridge/br_private.h | 5 +++++ net/bridge/br_stp_bpdu.c | 5 +++-- net/bridge/netfilter/ebt_redirect.c | 3 ++- net/bridge/netfilter/ebt_ulog.c | 8 +++++--- net/bridge/netfilter/ebtables.c | 11 +++++++---- net/core/dev.c | 3 ++- net/netfilter/nfnetlink_log.c | 6 ++++-- net/netfilter/nfnetlink_queue.c | 6 ++++-- net/wireless/nl80211.c | 2 +- net/wireless/util.c | 4 ++-- 20 files changed, 71 insertions(+), 50 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/drivers/net/ksz884x.c b/drivers/net/ksz884x.c index 7805bbf1d53a..62362b4a8c56 100644 --- a/drivers/net/ksz884x.c +++ b/drivers/net/ksz884x.c @@ -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; diff --git a/drivers/staging/batman-adv/hard-interface.c b/drivers/staging/batman-adv/hard-interface.c index 7a582e80de18..5ede9c255094 100644 --- a/drivers/staging/batman-adv/hard-interface.c +++ b/drivers/staging/batman-adv/hard-interface.c @@ -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; diff --git a/include/linux/if.h b/include/linux/if.h index 31f2e27ebcd0..53558ec59e1b 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -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 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5f231de2032f..a7e0458029b5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -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; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 26637439965b..6818e609b2c0 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -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(); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 0d142ed0bbe3..c03d2c3ff03e 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -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 @@ put_back: /* 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 (!br_port_exists(dev)) + return -EINVAL; - if (!p || p->br != br) + p = br_port_get(dev); + if (p->br != br) return -EINVAL; del_nbp(p); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 99647d8f95c8..f076c9d79d5e 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -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 @@ drop: /* 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 */ diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 0685b2558ab5..f54404ddee53 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -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) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index fe0a79018ab2..4a6a378c84e3 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -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) diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index 717e1fd6133c..404d4e14c6a7 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -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) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 0f5394c4f2f1..f6bc979b1135 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -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; diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index 217bd225a42f..70aecb48fb69 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -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; diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 9e19166ba453..46624bb6d9be 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -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; diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index ae3c7cef1484..26377e96fa1c 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -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'; diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 59ca00e40dec..bcc102e3be4d 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -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) { diff --git a/net/core/dev.c b/net/core/dev.c index abdb19e547a7..5902426ef585 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -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. diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index fc9a211e629e..e0504e90a0f0 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -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) */ diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 12e1ab37fcd8..cc3ae861e8f3 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -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) */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 90ab3c8519be..3a7b8a2f2d5a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -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; } diff --git a/net/wireless/util.c b/net/wireless/util.c index 3416373a9c0c..0c8a1e8b7690 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -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) { -- cgit v1.2.3 From 82695d9b186dcefe9bd119b53521deec20858f19 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 15 Jun 2010 15:08:48 -0700 Subject: net: Fix error in comment on net_device_ops::ndo_get_stats ndo_get_stats still returns struct net_device_stats *; there is no struct net_device_stats64. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a7e0458029b5..398f6c28cf8a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -673,7 +673,7 @@ struct netdev_rx_queue { * 1. Define @ndo_get_stats64 to update a rtnl_link_stats64 structure * (which should normally be dev->stats64) and return a ponter to * it. The structure must not be changed asynchronously. - * 2. Define @ndo_get_stats to update a net_device_stats64 structure + * 2. Define @ndo_get_stats to update a net_device_stats structure * (which should normally be dev->stats) and return a pointer to * it. The structure may be changed asynchronously only if each * field is written atomically. -- cgit v1.2.3 From d29c0c5c332131f1151cf33995e2f01299b9234f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 14 Jun 2010 20:21:04 +0000 Subject: udp: Add UFO to NETIF_F_SOFTWARE_GSO This patch adds UFO to the list of GSO features with a software fallback. This allows UFO to be used even if the hardware does not support it. In particular, this allows us to test the UFO fallback, as it has been reported to not work in some cases. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 398f6c28cf8a..8fa5e5aa879a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -865,7 +865,8 @@ struct net_device { #define NETIF_F_FSO (SKB_GSO_FCOE << NETIF_F_GSO_SHIFT) /* List of features with software fallbacks. */ -#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6) +#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \ + NETIF_F_TSO6 | NETIF_F_UFO) #define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) -- cgit v1.2.3 From 256df2f3879efdb2e9808bdb1b54b16fbb11fa38 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 27 Jun 2010 01:02:35 +0000 Subject: netdevice.h net/core/dev.c: Convert netdev_ logging macros to functions Reduces an x86 defconfig text and data ~2k. text is smaller, data is larger. $ size vmlinux* text data bss dec hex filename 7198862 720112 1366288 9285262 8dae8e vmlinux 7205273 716016 1366288 9287577 8db799 vmlinux.device_h Uses %pV and struct va_format Format arguments are verified before printk Signed-off-by: Joe Perches Acked-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- include/linux/netdevice.h | 36 +++++++++++++-------------- net/core/dev.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 19 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8fa5e5aa879a..0183901ea475 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2250,25 +2250,23 @@ static inline const char *netdev_name(const struct net_device *dev) return dev->name; } -#define netdev_printk(level, netdev, format, args...) \ - dev_printk(level, (netdev)->dev.parent, \ - "%s: " format, \ - netdev_name(netdev), ##args) - -#define netdev_emerg(dev, format, args...) \ - netdev_printk(KERN_EMERG, dev, format, ##args) -#define netdev_alert(dev, format, args...) \ - netdev_printk(KERN_ALERT, dev, format, ##args) -#define netdev_crit(dev, format, args...) \ - netdev_printk(KERN_CRIT, dev, format, ##args) -#define netdev_err(dev, format, args...) \ - netdev_printk(KERN_ERR, dev, format, ##args) -#define netdev_warn(dev, format, args...) \ - netdev_printk(KERN_WARNING, dev, format, ##args) -#define netdev_notice(dev, format, args...) \ - netdev_printk(KERN_NOTICE, dev, format, ##args) -#define netdev_info(dev, format, args...) \ - netdev_printk(KERN_INFO, dev, format, ##args) +extern int netdev_printk(const char *level, const struct net_device *dev, + const char *format, ...) + __attribute__ ((format (printf, 3, 4))); +extern int netdev_emerg(const struct net_device *dev, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); +extern int netdev_alert(const struct net_device *dev, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); +extern int netdev_crit(const struct net_device *dev, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); +extern int netdev_err(const struct net_device *dev, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); +extern int netdev_warn(const struct net_device *dev, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); +extern int netdev_notice(const struct net_device *dev, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); +extern int netdev_info(const struct net_device *dev, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); #if defined(DEBUG) #define netdev_dbg(__dev, format, args...) \ diff --git a/net/core/dev.c b/net/core/dev.c index e85cc5fa3c4e..93b8929fa21d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5802,6 +5802,68 @@ char *netdev_drivername(const struct net_device *dev, char *buffer, int len) return buffer; } +static int __netdev_printk(const char *level, const struct net_device *dev, + struct va_format *vaf) +{ + int r; + + if (dev && dev->dev.parent) + r = dev_printk(level, dev->dev.parent, "%s: %pV", + netdev_name(dev), vaf); + else if (dev) + r = printk("%s%s: %pV", level, netdev_name(dev), vaf); + else + r = printk("%s(NULL net_device): %pV", level, vaf); + + return r; +} + +int netdev_printk(const char *level, const struct net_device *dev, + const char *format, ...) +{ + struct va_format vaf; + va_list args; + int r; + + va_start(args, format); + + vaf.fmt = format; + vaf.va = &args; + + r = __netdev_printk(level, dev, &vaf); + va_end(args); + + return r; +} +EXPORT_SYMBOL(netdev_printk); + +#define define_netdev_printk_level(func, level) \ +int func(const struct net_device *dev, const char *fmt, ...) \ +{ \ + int r; \ + struct va_format vaf; \ + va_list args; \ + \ + va_start(args, fmt); \ + \ + vaf.fmt = fmt; \ + vaf.va = &args; \ + \ + r = __netdev_printk(level, dev, &vaf); \ + va_end(args); \ + \ + return r; \ +} \ +EXPORT_SYMBOL(func); + +define_netdev_printk_level(netdev_emerg, KERN_EMERG); +define_netdev_printk_level(netdev_alert, KERN_ALERT); +define_netdev_printk_level(netdev_crit, KERN_CRIT); +define_netdev_printk_level(netdev_err, KERN_ERR); +define_netdev_printk_level(netdev_warn, KERN_WARNING); +define_netdev_printk_level(netdev_notice, KERN_NOTICE); +define_netdev_printk_level(netdev_info, KERN_INFO); + static void __net_exit netdev_exit(struct net *net) { kfree(net->dev_name_head); -- cgit v1.2.3 From f45f4321d2c977c9eff77e5a5225f3cd2140eb20 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 27 Jun 2010 01:02:36 +0000 Subject: netdevice.h: Change netif_ macros to call netdev_ functions Reduces text ~300 bytes of text (woohoo!) in an x86 defconfig $ size vmlinux* text data bss dec hex filename 7198526 720112 1366288 9284926 8dad3e vmlinux 7198862 720112 1366288 9285262 8dae8e vmlinux.netdev Signed-off-by: Joe Perches Acked-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- include/linux/netdevice.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0183901ea475..4d27368674db 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2314,20 +2314,26 @@ do { \ netdev_printk(level, (dev), fmt, ##args); \ } while (0) +#define netif_level(level, priv, type, dev, fmt, args...) \ +do { \ + if (netif_msg_##type(priv)) \ + netdev_##level(dev, fmt, ##args); \ +} while (0) + #define netif_emerg(priv, type, dev, fmt, args...) \ - netif_printk(priv, type, KERN_EMERG, dev, fmt, ##args) + netif_level(emerg, priv, type, dev, fmt, ##args) #define netif_alert(priv, type, dev, fmt, args...) \ - netif_printk(priv, type, KERN_ALERT, dev, fmt, ##args) + netif_level(alert, priv, type, dev, fmt, ##args) #define netif_crit(priv, type, dev, fmt, args...) \ - netif_printk(priv, type, KERN_CRIT, dev, fmt, ##args) + netif_level(crit, priv, type, dev, fmt, ##args) #define netif_err(priv, type, dev, fmt, args...) \ - netif_printk(priv, type, KERN_ERR, dev, fmt, ##args) + netif_level(err, priv, type, dev, fmt, ##args) #define netif_warn(priv, type, dev, fmt, args...) \ - netif_printk(priv, type, KERN_WARNING, dev, fmt, ##args) + netif_level(warn, priv, type, dev, fmt, ##args) #define netif_notice(priv, type, dev, fmt, args...) \ - netif_printk(priv, type, KERN_NOTICE, dev, fmt, ##args) + netif_level(notice, priv, type, dev, fmt, ##args) #define netif_info(priv, type, dev, fmt, args...) \ - netif_printk(priv, type, KERN_INFO, (dev), fmt, ##args) + netif_level(info, priv, type, dev, fmt, ##args) #if defined(DEBUG) #define netif_dbg(priv, type, dev, format, args...) \ -- cgit v1.2.3 From 28172739f0a276eb8d6ca917b3974c2edb036da3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 7 Jul 2010 14:58:56 -0700 Subject: net: fix 64 bit counters on 32 bit arches There is a small possibility that a reader gets incorrect values on 32 bit arches. SNMP applications could catch incorrect counters when a 32bit high part is changed by another stats consumer/provider. One way to solve this is to add a rtnl_link_stats64 param to all ndo_get_stats64() methods, and also add such a parameter to dev_get_stats(). Rule is that we are not allowed to use dev->stats64 as a temporary storage for 64bit stats, but a caller provided area (usually on stack) Old drivers (only providing get_stats() method) need no changes. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- arch/s390/appldata/appldata_net_sum.c | 3 +- drivers/net/bonding/bond_main.c | 64 ++++++++++++++--------------- drivers/net/ixgbe/ixgbe_ethtool.c | 8 ++-- drivers/net/loopback.c | 4 +- drivers/net/macvlan.c | 6 +-- drivers/net/sfc/efx.c | 3 +- drivers/net/sfc/ethtool.c | 3 +- drivers/parisc/led.c | 3 +- drivers/scsi/fcoe/fcoe.c | 3 +- drivers/staging/batman-adv/hard-interface.c | 3 +- drivers/usb/gadget/rndis.c | 3 +- include/linux/netdevice.h | 12 ++++-- net/8021q/vlan_dev.c | 6 +-- net/8021q/vlanproc.c | 3 +- net/bridge/br_device.c | 4 +- net/core/dev.c | 25 +++++++---- net/core/net-sysfs.c | 4 +- net/core/rtnetlink.c | 3 +- 18 files changed, 89 insertions(+), 71 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c index 9a9586f4103f..f02e89ce4df1 100644 --- a/arch/s390/appldata/appldata_net_sum.c +++ b/arch/s390/appldata/appldata_net_sum.c @@ -85,7 +85,8 @@ static void appldata_get_net_sum_data(void *data) rcu_read_lock(); for_each_netdev_rcu(&init_net, dev) { - const struct net_device_stats *stats = dev_get_stats(dev); + struct rtnl_link_stats64 temp; + const struct net_device_stats *stats = dev_get_stats(dev, &temp); rx_packets += stats->rx_packets; tx_packets += stats->tx_packets; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index a95a41b74b4e..9bb9bfa225b6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3804,51 +3804,49 @@ static int bond_close(struct net_device *bond_dev) return 0; } -static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev) +static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev, + struct rtnl_link_stats64 *stats) { struct bonding *bond = netdev_priv(bond_dev); - struct rtnl_link_stats64 *stats = &bond_dev->stats64; - struct rtnl_link_stats64 local_stats; + struct rtnl_link_stats64 temp; struct slave *slave; int i; - memset(&local_stats, 0, sizeof(local_stats)); + memset(stats, 0, sizeof(*stats)); read_lock_bh(&bond->lock); bond_for_each_slave(bond, slave, i) { const struct rtnl_link_stats64 *sstats = - dev_get_stats(slave->dev); - - local_stats.rx_packets += sstats->rx_packets; - local_stats.rx_bytes += sstats->rx_bytes; - local_stats.rx_errors += sstats->rx_errors; - local_stats.rx_dropped += sstats->rx_dropped; - - local_stats.tx_packets += sstats->tx_packets; - local_stats.tx_bytes += sstats->tx_bytes; - local_stats.tx_errors += sstats->tx_errors; - local_stats.tx_dropped += sstats->tx_dropped; - - local_stats.multicast += sstats->multicast; - local_stats.collisions += sstats->collisions; - - local_stats.rx_length_errors += sstats->rx_length_errors; - local_stats.rx_over_errors += sstats->rx_over_errors; - local_stats.rx_crc_errors += sstats->rx_crc_errors; - local_stats.rx_frame_errors += sstats->rx_frame_errors; - local_stats.rx_fifo_errors += sstats->rx_fifo_errors; - local_stats.rx_missed_errors += sstats->rx_missed_errors; - - local_stats.tx_aborted_errors += sstats->tx_aborted_errors; - local_stats.tx_carrier_errors += sstats->tx_carrier_errors; - local_stats.tx_fifo_errors += sstats->tx_fifo_errors; - local_stats.tx_heartbeat_errors += sstats->tx_heartbeat_errors; - local_stats.tx_window_errors += sstats->tx_window_errors; + dev_get_stats(slave->dev, &temp); + + stats->rx_packets += sstats->rx_packets; + stats->rx_bytes += sstats->rx_bytes; + stats->rx_errors += sstats->rx_errors; + stats->rx_dropped += sstats->rx_dropped; + + stats->tx_packets += sstats->tx_packets; + stats->tx_bytes += sstats->tx_bytes; + stats->tx_errors += sstats->tx_errors; + stats->tx_dropped += sstats->tx_dropped; + + stats->multicast += sstats->multicast; + stats->collisions += sstats->collisions; + + stats->rx_length_errors += sstats->rx_length_errors; + stats->rx_over_errors += sstats->rx_over_errors; + stats->rx_crc_errors += sstats->rx_crc_errors; + stats->rx_frame_errors += sstats->rx_frame_errors; + stats->rx_fifo_errors += sstats->rx_fifo_errors; + stats->rx_missed_errors += sstats->rx_missed_errors; + + stats->tx_aborted_errors += sstats->tx_aborted_errors; + stats->tx_carrier_errors += sstats->tx_carrier_errors; + stats->tx_fifo_errors += sstats->tx_fifo_errors; + stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors; + stats->tx_window_errors += sstats->tx_window_errors; } - memcpy(stats, &local_stats, sizeof(struct net_device_stats)); - read_unlock_bh(&bond->lock); return stats; diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index b35ef36741ef..da54b38bb480 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -55,7 +55,7 @@ struct ixgbe_stats { offsetof(struct ixgbe_adapter, m) #define IXGBE_NETDEV_STAT(m) NETDEV_STATS, \ sizeof(((struct net_device *)0)->m), \ - offsetof(struct net_device, m) + offsetof(struct net_device, m) - offsetof(struct net_device, stats) static struct ixgbe_stats ixgbe_gstrings_stats[] = { {"rx_packets", IXGBE_NETDEV_STAT(stats.rx_packets)}, @@ -998,16 +998,18 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, struct ixgbe_adapter *adapter = netdev_priv(netdev); u64 *queue_stat; int stat_count = sizeof(struct ixgbe_queue_stats) / sizeof(u64); + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *net_stats; int j, k; int i; char *p = NULL; ixgbe_update_stats(adapter); - dev_get_stats(netdev); + net_stats = dev_get_stats(netdev, &temp); for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) { switch (ixgbe_gstrings_stats[i].type) { case NETDEV_STATS: - p = (char *) netdev + + p = (char *) net_stats + ixgbe_gstrings_stats[i].stat_offset; break; case IXGBE_STATS: diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 4dd0510d7a99..9a0996795321 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -98,10 +98,10 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } -static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev) +static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) { const struct pcpu_lstats __percpu *pcpu_lstats; - struct rtnl_link_stats64 *stats = &dev->stats64; u64 bytes = 0; u64 packets = 0; u64 drops = 0; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index e6d626e78515..6112f1498940 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -431,12 +431,12 @@ static void macvlan_uninit(struct net_device *dev) free_percpu(vlan->rx_stats); } -static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev) +static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) { - struct rtnl_link_stats64 *stats = &dev->stats64; struct macvlan_dev *vlan = netdev_priv(dev); - dev_txq_stats_fold(dev, &dev->stats); + dev_txq_stats_fold(dev, (struct net_device_stats *)stats); if (vlan->rx_stats) { struct macvlan_rx_stats *p, accum = {0}; diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 35b3f2922e5c..ba674c5ca29e 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -1533,11 +1533,10 @@ static int efx_net_stop(struct net_device *net_dev) } /* Context: process, dev_base_lock or RTNL held, non-blocking. */ -static struct rtnl_link_stats64 *efx_net_stats(struct net_device *net_dev) +static struct rtnl_link_stats64 *efx_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats) { struct efx_nic *efx = netdev_priv(net_dev); struct efx_mac_stats *mac_stats = &efx->mac_stats; - struct rtnl_link_stats64 *stats = &net_dev->stats64; spin_lock_bh(&efx->stats_lock); efx->type->update_stats(efx); diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 3b8b0a062749..fd19d6ab97a2 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -469,12 +469,13 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, struct efx_mac_stats *mac_stats = &efx->mac_stats; struct efx_ethtool_stat *stat; struct efx_channel *channel; + struct rtnl_link_stats64 temp; int i; EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS); /* Update MAC and NIC statistics */ - dev_get_stats(net_dev); + dev_get_stats(net_dev, &temp); /* Fill detailed statistics buffer */ for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) { diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c index 188bc8496a26..18dff43b8bd2 100644 --- a/drivers/parisc/led.c +++ b/drivers/parisc/led.c @@ -355,12 +355,13 @@ static __inline__ int led_get_net_activity(void) rcu_read_lock(); for_each_netdev_rcu(&init_net, dev) { const struct net_device_stats *stats; + struct rtnl_link_stats64 temp; struct in_device *in_dev = __in_dev_get_rcu(dev); if (!in_dev || !in_dev->ifa_list) continue; if (ipv4_is_loopback(in_dev->ifa_list->ifa_local)) continue; - stats = dev_get_stats(dev); + stats = dev_get_stats(dev, &temp); rx_total += stats->rx_packets; tx_total += stats->tx_packets; } diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 44a07593de56..1a429ed6da9d 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -2653,6 +2653,7 @@ static void fcoe_get_lesb(struct fc_lport *lport, u32 lfc, vlfc, mdac; struct fcoe_dev_stats *devst; struct fcoe_fc_els_lesb *lesb; + struct rtnl_link_stats64 temp; struct net_device *netdev = fcoe_netdev(lport); lfc = 0; @@ -2669,7 +2670,7 @@ static void fcoe_get_lesb(struct fc_lport *lport, lesb->lesb_link_fail = htonl(lfc); lesb->lesb_vlink_fail = htonl(vlfc); lesb->lesb_miss_fka = htonl(mdac); - lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors); + lesb->lesb_fcs_error = htonl(dev_get_stats(netdev, &temp)->rx_crc_errors); } /** diff --git a/drivers/staging/batman-adv/hard-interface.c b/drivers/staging/batman-adv/hard-interface.c index 5ede9c255094..96c86c873011 100644 --- a/drivers/staging/batman-adv/hard-interface.c +++ b/drivers/staging/batman-adv/hard-interface.c @@ -440,6 +440,7 @@ int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, struct batman_packet *batman_packet; struct batman_if *batman_if; struct net_device_stats *stats; + struct rtnl_link_stats64 temp; int ret; skb = skb_share_check(skb, GFP_ATOMIC); @@ -468,7 +469,7 @@ int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, if (batman_if->if_status != IF_ACTIVE) goto err_free; - stats = (struct net_device_stats *)dev_get_stats(skb->dev); + stats = (struct net_device_stats *)dev_get_stats(skb->dev, &temp); if (stats) { stats->rx_packets++; stats->rx_bytes += skb->len; diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index fb69b01c8f3a..020fa5a25fda 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -171,6 +171,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, int i, count; rndis_query_cmplt_type *resp; struct net_device *net; + struct rtnl_link_stats64 temp; const struct rtnl_link_stats64 *stats; if (!r) return -ENOMEM; @@ -194,7 +195,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, resp->InformationBufferOffset = cpu_to_le32 (16); net = rndis_per_dev_params[configNr].dev; - stats = dev_get_stats(net); + stats = dev_get_stats(net, &temp); switch (OID) { diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4d27368674db..60de65316fdb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -666,7 +666,8 @@ struct netdev_rx_queue { * Callback uses when the transmitter has not made any progress * for dev->watchdog ticks. * - * struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev); + * struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev + * struct rtnl_link_stats64 *storage); * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); * Called when a user wants to get the network device usage * statistics. Drivers must do one of the following: @@ -733,7 +734,8 @@ struct net_device_ops { struct neigh_parms *); void (*ndo_tx_timeout) (struct net_device *dev); - struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev); + struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev, + struct rtnl_link_stats64 *storage); struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); void (*ndo_vlan_rx_register)(struct net_device *dev, @@ -2139,8 +2141,10 @@ extern void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ extern void dev_load(struct net *net, const char *name); extern void dev_mcast_init(void); -extern const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev); -extern void dev_txq_stats_fold(const struct net_device *dev, struct net_device_stats *stats); +extern const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *storage); +extern void dev_txq_stats_fold(const struct net_device *dev, + struct net_device_stats *stats); extern int netdev_max_backlog; extern int netdev_tstamp_prequeue; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index c6456cb842fa..7865a4ce5250 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -803,11 +803,9 @@ static u32 vlan_ethtool_get_flags(struct net_device *dev) return dev_ethtool_get_flags(vlan->real_dev); } -static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev) +static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { - struct rtnl_link_stats64 *stats = &dev->stats64; - - dev_txq_stats_fold(dev, &dev->stats); + dev_txq_stats_fold(dev, (struct net_device_stats *)stats); if (vlan_dev_info(dev)->vlan_rx_stats) { struct vlan_rx_stats *p, accum = {0}; diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index df56f5ce887c..80e280f56686 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c @@ -278,6 +278,7 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset) { struct net_device *vlandev = (struct net_device *) seq->private; const struct vlan_dev_info *dev_info = vlan_dev_info(vlandev); + struct rtnl_link_stats64 temp; const struct rtnl_link_stats64 *stats; static const char fmt[] = "%30s %12lu\n"; static const char fmt64[] = "%30s %12llu\n"; @@ -286,7 +287,7 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset) if (!is_vlan_dev(vlandev)) return 0; - stats = dev_get_stats(vlandev); + stats = dev_get_stats(vlandev, &temp); seq_printf(seq, "%s VID: %d REORDER_HDR: %i dev->priv_flags: %hx\n", vlandev->name, dev_info->vlan_id, diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index edf639e96281..075c435ad22d 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -98,10 +98,10 @@ static int br_dev_stop(struct net_device *dev) return 0; } -static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev) +static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) { struct net_bridge *br = netdev_priv(dev); - struct rtnl_link_stats64 *stats = &dev->stats64; struct br_cpu_netstats tmp, sum = { 0 }; unsigned int cpu; diff --git a/net/core/dev.c b/net/core/dev.c index 93b8929fa21d..92482d7a87a9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3703,7 +3703,8 @@ void dev_seq_stop(struct seq_file *seq, void *v) static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev) { - const struct rtnl_link_stats64 *stats = dev_get_stats(dev); + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu " "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n", @@ -5281,23 +5282,29 @@ EXPORT_SYMBOL(dev_txq_stats_fold); /** * dev_get_stats - get network device statistics * @dev: device to get statistics from + * @storage: place to store stats * * Get network statistics from device. The device driver may provide * its own method by setting dev->netdev_ops->get_stats64 or * dev->netdev_ops->get_stats; otherwise the internal statistics * structure is used. */ -const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev) +const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *storage) { const struct net_device_ops *ops = dev->netdev_ops; - if (ops->ndo_get_stats64) - return ops->ndo_get_stats64(dev); - if (ops->ndo_get_stats) - return (struct rtnl_link_stats64 *)ops->ndo_get_stats(dev); - - dev_txq_stats_fold(dev, &dev->stats); - return &dev->stats64; + if (ops->ndo_get_stats64) { + memset(storage, 0, sizeof(*storage)); + return ops->ndo_get_stats64(dev, storage); + } + if (ops->ndo_get_stats) { + memcpy(storage, ops->ndo_get_stats(dev), sizeof(*storage)); + return storage; + } + memcpy(storage, &dev->stats, sizeof(*storage)); + dev_txq_stats_fold(dev, (struct net_device_stats *)storage); + return storage; } EXPORT_SYMBOL(dev_get_stats); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index ea3bb4c3b87d..914f42b0f039 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -330,7 +330,9 @@ static ssize_t netstat_show(const struct device *d, read_lock(&dev_base_lock); if (dev_isalive(dev)) { - const struct rtnl_link_stats64 *stats = dev_get_stats(dev); + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); + ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *) stats) + offset)); } read_unlock(&dev_base_lock); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index e645778e9b7e..5e773ea2201d 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -791,6 +791,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, { struct ifinfomsg *ifm; struct nlmsghdr *nlh; + struct rtnl_link_stats64 temp; const struct rtnl_link_stats64 *stats; struct nlattr *attr; @@ -847,7 +848,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (attr == NULL) goto nla_put_failure; - stats = dev_get_stats(dev); + stats = dev_get_stats(dev, &temp); copy_rtnl_link_stats(nla_data(attr), stats); attr = nla_reserve(skb, IFLA_STATS64, -- cgit v1.2.3 From 3cfde79c6c7c8002375c4a8e5be7f602fbb9675d Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 9 Jul 2010 09:11:52 +0000 Subject: net: Get rid of rtnl_link_stats64 / net_device_stats union In commit be1f3c2c027cc5ad735df6a45a542ed1db7ec48b "net: Enable 64-bit net device statistics on 32-bit architectures" I redefined struct net_device_stats so that it could be used in a union with struct rtnl_link_stats64, avoiding the need for explicit copying or conversion between the two. However, this is unsafe because there is no locking required and no lock consistently held around calls to dev_get_stats() and use of the statistics structure it returns. In commit 28172739f0a276eb8d6ca917b3974c2edb036da3 "net: fix 64 bit counters on 32 bit arches" Eric Dumazet dealt with that problem by requiring callers of dev_get_stats() to provide storage for the result. This means that the net_device::stats64 field and the padding in struct net_device_stats are now redundant, so remove them. Update the comment on net_device_ops::ndo_get_stats64 to reflect its new usage. Change dev_txq_stats_fold() to use struct rtnl_link_stats64, since that is what all its callers are really using and it is no longer going to be compatible with struct net_device_stats. Eric Dumazet suggested the separate function for the structure conversion. Signed-off-by: Ben Hutchings Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 2 +- include/linux/netdevice.h | 70 +++++++++++++++++++---------------------------- net/8021q/vlan_dev.c | 2 +- net/core/dev.c | 31 +++++++++++++++++---- 4 files changed, 56 insertions(+), 49 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 6112f1498940..1b28aaec0a5a 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -436,7 +436,7 @@ static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev, { struct macvlan_dev *vlan = netdev_priv(dev); - dev_txq_stats_fold(dev, (struct net_device_stats *)stats); + dev_txq_stats_fold(dev, stats); if (vlan->rx_stats) { struct macvlan_rx_stats *p, accum = {0}; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8018f6bf3051..17e95e37aed9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -162,42 +162,32 @@ static inline bool dev_xmit_complete(int rc) /* * Old network device statistics. Fields are native words * (unsigned long) so they can be read and written atomically. - * Each field is padded to 64 bits for compatibility with - * rtnl_link_stats64. */ -#if BITS_PER_LONG == 64 -#define NET_DEVICE_STATS_DEFINE(name) unsigned long name -#elif defined(__LITTLE_ENDIAN) -#define NET_DEVICE_STATS_DEFINE(name) unsigned long name, pad_ ## name -#else -#define NET_DEVICE_STATS_DEFINE(name) unsigned long pad_ ## name, name -#endif - struct net_device_stats { - NET_DEVICE_STATS_DEFINE(rx_packets); - NET_DEVICE_STATS_DEFINE(tx_packets); - NET_DEVICE_STATS_DEFINE(rx_bytes); - NET_DEVICE_STATS_DEFINE(tx_bytes); - NET_DEVICE_STATS_DEFINE(rx_errors); - NET_DEVICE_STATS_DEFINE(tx_errors); - NET_DEVICE_STATS_DEFINE(rx_dropped); - NET_DEVICE_STATS_DEFINE(tx_dropped); - NET_DEVICE_STATS_DEFINE(multicast); - NET_DEVICE_STATS_DEFINE(collisions); - NET_DEVICE_STATS_DEFINE(rx_length_errors); - NET_DEVICE_STATS_DEFINE(rx_over_errors); - NET_DEVICE_STATS_DEFINE(rx_crc_errors); - NET_DEVICE_STATS_DEFINE(rx_frame_errors); - NET_DEVICE_STATS_DEFINE(rx_fifo_errors); - NET_DEVICE_STATS_DEFINE(rx_missed_errors); - NET_DEVICE_STATS_DEFINE(tx_aborted_errors); - NET_DEVICE_STATS_DEFINE(tx_carrier_errors); - NET_DEVICE_STATS_DEFINE(tx_fifo_errors); - NET_DEVICE_STATS_DEFINE(tx_heartbeat_errors); - NET_DEVICE_STATS_DEFINE(tx_window_errors); - NET_DEVICE_STATS_DEFINE(rx_compressed); - NET_DEVICE_STATS_DEFINE(tx_compressed); + unsigned long rx_packets; + unsigned long tx_packets; + unsigned long rx_bytes; + unsigned long tx_bytes; + unsigned long rx_errors; + unsigned long tx_errors; + unsigned long rx_dropped; + unsigned long tx_dropped; + unsigned long multicast; + unsigned long collisions; + unsigned long rx_length_errors; + unsigned long rx_over_errors; + unsigned long rx_crc_errors; + unsigned long rx_frame_errors; + unsigned long rx_fifo_errors; + unsigned long rx_missed_errors; + unsigned long tx_aborted_errors; + unsigned long tx_carrier_errors; + unsigned long tx_fifo_errors; + unsigned long tx_heartbeat_errors; + unsigned long tx_window_errors; + unsigned long rx_compressed; + unsigned long tx_compressed; }; #endif /* __KERNEL__ */ @@ -666,14 +656,13 @@ struct netdev_rx_queue { * Callback uses when the transmitter has not made any progress * for dev->watchdog ticks. * - * struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev + * struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev, * struct rtnl_link_stats64 *storage); * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); * Called when a user wants to get the network device usage * statistics. Drivers must do one of the following: - * 1. Define @ndo_get_stats64 to update a rtnl_link_stats64 structure - * (which should normally be dev->stats64) and return a ponter to - * it. The structure must not be changed asynchronously. + * 1. Define @ndo_get_stats64 to fill in a zero-initialised + * rtnl_link_stats64 structure passed by the caller. * 2. Define @ndo_get_stats to update a net_device_stats structure * (which should normally be dev->stats) and return a pointer to * it. The structure may be changed asynchronously only if each @@ -888,10 +877,7 @@ struct net_device { int ifindex; int iflink; - union { - struct rtnl_link_stats64 stats64; - struct net_device_stats stats; - }; + struct net_device_stats stats; #ifdef CONFIG_WIRELESS_EXT /* List of functions to handle Wireless Extensions (instead of ioctl). @@ -2147,7 +2133,7 @@ extern void dev_mcast_init(void); extern const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage); extern void dev_txq_stats_fold(const struct net_device *dev, - struct net_device_stats *stats); + struct rtnl_link_stats64 *stats); extern int netdev_max_backlog; extern int netdev_tstamp_prequeue; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index a1b8171cfa7b..7cb285f96b99 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -805,7 +805,7 @@ static u32 vlan_ethtool_get_flags(struct net_device *dev) static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { - dev_txq_stats_fold(dev, (struct net_device_stats *)stats); + dev_txq_stats_fold(dev, stats); if (vlan_dev_info(dev)->vlan_rx_stats) { struct vlan_rx_stats *p, accum = {0}; diff --git a/net/core/dev.c b/net/core/dev.c index eb4201cf9c8c..79ee26ef5095 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5274,10 +5274,10 @@ void netdev_run_todo(void) /** * dev_txq_stats_fold - fold tx_queues stats * @dev: device to get statistics from - * @stats: struct net_device_stats to hold results + * @stats: struct rtnl_link_stats64 to hold results */ void dev_txq_stats_fold(const struct net_device *dev, - struct net_device_stats *stats) + struct rtnl_link_stats64 *stats) { unsigned long tx_bytes = 0, tx_packets = 0, tx_dropped = 0; unsigned int i; @@ -5297,6 +5297,27 @@ void dev_txq_stats_fold(const struct net_device *dev, } EXPORT_SYMBOL(dev_txq_stats_fold); +/* Convert net_device_stats to rtnl_link_stats64. They have the same + * fields in the same order, with only the type differing. + */ +static void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, + const struct net_device_stats *netdev_stats) +{ +#if BITS_PER_LONG == 64 + BUILD_BUG_ON(sizeof(*stats64) != sizeof(*netdev_stats)); + memcpy(stats64, netdev_stats, sizeof(*stats64)); +#else + size_t i, n = sizeof(*stats64) / sizeof(u64); + const unsigned long *src = (const unsigned long *)netdev_stats; + u64 *dst = (u64 *)stats64; + + BUILD_BUG_ON(sizeof(*netdev_stats) / sizeof(unsigned long) != + sizeof(*stats64) / sizeof(u64)); + for (i = 0; i < n; i++) + dst[i] = src[i]; +#endif +} + /** * dev_get_stats - get network device statistics * @dev: device to get statistics from @@ -5317,11 +5338,11 @@ const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, return ops->ndo_get_stats64(dev, storage); } if (ops->ndo_get_stats) { - memcpy(storage, ops->ndo_get_stats(dev), sizeof(*storage)); + netdev_stats_to_stats64(storage, ops->ndo_get_stats(dev)); return storage; } - memcpy(storage, &dev->stats, sizeof(*storage)); - dev_txq_stats_fold(dev, (struct net_device_stats *)storage); + netdev_stats_to_stats64(storage, &dev->stats); + dev_txq_stats_fold(dev, storage); return storage; } EXPORT_SYMBOL(dev_get_stats); -- cgit v1.2.3 From d77535162e736c47978d5c01469c56e1781dc91b Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 9 Jul 2010 09:12:41 +0000 Subject: net: Document that dev_get_stats() returns the given pointer Document that dev_get_stats() returns the same stats pointer it was given. Remove const qualification from the returned pointer since the caller may do what it likes with that structure. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- net/core/dev.c | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 17e95e37aed9..c4fedf000541 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2130,8 +2130,8 @@ extern void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ extern void dev_load(struct net *net, const char *name); extern void dev_mcast_init(void); -extern const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, - struct rtnl_link_stats64 *storage); +extern struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *storage); extern void dev_txq_stats_fold(const struct net_device *dev, struct rtnl_link_stats64 *stats); diff --git a/net/core/dev.c b/net/core/dev.c index 79ee26ef5095..e2b9fa2c917e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5323,13 +5323,13 @@ static void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, * @dev: device to get statistics from * @storage: place to store stats * - * Get network statistics from device. The device driver may provide - * its own method by setting dev->netdev_ops->get_stats64 or - * dev->netdev_ops->get_stats; otherwise the internal statistics - * structure is used. + * Get network statistics from device. Return @storage. + * The device driver may provide its own method by setting + * dev->netdev_ops->get_stats64 or dev->netdev_ops->get_stats; + * otherwise the internal statistics structure is used. */ -const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, - struct rtnl_link_stats64 *storage) +struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *storage) { const struct net_device_ops *ops = dev->netdev_ops; -- cgit v1.2.3 From c1f19b51d1d87f3e3bb7e6648f43f7d57ed2da6b Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sat, 17 Jul 2010 08:49:36 +0000 Subject: net: support time stamping in phy devices. This patch adds a new networking option to allow hardware time stamps from PHY devices. When enabled, likely candidates among incoming and outgoing network packets are offered to the PHY driver for possible time stamping. When accepted by the PHY driver, incoming packets are deferred for later delivery by the driver. The patch also adds phylib driver methods for the SIOCSHWTSTAMP ioctl and callbacks for transmit and receive time stamping. Drivers may optionally implement these functions. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 5 ++ drivers/net/phy/phy_device.c | 2 + include/linux/netdevice.h | 4 ++ include/linux/phy.h | 22 ++++++++ include/linux/skbuff.h | 31 +++++++++++ net/Kconfig | 10 ++++ net/core/Makefile | 2 +- net/core/dev.c | 3 ++ net/core/timestamping.c | 126 +++++++++++++++++++++++++++++++++++++++++++ net/socket.c | 4 ++ 10 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 net/core/timestamping.c (limited to 'include/linux/netdevice.h') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index bd88d818f082..5130db8f5c4e 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -361,6 +361,11 @@ int phy_mii_ioctl(struct phy_device *phydev, } break; + case SIOCSHWTSTAMP: + if (phydev->drv->hwtstamp) + return phydev->drv->hwtstamp(phydev, ifr); + /* fall through */ + default: return -EOPNOTSUPP; } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 1a99bb244106..c0761197c07e 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -460,6 +460,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, } phydev->attached_dev = dev; + dev->phydev = phydev; phydev->dev_flags = flags; @@ -513,6 +514,7 @@ EXPORT_SYMBOL(phy_attach); */ void phy_detach(struct phy_device *phydev) { + phydev->attached_dev->phydev = NULL; phydev->attached_dev = NULL; /* If the device had no specific driver before (i.e. - it diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c4fedf000541..fdc3f2992230 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -54,6 +54,7 @@ struct vlan_group; struct netpoll_info; +struct phy_device; /* 802.11 specific */ struct wireless_dev; /* source back-compat hooks */ @@ -1065,6 +1066,9 @@ struct net_device { #endif /* n-tuple filter list attached to this device */ struct ethtool_rx_ntuple_list ethtool_ntuple_list; + + /* phy device may attach itself for hardware timestamping */ + struct phy_device *phydev; }; #define to_net_dev(d) container_of(d, struct net_device, dev) diff --git a/include/linux/phy.h b/include/linux/phy.h index d63736a84002..6b0a782c6224 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -234,6 +234,8 @@ enum phy_state { PHY_RESUMING }; +struct sk_buff; + /* phy_device: An instance of a PHY * * drv: Pointer to the driver for this PHY instance @@ -402,6 +404,26 @@ struct phy_driver { /* Clears up any memory if needed */ void (*remove)(struct phy_device *phydev); + /* Handles SIOCSHWTSTAMP ioctl for hardware time stamping. */ + int (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr); + + /* + * Requests a Rx timestamp for 'skb'. If the skb is accepted, + * the phy driver promises to deliver it using netif_rx() as + * soon as a timestamp becomes available. One of the + * PTP_CLASS_ values is passed in 'type'. The function must + * return true if the skb is accepted for delivery. + */ + bool (*rxtstamp)(struct phy_device *dev, struct sk_buff *skb, int type); + + /* + * Requests a Tx timestamp for 'skb'. The phy driver promises + * to deliver it to the socket's error queue as soon as a + * timestamp becomes available. One of the PTP_CLASS_ values + * is passed in 'type'. + */ + void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type); + struct device_driver driver; }; #define to_phy_driver(d) container_of(d, struct phy_driver, driver) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a1b0400c8d86..f5aa87e1e0c8 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1933,6 +1933,36 @@ static inline ktime_t net_invalid_timestamp(void) return ktime_set(0, 0); } +extern void skb_timestamping_init(void); + +#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING + +extern void skb_clone_tx_timestamp(struct sk_buff *skb); +extern bool skb_defer_rx_timestamp(struct sk_buff *skb); + +#else /* CONFIG_NETWORK_PHY_TIMESTAMPING */ + +static inline void skb_clone_tx_timestamp(struct sk_buff *skb) +{ +} + +static inline bool skb_defer_rx_timestamp(struct sk_buff *skb) +{ + return false; +} + +#endif /* !CONFIG_NETWORK_PHY_TIMESTAMPING */ + +/** + * skb_complete_tx_timestamp() - deliver cloned skb with tx timestamps + * + * @skb: clone of the the original outgoing packet + * @hwtstamps: hardware time stamps + * + */ +void skb_complete_tx_timestamp(struct sk_buff *skb, + struct skb_shared_hwtstamps *hwtstamps); + /** * skb_tstamp_tx - queue clone of skb with send time stamps * @orig_skb: the original outgoing packet @@ -1965,6 +1995,7 @@ static inline void sw_tx_timestamp(struct sk_buff *skb) */ static inline void skb_tx_timestamp(struct sk_buff *skb) { + skb_clone_tx_timestamp(skb); sw_tx_timestamp(skb); } diff --git a/net/Kconfig b/net/Kconfig index 0d68b40fc0e6..b3250944cde9 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -86,6 +86,16 @@ config NETWORK_SECMARK to nfmark, but designated for security purposes. If you are unsure how to answer this question, answer N. +config NETWORK_PHY_TIMESTAMPING + bool "Timestamping in PHY devices" + depends on EXPERIMENTAL + help + This allows timestamping of network packets by PHYs with + hardware timestamping capabilities. This option adds some + overhead in the transmit and receive paths. + + If you are unsure how to answer this question, answer N. + menuconfig NETFILTER bool "Network packet filtering framework (Netfilter)" ---help--- diff --git a/net/core/Makefile b/net/core/Makefile index 51c3eec850ef..8a04dd22cf77 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -18,4 +18,4 @@ obj-$(CONFIG_NET_DMA) += user_dma.o obj-$(CONFIG_FIB_RULES) += fib_rules.o obj-$(CONFIG_TRACEPOINTS) += net-traces.o obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o - +obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o diff --git a/net/core/dev.c b/net/core/dev.c index e2b9fa2c917e..1c002c7ef5d5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2957,6 +2957,9 @@ int netif_receive_skb(struct sk_buff *skb) if (netdev_tstamp_prequeue) net_timestamp_check(skb); + if (skb_defer_rx_timestamp(skb)) + return NET_RX_SUCCESS; + #ifdef CONFIG_RPS { struct rps_dev_flow voidflow, *rflow = &voidflow; diff --git a/net/core/timestamping.c b/net/core/timestamping.c new file mode 100644 index 000000000000..0ae6c22da85b --- /dev/null +++ b/net/core/timestamping.c @@ -0,0 +1,126 @@ +/* + * PTP 1588 clock support - support for timestamping in PHY devices + * + * Copyright (C) 2010 OMICRON electronics GmbH + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include + +static struct sock_filter ptp_filter[] = { + PTP_FILTER +}; + +static unsigned int classify(struct sk_buff *skb) +{ + if (likely(skb->dev && + skb->dev->phydev && + skb->dev->phydev->drv)) + return sk_run_filter(skb, ptp_filter, ARRAY_SIZE(ptp_filter)); + else + return PTP_CLASS_NONE; +} + +void skb_clone_tx_timestamp(struct sk_buff *skb) +{ + struct phy_device *phydev; + struct sk_buff *clone; + struct sock *sk = skb->sk; + unsigned int type; + + if (!sk) + return; + + type = classify(skb); + + switch (type) { + case PTP_CLASS_V1_IPV4: + case PTP_CLASS_V1_IPV6: + case PTP_CLASS_V2_IPV4: + case PTP_CLASS_V2_IPV6: + case PTP_CLASS_V2_L2: + case PTP_CLASS_V2_VLAN: + phydev = skb->dev->phydev; + if (likely(phydev->drv->txtstamp)) { + clone = skb_clone(skb, GFP_ATOMIC); + if (!clone) + return; + clone->sk = sk; + phydev->drv->txtstamp(phydev, clone, type); + } + break; + default: + break; + } +} + +void skb_complete_tx_timestamp(struct sk_buff *skb, + struct skb_shared_hwtstamps *hwtstamps) +{ + struct sock *sk = skb->sk; + struct sock_exterr_skb *serr; + int err; + + if (!hwtstamps) + return; + + *skb_hwtstamps(skb) = *hwtstamps; + serr = SKB_EXT_ERR(skb); + memset(serr, 0, sizeof(*serr)); + serr->ee.ee_errno = ENOMSG; + serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING; + skb->sk = NULL; + err = sock_queue_err_skb(sk, skb); + if (err) + kfree_skb(skb); +} +EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp); + +bool skb_defer_rx_timestamp(struct sk_buff *skb) +{ + struct phy_device *phydev; + unsigned int type; + + skb_push(skb, ETH_HLEN); + + type = classify(skb); + + skb_pull(skb, ETH_HLEN); + + switch (type) { + case PTP_CLASS_V1_IPV4: + case PTP_CLASS_V1_IPV6: + case PTP_CLASS_V2_IPV4: + case PTP_CLASS_V2_IPV6: + case PTP_CLASS_V2_L2: + case PTP_CLASS_V2_VLAN: + phydev = skb->dev->phydev; + if (likely(phydev->drv->rxtstamp)) + return phydev->drv->rxtstamp(phydev, skb, type); + break; + default: + break; + } + + return false; +} + +void __init skb_timestamping_init(void) +{ + BUG_ON(sk_chk_filter(ptp_filter, ARRAY_SIZE(ptp_filter))); +} diff --git a/net/socket.c b/net/socket.c index 6fe484122a44..2270b941bcc7 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2394,6 +2394,10 @@ static int __init sock_init(void) netfilter_init(); #endif +#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING + skb_timestamping_init(); +#endif + return 0; } -- cgit v1.2.3 From bd27290a593f80cb99e95287cb29c72c0d57608b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 19 Jul 2010 09:35:40 -0700 Subject: net: 64bit stats for netdev_queue Since struct netdev_queue tx_bytes/tx_packets/tx_dropped are already protected by _xmit_lock, its easy to convert these fields to u64 instead of unsigned long. This completes 64bit stats for devices using them (vlan, macvlan, ...) Strictly, we could avoid the locking in dev_txq_stats_fold() on 64bit arches, but its slow path and we prefer keep it simple. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 6 +++--- net/core/dev.c | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fdc3f2992230..b6262898ece0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -501,9 +501,9 @@ struct netdev_queue { * please use this field instead of dev->trans_start */ unsigned long trans_start; - unsigned long tx_bytes; - unsigned long tx_packets; - unsigned long tx_dropped; + u64 tx_bytes; + u64 tx_packets; + u64 tx_dropped; } ____cacheline_aligned_in_smp; #ifdef CONFIG_RPS diff --git a/net/core/dev.c b/net/core/dev.c index 1c002c7ef5d5..9de75cdade56 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5282,15 +5282,17 @@ void netdev_run_todo(void) void dev_txq_stats_fold(const struct net_device *dev, struct rtnl_link_stats64 *stats) { - unsigned long tx_bytes = 0, tx_packets = 0, tx_dropped = 0; + u64 tx_bytes = 0, tx_packets = 0, tx_dropped = 0; unsigned int i; struct netdev_queue *txq; for (i = 0; i < dev->num_tx_queues; i++) { txq = netdev_get_tx_queue(dev, i); + spin_lock_bh(&txq->_xmit_lock); tx_bytes += txq->tx_bytes; tx_packets += txq->tx_packets; tx_dropped += txq->tx_dropped; + spin_unlock_bh(&txq->_xmit_lock); } if (tx_bytes || tx_packets || tx_dropped) { stats->tx_bytes = tx_bytes; -- cgit v1.2.3 From c1f79426e2df5ef96fe3e76de6c7606d15bf390b Mon Sep 17 00:00:00 2001 From: Stefan Assmann Date: Thu, 22 Jul 2010 02:50:21 +0000 Subject: sysfs: add attribute to indicate hw address assignment type Add addr_assign_type to struct net_device and expose it via sysfs. This new attribute has the purpose of giving user-space the ability to distinguish between different assignment types of MAC addresses. For example user-space can treat NICs with randomly generated MAC addresses differently than NICs that have permanent (locally assigned) MAC addresses. For the former udev could write a persistent net rule by matching the device path instead of the MAC address. There's also the case of devices that 'steal' MAC addresses from slave devices. In which it is also be beneficial for user-space to be aware of the fact. This patch also introduces a helper function to assist adoption of drivers that generate MAC addresses randomly. Signed-off-by: Stefan Assmann Signed-off-by: David S. Miller --- include/linux/etherdevice.h | 14 ++++++++++++++ include/linux/netdevice.h | 6 ++++++ net/core/net-sysfs.c | 2 ++ 3 files changed, 22 insertions(+) (limited to 'include/linux/netdevice.h') diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 3d7a6687d247..848480bc2bf9 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -126,6 +126,20 @@ static inline void random_ether_addr(u8 *addr) addr [0] |= 0x02; /* set local assignment bit (IEEE802) */ } +/** + * dev_hw_addr_random - Create random MAC and set device flag + * @dev: pointer to net_device structure + * @addr: Pointer to a six-byte array containing the Ethernet address + * + * Generate random MAC to be used by a device and set addr_assign_type + * so the state can be read by sysfs and be used by udev. + */ +static inline void dev_hw_addr_random(struct net_device *dev, u8 *hwaddr) +{ + dev->addr_assign_type |= NET_ADDR_RANDOM; + random_ether_addr(hwaddr); +} + /** * compare_ether_addr - Compare two Ethernet addresses * @addr1: Pointer to a six-byte array containing the Ethernet address diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b6262898ece0..1bca6171b1aa 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -66,6 +66,11 @@ struct wireless_dev; #define HAVE_FREE_NETDEV /* free_netdev() */ #define HAVE_NETDEV_PRIV /* netdev_priv() */ +/* hardware address assignment types */ +#define NET_ADDR_PERM 0 /* address is permanent (default) */ +#define NET_ADDR_RANDOM 1 /* address is generated randomly */ +#define NET_ADDR_STOLEN 2 /* address is stolen from other device */ + /* Backlog congestion levels */ #define NET_RX_SUCCESS 0 /* keep 'em coming, baby */ #define NET_RX_DROP 1 /* packet dropped */ @@ -919,6 +924,7 @@ struct net_device { /* Interface address info. */ unsigned char perm_addr[MAX_ADDR_LEN]; /* permanent hw address */ + unsigned char addr_assign_type; /* hw address assignment type */ unsigned char addr_len; /* hardware address length */ unsigned short dev_id; /* for shared network cards */ diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index d2b596537d41..af4dfbadf2a0 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -95,6 +95,7 @@ static ssize_t netdev_store(struct device *dev, struct device_attribute *attr, } NETDEVICE_SHOW(dev_id, fmt_hex); +NETDEVICE_SHOW(addr_assign_type, fmt_dec); NETDEVICE_SHOW(addr_len, fmt_dec); NETDEVICE_SHOW(iflink, fmt_dec); NETDEVICE_SHOW(ifindex, fmt_dec); @@ -295,6 +296,7 @@ static ssize_t show_ifalias(struct device *dev, } static struct device_attribute net_class_attributes[] = { + __ATTR(addr_assign_type, S_IRUGO, show_addr_assign_type, NULL), __ATTR(addr_len, S_IRUGO, show_addr_len, NULL), __ATTR(dev_id, S_IRUGO, show_dev_id, NULL), __ATTR(ifalias, S_IRUGO | S_IWUSR, show_ifalias, store_ifalias), -- cgit v1.2.3