mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-14 06:22:34 +02:00
dev->hwprov tracks the active hwtstamp provider for the device.
Make it ops protected (instance lock if the netdev driver opts
into holding instance lock around callbacks, otherwise rtnl_lock).
hwprov is written and read in:
- drivers/net/phy/phy_device.c
phydev and ops protection don't currently mix, add a comment
- net/ethtool/
as of now holds both rtnl lock and ops lock, this one will
soon only hold one lock or the other
read in:
- net/core/dev_ioctl.c
holds both rtnl lock and ops lock
- net/core/timestamping.c
RCU reader
The new netdev_ops_lock_dereference() helper does not have
"compat" in the name. The name would be quite long and I think
in this case it should be obvious that we need _a_ lock.
netdev_lock_dereference() already exists and means dev->lock
is always expected.
Reviewed-by: Eric Dumazet <edumazet@google.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Link: https://patch.msgid.link/20260605002912.3456868-4-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
116 lines
2.6 KiB
C
116 lines
2.6 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Infrastructure to handle all PHY devices connected to a given netdev,
|
|
* either directly or indirectly attached.
|
|
*
|
|
* Copyright (c) 2023 Maxime Chevallier<maxime.chevallier@bootlin.com>
|
|
*/
|
|
|
|
#include <linux/phy_link_topology.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/xarray.h>
|
|
#include <net/netdev_lock.h>
|
|
|
|
static int netdev_alloc_phy_link_topology(struct net_device *dev)
|
|
{
|
|
struct phy_link_topology *topo;
|
|
|
|
topo = kzalloc_obj(*topo);
|
|
if (!topo)
|
|
return -ENOMEM;
|
|
|
|
xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1);
|
|
topo->next_phy_index = 1;
|
|
|
|
dev->link_topo = topo;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int phy_link_topo_add_phy(struct net_device *dev,
|
|
struct phy_device *phy,
|
|
enum phy_upstream upt, void *upstream)
|
|
{
|
|
struct phy_link_topology *topo = dev->link_topo;
|
|
struct phy_device_node *pdn;
|
|
int ret;
|
|
|
|
/* ethtool ops may run without rtnl_lock, and rtnl_lock is what
|
|
* currently protects the PHY topology. No driver currently mixes
|
|
* the two, flag if someone tries. See also:
|
|
* - ethnl_req_get_phydev()
|
|
* - phy_detach()
|
|
*/
|
|
if (WARN_ON_ONCE(netdev_need_ops_lock(dev)))
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!topo) {
|
|
ret = netdev_alloc_phy_link_topology(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
topo = dev->link_topo;
|
|
}
|
|
|
|
pdn = kzalloc_obj(*pdn);
|
|
if (!pdn)
|
|
return -ENOMEM;
|
|
|
|
pdn->phy = phy;
|
|
switch (upt) {
|
|
case PHY_UPSTREAM_MAC:
|
|
pdn->upstream.netdev = (struct net_device *)upstream;
|
|
if (phy_on_sfp(phy))
|
|
pdn->parent_sfp_bus = pdn->upstream.netdev->sfp_bus;
|
|
break;
|
|
case PHY_UPSTREAM_PHY:
|
|
pdn->upstream.phydev = (struct phy_device *)upstream;
|
|
if (phy_on_sfp(phy))
|
|
pdn->parent_sfp_bus = pdn->upstream.phydev->sfp_bus;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
pdn->upstream_type = upt;
|
|
|
|
/* Attempt to re-use a previously allocated phy_index */
|
|
if (phy->phyindex)
|
|
ret = xa_insert(&topo->phys, phy->phyindex, pdn, GFP_KERNEL);
|
|
else
|
|
ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn,
|
|
xa_limit_32b, &topo->next_phy_index,
|
|
GFP_KERNEL);
|
|
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
kfree(pdn);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(phy_link_topo_add_phy);
|
|
|
|
void phy_link_topo_del_phy(struct net_device *dev,
|
|
struct phy_device *phy)
|
|
{
|
|
struct phy_link_topology *topo = dev->link_topo;
|
|
struct phy_device_node *pdn;
|
|
|
|
if (!topo)
|
|
return;
|
|
|
|
pdn = xa_erase(&topo->phys, phy->phyindex);
|
|
|
|
/* We delete the PHY from the topology, however we don't re-set the
|
|
* phy->phyindex field. If the PHY isn't gone, we can re-assign it the
|
|
* same index next time it's added back to the topology
|
|
*/
|
|
|
|
kfree(pdn);
|
|
}
|
|
EXPORT_SYMBOL_GPL(phy_link_topo_del_phy);
|