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


Reply via email to