AppleTalk keeps its per-interface control block (struct atalk_iface) directly in struct netdevice (dev->atalk_ptr). This is the only thing tying the protocol into the core net_device layout and is the sole blocker to moving AppleTalk out of tree.
Replace dev->atalk_ptr with a small ifindex-keyed hashtable internal to ddp.c. The existing atalk_interfaces list stays the owner of the iface objects; the hashtable is purely a fast dev->iface index and reuses the same atalk_interfaces_lock. AFAICT this patch does not make this code any more racy than it already is, I'm sure Sashiko will point out some basically existing bugs. AFAICT atalk_interfaces_lock is the innermost lock already. Signed-off-by: Jakub Kicinski <[email protected]> --- .../networking/net_cachelines/net_device.rst | 1 - include/linux/atalk.h | 8 +--- include/linux/netdevice.h | 4 -- net/appletalk/ddp.c | 48 +++++++++++++++++-- 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst index eb2e6851c6f6..512f6d6fa3d8 100644 --- a/Documentation/networking/net_cachelines/net_device.rst +++ b/Documentation/networking/net_cachelines/net_device.rst @@ -90,7 +90,6 @@ struct inet6_dev* ip6_ptr read_mostly struct vlan_info* vlan_info struct dsa_port* dsa_ptr struct tipc_bearer* tipc_ptr -void* atalk_ptr struct wireless_dev* ieee80211_ptr struct wpan_dev* ieee802154_ptr struct mpls_dev* mpls_ptr diff --git a/include/linux/atalk.h b/include/linux/atalk.h index a55bfc6567d0..ce7e6bfa9e2a 100644 --- a/include/linux/atalk.h +++ b/include/linux/atalk.h @@ -30,6 +30,7 @@ struct atalk_iface { #define ATIF_PROBE_FAIL 2 /* Probe collided */ struct atalk_netrange nets; struct atalk_iface *next; + struct hlist_node hash_node; /* keyed on dev->ifindex */ }; struct atalk_sock { @@ -113,12 +114,7 @@ extern int aarp_proto_init(void); /* Inter module exports */ /* Give a device find its atif control structure */ -#if IS_ENABLED(CONFIG_ATALK) -static inline struct atalk_iface *atalk_find_dev(struct net_device *dev) -{ - return dev->atalk_ptr; -} -#endif +extern struct atalk_iface *atalk_find_dev(struct net_device *dev); extern struct atalk_addr *atalk_find_dev_addr(struct net_device *dev); extern struct net_device *atrtr_get_dev(struct atalk_addr *sa); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7f4f0837c09f..655564621f28 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1947,7 +1947,6 @@ enum netdev_reg_state { * @vlan_info: VLAN info * @dsa_ptr: dsa specific data * @tipc_ptr: TIPC specific data - * @atalk_ptr: AppleTalk link * @ip_ptr: IPv4 specific data * @ip6_ptr: IPv6 specific data * @ieee80211_ptr: IEEE 802.11 specific data, assign before registering @@ -2349,9 +2348,6 @@ struct net_device { #if IS_ENABLED(CONFIG_TIPC) struct tipc_bearer __rcu *tipc_ptr; #endif -#if IS_ENABLED(CONFIG_ATALK) - void *atalk_ptr; -#endif #if IS_ENABLED(CONFIG_CFG80211) struct wireless_dev *ieee80211_ptr; #endif diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 30a6dc06291c..afb86ce6e644 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -52,6 +52,7 @@ #include <linux/termios.h> /* For TIOCOUTQ/INQ */ #include <linux/compat.h> #include <linux/slab.h> +#include <linux/hashtable.h> #include <net/datalink.h> #include <net/psnap.h> #include <net/sock.h> @@ -204,6 +205,33 @@ DEFINE_RWLOCK(atalk_routes_lock); struct atalk_iface *atalk_interfaces; DEFINE_RWLOCK(atalk_interfaces_lock); +/* Fast dev->iface lookup, keyed on ifindex. Shares atalk_interfaces_lock with + * the atalk_interfaces list, which remains the owner of the iface objects. + */ +#define ATALK_IFACE_HASH_BITS 8 +static DEFINE_HASHTABLE(atalk_iface_hash, ATALK_IFACE_HASH_BITS); + +/* Find the iface for @dev. Caller must hold atalk_interfaces_lock. */ +static struct atalk_iface *__atalk_find_dev(struct net_device *dev) +{ + struct atalk_iface *iface; + + hash_for_each_possible(atalk_iface_hash, iface, hash_node, dev->ifindex) + if (iface->dev == dev) + return iface; + return NULL; +} + +struct atalk_iface *atalk_find_dev(struct net_device *dev) +{ + struct atalk_iface *iface; + + read_lock_bh(&atalk_interfaces_lock); + iface = __atalk_find_dev(dev); + read_unlock_bh(&atalk_interfaces_lock); + return iface; +} + /* For probing devices or in a routerless network */ struct atalk_route atrtr_default; @@ -221,9 +249,9 @@ static void atif_drop_device(struct net_device *dev) while ((tmp = *iface) != NULL) { if (tmp->dev == dev) { *iface = tmp->next; + hash_del(&tmp->hash_node); dev_put(dev); kfree(tmp); - dev->atalk_ptr = NULL; } else iface = &tmp->next; } @@ -240,13 +268,13 @@ static struct atalk_iface *atif_add_device(struct net_device *dev, dev_hold(dev); iface->dev = dev; - dev->atalk_ptr = iface; iface->address = *sa; iface->status = 0; write_lock_bh(&atalk_interfaces_lock); iface->next = atalk_interfaces; atalk_interfaces = iface; + hash_add(atalk_iface_hash, &iface->hash_node, dev->ifindex); write_unlock_bh(&atalk_interfaces_lock); out: return iface; @@ -347,8 +375,15 @@ static int atif_proxy_probe_device(struct atalk_iface *atif, struct atalk_addr *atalk_find_dev_addr(struct net_device *dev) { - struct atalk_iface *iface = dev->atalk_ptr; - return iface ? &iface->address : NULL; + struct atalk_addr *addr = NULL; + struct atalk_iface *iface; + + read_lock_bh(&atalk_interfaces_lock); + iface = __atalk_find_dev(dev); + if (iface) + addr = &iface->address; + read_unlock_bh(&atalk_interfaces_lock); + return addr; } static struct atalk_addr *atalk_find_primary(void) @@ -388,8 +423,10 @@ static struct atalk_addr *atalk_find_primary(void) */ static struct atalk_iface *atalk_find_anynet(int node, struct net_device *dev) { - struct atalk_iface *iface = dev->atalk_ptr; + struct atalk_iface *iface; + read_lock_bh(&atalk_interfaces_lock); + iface = __atalk_find_dev(dev); if (!iface || iface->status & ATIF_PROBE) goto out_err; @@ -398,6 +435,7 @@ static struct atalk_iface *atalk_find_anynet(int node, struct net_device *dev) node != ATADDR_ANYNODE) goto out_err; out: + read_unlock_bh(&atalk_interfaces_lock); return iface; out_err: iface = NULL; -- 2.54.0
