Skip to content

Commit

Permalink
net: ethtool: Add infrastructure for reporting cable test results
Browse files Browse the repository at this point in the history
Provide infrastructure for PHY drivers to report the cable test
results.  A netlink skb is associated to the phydev. Helpers will be
added which can add results to this skb. Once the test has finished
the results are sent to user space.

When netlink ethtool is not part of the kernel configuration stubs are
provided. It is also impossible to trigger a cable test, so the error
code returned by the alloc function is of no consequence.

v2:
Include the status complete in the netlink notification message

v4:
Replace -EINVAL with -EMSGSIZE

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Michal Kubecek <mkubecek@suse.cz>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Andrew Lunn authored and Jakub Kicinski committed May 10, 2020
1 parent 0df960f commit 1dd3f21
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 2 deletions.
22 changes: 20 additions & 2 deletions drivers/net/phy/phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/phy.h>
#include <linux/phy_led_triggers.h>
#include <linux/sfp.h>
Expand All @@ -30,6 +31,9 @@
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <net/sock.h>

#define PHY_STATE_TIME HZ

Expand Down Expand Up @@ -478,6 +482,8 @@ static void phy_abort_cable_test(struct phy_device *phydev)
{
int err;

ethnl_cable_test_finished(phydev);

err = phy_init_hw(phydev);
if (err)
phydev_err(phydev, "Error while aborting cable test");
Expand All @@ -486,7 +492,7 @@ static void phy_abort_cable_test(struct phy_device *phydev)
int phy_start_cable_test(struct phy_device *phydev,
struct netlink_ext_ack *extack)
{
int err;
int err = -ENOMEM;

if (!(phydev->drv &&
phydev->drv->cable_test_start &&
Expand All @@ -512,19 +518,30 @@ int phy_start_cable_test(struct phy_device *phydev,
goto out;
}

err = ethnl_cable_test_alloc(phydev);
if (err)
goto out;

/* Mark the carrier down until the test is complete */
phy_link_down(phydev, true);

err = phydev->drv->cable_test_start(phydev);
if (err) {
phy_link_up(phydev);
goto out;
goto out_free;
}

phydev->state = PHY_CABLETEST;

if (phy_polling_mode(phydev))
phy_trigger_machine(phydev);

mutex_unlock(&phydev->lock);

return 0;

out_free:
ethnl_cable_test_free(phydev);
out:
mutex_unlock(&phydev->lock);

Expand Down Expand Up @@ -964,6 +981,7 @@ void phy_state_machine(struct work_struct *work)
}

if (finished) {
ethnl_cable_test_finished(phydev);
needs_aneg = true;
phydev->state = PHY_UP;
}
Expand Down
20 changes: 20 additions & 0 deletions include/linux/ethtool_netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,24 @@ enum ethtool_multicast_groups {
ETHNL_MCGRP_MONITOR,
};

struct phy_device;

#if IS_ENABLED(CONFIG_ETHTOOL_NETLINK)
int ethnl_cable_test_alloc(struct phy_device *phydev);
void ethnl_cable_test_free(struct phy_device *phydev);
void ethnl_cable_test_finished(struct phy_device *phydev);
#else
static inline int ethnl_cable_test_alloc(struct phy_device *phydev)
{
return -ENOTSUPP;
}

static inline void ethnl_cable_test_free(struct phy_device *phydev)
{
}

static inline void ethnl_cable_test_finished(struct phy_device *phydev)
{
}
#endif /* IS_ENABLED(ETHTOOL_NETLINK) */
#endif /* _LINUX_ETHTOOL_NETLINK_H_ */
5 changes: 5 additions & 0 deletions include/linux/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,11 @@ struct phy_device {
/* For use by PHYs inside the same package that need a shared state. */
struct phy_package_shared *shared;

/* Reporting cable test results */
struct sk_buff *skb;
void *ehdr;
struct nlattr *nest;

/* Interrupt and Polling infrastructure */
struct delayed_work state_queue;

Expand Down
55 changes: 55 additions & 0 deletions net/ethtool/cabletest.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/phy.h>
#include <linux/ethtool_netlink.h>
#include "netlink.h"
#include "common.h"

Expand Down Expand Up @@ -52,3 +53,57 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
dev_put(dev);
return ret;
}

int ethnl_cable_test_alloc(struct phy_device *phydev)
{
int err = -ENOMEM;

phydev->skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!phydev->skb)
goto out;

phydev->ehdr = ethnl_bcastmsg_put(phydev->skb,
ETHTOOL_MSG_CABLE_TEST_NTF);
if (!phydev->ehdr) {
err = -EMSGSIZE;
goto out;
}

err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev,
ETHTOOL_A_CABLE_TEST_NTF_HEADER);
if (err)
goto out;

err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED);
if (err)
goto out;

phydev->nest = nla_nest_start(phydev->skb,
ETHTOOL_A_CABLE_TEST_NTF_NEST);
if (!phydev->nest)
goto out;

return 0;

out:
nlmsg_free(phydev->skb);
return err;
}
EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc);

void ethnl_cable_test_free(struct phy_device *phydev)
{
nlmsg_free(phydev->skb);
}
EXPORT_SYMBOL_GPL(ethnl_cable_test_free);

void ethnl_cable_test_finished(struct phy_device *phydev)
{
nla_nest_end(phydev->skb, phydev->nest);

genlmsg_end(phydev->skb, phydev->ehdr);

ethnl_multicast(phydev->skb, phydev->attached_dev);
}
EXPORT_SYMBOL_GPL(ethnl_cable_test_finished);

0 comments on commit 1dd3f21

Please sign in to comment.