From: Jakub Kicinski Date: Mon, 15 Jun 2026 22:29:34 +0000 (-0700) Subject: appletalk: stop storing per-interface state in struct net_device X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=023f9b0f2f4ffbf78ce9dfb8fbacb767c8e97162;p=thirdparty%2Fkernel%2Flinux.git appletalk: stop storing per-interface state in struct net_device 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. Acked-by: Stephen Hemminger Link: https://patch.msgid.link/20260615222935.947233-2-kuba@kernel.org Signed-off-by: Jakub Kicinski --- diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst index eb2e6851c6f60..512f6d6fa3d8f 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 a55bfc6567d01..ce7e6bfa9e2a2 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 7f4f0837c09f9..655564621f28e 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 30a6dc06291c3..afb86ce6e6446 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -52,6 +52,7 @@ #include /* For TIOCOUTQ/INQ */ #include #include +#include #include #include #include @@ -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 @@ out: */ 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;