mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-08-24 09:34:02 +00:00

xa_alloc_cyclic() can return 1, which isn't an error. To prevent
situation when the caller of this function will treat it as no error do
a check only for negative here.
Fixes: 3849687869
("net: phy: Introduce ethernet link topology representation")
Signed-off-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
106 lines
2.3 KiB
C
106 lines
2.3 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>
|
|
|
|
static int netdev_alloc_phy_link_topology(struct net_device *dev)
|
|
{
|
|
struct phy_link_topology *topo;
|
|
|
|
topo = kzalloc(sizeof(*topo), GFP_KERNEL);
|
|
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;
|
|
|
|
if (!topo) {
|
|
ret = netdev_alloc_phy_link_topology(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
topo = dev->link_topo;
|
|
}
|
|
|
|
pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
|
|
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);
|