Commit b23ec2bd authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'introduce-ndo_hwtstamp_get-and-ndo_hwtstamp_set'

Vladimir Oltean says:

====================
Introduce ndo_hwtstamp_get() and ndo_hwtstamp_set()

Based on previous RFCs from Maxim Georgiev:
https://lore.kernel.org/netdev/20230502043150.17097-1-glipus@gmail.com/

this series attempts to introduce new API for the hardware timestamping
control path (SIOCGHWTSTAMP and SIOCSHWTSTAMP handling).

I don't have any board with phylib hardware timestamping, so I would
appreciate testing (especially on lan966x, the most intricate
conversion). I was, however, able to test netdev level timestamping,
because I also have some more unsubmitted conversions in progress:

https://github.com/vladimiroltean/linux/commits/ndo-hwtstamp-v9

I hope that the concerns expressed in the comments of previous series
were addressed, and that Köry Maincent's series:
https://lore.kernel.org/netdev/20230406173308.401924-1-kory.maincent@bootlin.com/
can make progress in parallel with the conversion of the rest of drivers.
====================

Link: https://lore.kernel.org/r/20230801142824.1772134-1-vladimir.oltean@nxp.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 72c1a284 fd770e85
......@@ -7752,6 +7752,7 @@ F: include/linux/mii.h
F: include/linux/of_net.h
F: include/linux/phy.h
F: include/linux/phy_fixed.h
F: include/linux/phylib_stubs.h
F: include/linux/platform_data/mdio-bcm-unimac.h
F: include/linux/platform_data/mdio-gpio.h
F: include/trace/events/mdio.h
......
......@@ -4446,11 +4446,6 @@ static int bond_eth_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cm
{
struct bonding *bond = netdev_priv(bond_dev);
struct mii_ioctl_data *mii = NULL;
const struct net_device_ops *ops;
struct net_device *real_dev;
struct hwtstamp_config cfg;
struct ifreq ifrr;
int res = 0;
netdev_dbg(bond_dev, "bond_eth_ioctl: cmd=%d\n", cmd);
......@@ -4477,44 +4472,11 @@ static int bond_eth_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cm
}
break;
case SIOCSHWTSTAMP:
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
if (!(cfg.flags & HWTSTAMP_FLAG_BONDED_PHC_INDEX))
return -EOPNOTSUPP;
fallthrough;
case SIOCGHWTSTAMP:
real_dev = bond_option_active_slave_get_rcu(bond);
if (!real_dev)
return -EOPNOTSUPP;
strscpy_pad(ifrr.ifr_name, real_dev->name, IFNAMSIZ);
ifrr.ifr_ifru = ifr->ifr_ifru;
ops = real_dev->netdev_ops;
if (netif_device_present(real_dev) && ops->ndo_eth_ioctl) {
res = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
if (res)
return res;
ifr->ifr_ifru = ifrr.ifr_ifru;
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
/* Set the BOND_PHC_INDEX flag to notify user space */
cfg.flags |= HWTSTAMP_FLAG_BONDED_PHC_INDEX;
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ?
-EFAULT : 0;
}
fallthrough;
default:
res = -EOPNOTSUPP;
return -EOPNOTSUPP;
}
return res;
return 0;
}
static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd)
......@@ -5688,6 +5650,67 @@ static u32 bond_mode_bcast_speed(struct slave *slave, u32 speed)
return speed;
}
/* Set the BOND_PHC_INDEX flag to notify user space */
static int bond_set_phc_index_flag(struct kernel_hwtstamp_config *kernel_cfg)
{
struct ifreq *ifr = kernel_cfg->ifr;
struct hwtstamp_config cfg;
if (kernel_cfg->copied_to_user) {
/* Lower device has a legacy implementation */
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
cfg.flags |= HWTSTAMP_FLAG_BONDED_PHC_INDEX;
if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)))
return -EFAULT;
} else {
kernel_cfg->flags |= HWTSTAMP_FLAG_BONDED_PHC_INDEX;
}
return 0;
}
static int bond_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
struct bonding *bond = netdev_priv(dev);
struct net_device *real_dev;
int err;
real_dev = bond_option_active_slave_get_rcu(bond);
if (!real_dev)
return -EOPNOTSUPP;
err = generic_hwtstamp_get_lower(real_dev, cfg);
if (err)
return err;
return bond_set_phc_index_flag(cfg);
}
static int bond_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct bonding *bond = netdev_priv(dev);
struct net_device *real_dev;
int err;
if (!(cfg->flags & HWTSTAMP_FLAG_BONDED_PHC_INDEX))
return -EOPNOTSUPP;
real_dev = bond_option_active_slave_get_rcu(bond);
if (!real_dev)
return -EOPNOTSUPP;
err = generic_hwtstamp_set_lower(real_dev, cfg, extack);
if (err)
return err;
return bond_set_phc_index_flag(cfg);
}
static int bond_ethtool_get_link_ksettings(struct net_device *bond_dev,
struct ethtool_link_ksettings *cmd)
{
......@@ -5836,6 +5859,8 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_bpf = bond_xdp,
.ndo_xdp_xmit = bond_xdp_xmit,
.ndo_xdp_get_xmit_slave = bond_xdp_get_xmit_slave,
.ndo_hwtstamp_get = bond_hwtstamp_get,
.ndo_hwtstamp_set = bond_hwtstamp_set,
};
static const struct device_type bond_type = {
......
......@@ -698,9 +698,9 @@ struct fec_enet_private {
void fec_ptp_init(struct platform_device *pdev, int irq_idx);
void fec_ptp_stop(struct platform_device *pdev);
void fec_ptp_start_cyclecounter(struct net_device *ndev);
void fec_ptp_disable_hwts(struct net_device *ndev);
int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr);
int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr);
int fec_ptp_set(struct net_device *ndev, struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
void fec_ptp_get(struct net_device *ndev, struct kernel_hwtstamp_config *config);
/****************************************************************************/
#endif /* FEC_H */
......@@ -3203,33 +3203,6 @@ static const struct ethtool_ops fec_enet_ethtool_ops = {
.self_test = net_selftest,
};
static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct phy_device *phydev = ndev->phydev;
if (!netif_running(ndev))
return -EINVAL;
if (!phydev)
return -ENODEV;
if (fep->bufdesc_ex) {
bool use_fec_hwts = !phy_has_hwtstamp(phydev);
if (cmd == SIOCSHWTSTAMP) {
if (use_fec_hwts)
return fec_ptp_set(ndev, rq);
fec_ptp_disable_hwts(ndev);
} else if (cmd == SIOCGHWTSTAMP) {
if (use_fec_hwts)
return fec_ptp_get(ndev, rq);
}
}
return phy_mii_ioctl(phydev, rq, cmd);
}
static void fec_enet_free_buffers(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
......@@ -3895,6 +3868,37 @@ static int fec_enet_xdp_xmit(struct net_device *dev,
return sent_frames;
}
static int fec_hwtstamp_get(struct net_device *ndev,
struct kernel_hwtstamp_config *config)
{
struct fec_enet_private *fep = netdev_priv(ndev);
if (!netif_running(ndev))
return -EINVAL;
if (!fep->bufdesc_ex)
return -EOPNOTSUPP;
fec_ptp_get(ndev, config);
return 0;
}
static int fec_hwtstamp_set(struct net_device *ndev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
struct fec_enet_private *fep = netdev_priv(ndev);
if (!netif_running(ndev))
return -EINVAL;
if (!fep->bufdesc_ex)
return -EOPNOTSUPP;
return fec_ptp_set(ndev, config, extack);
}
static const struct net_device_ops fec_netdev_ops = {
.ndo_open = fec_enet_open,
.ndo_stop = fec_enet_close,
......@@ -3904,13 +3908,15 @@ static const struct net_device_ops fec_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_tx_timeout = fec_timeout,
.ndo_set_mac_address = fec_set_mac_address,
.ndo_eth_ioctl = fec_enet_ioctl,
.ndo_eth_ioctl = phy_do_ioctl_running,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = fec_poll_controller,
#endif
.ndo_set_features = fec_set_features,
.ndo_bpf = fec_enet_bpf,
.ndo_xdp_xmit = fec_enet_xdp_xmit,
.ndo_hwtstamp_get = fec_hwtstamp_get,
.ndo_hwtstamp_set = fec_hwtstamp_set,
};
static const unsigned short offset_des_active_rxq[] = {
......
......@@ -605,28 +605,12 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp,
}
}
/**
* fec_ptp_disable_hwts - disable hardware time stamping
* @ndev: pointer to net_device
*/
void fec_ptp_disable_hwts(struct net_device *ndev)
int fec_ptp_set(struct net_device *ndev, struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
struct fec_enet_private *fep = netdev_priv(ndev);
fep->hwts_tx_en = 0;
fep->hwts_rx_en = 0;
}
int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr)
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct hwtstamp_config config;
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
switch (config.tx_type) {
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
fep->hwts_tx_en = 0;
break;
......@@ -637,33 +621,28 @@ int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr)
return -ERANGE;
}
switch (config.rx_filter) {
switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
fep->hwts_rx_en = 0;
break;
default:
fep->hwts_rx_en = 1;
config.rx_filter = HWTSTAMP_FILTER_ALL;
config->rx_filter = HWTSTAMP_FILTER_ALL;
break;
}
return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-EFAULT : 0;
return 0;
}
int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr)
void fec_ptp_get(struct net_device *ndev, struct kernel_hwtstamp_config *config)
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct hwtstamp_config config;
config.flags = 0;
config.tx_type = fep->hwts_tx_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
config.rx_filter = (fep->hwts_rx_en ?
HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE);
return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-EFAULT : 0;
config->flags = 0;
config->tx_type = fep->hwts_tx_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
config->rx_filter = (fep->hwts_rx_en ?
HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE);
}
/*
......
......@@ -450,39 +450,46 @@ static int lan966x_port_get_parent_id(struct net_device *dev,
return 0;
}
static int lan966x_port_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd)
static int lan966x_port_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
struct lan966x_port *port = netdev_priv(dev);
if (!port->lan966x->ptp)
return -EOPNOTSUPP;
lan966x_ptp_hwtstamp_get(port, cfg);
return 0;
}
static int lan966x_port_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct lan966x_port *port = netdev_priv(dev);
int err;
if (cmd == SIOCSHWTSTAMP) {
err = lan966x_ptp_setup_traps(port, ifr);
if (err)
return err;
}
if (cfg->source != HWTSTAMP_SOURCE_NETDEV &&
cfg->source != HWTSTAMP_SOURCE_PHYLIB)
return -EOPNOTSUPP;
if (!phy_has_hwtstamp(dev->phydev) && port->lan966x->ptp) {
switch (cmd) {
case SIOCSHWTSTAMP:
err = lan966x_ptp_hwtstamp_set(port, ifr);
if (err)
lan966x_ptp_del_traps(port);
err = lan966x_ptp_setup_traps(port, cfg);
if (err)
return err;
if (cfg->source == HWTSTAMP_SOURCE_NETDEV) {
if (!port->lan966x->ptp)
return -EOPNOTSUPP;
err = lan966x_ptp_hwtstamp_set(port, cfg, extack);
if (err) {
lan966x_ptp_del_traps(port);
return err;
case SIOCGHWTSTAMP:
return lan966x_ptp_hwtstamp_get(port, ifr);
}
}
if (!dev->phydev)
return -ENODEV;
err = phy_mii_ioctl(dev->phydev, ifr, cmd);
if (err && cmd == SIOCSHWTSTAMP)
lan966x_ptp_del_traps(port);
return err;
return 0;
}
static const struct net_device_ops lan966x_port_netdev_ops = {
......@@ -495,10 +502,12 @@ static const struct net_device_ops lan966x_port_netdev_ops = {
.ndo_get_stats64 = lan966x_stats_get,
.ndo_set_mac_address = lan966x_port_set_mac_address,
.ndo_get_port_parent_id = lan966x_port_get_parent_id,
.ndo_eth_ioctl = lan966x_port_ioctl,
.ndo_eth_ioctl = phy_do_ioctl,
.ndo_setup_tc = lan966x_tc_setup,
.ndo_bpf = lan966x_xdp,
.ndo_xdp_xmit = lan966x_xdp_xmit,
.ndo_hwtstamp_get = lan966x_port_hwtstamp_get,
.ndo_hwtstamp_set = lan966x_port_hwtstamp_set,
};
bool lan966x_netdevice_check(const struct net_device *dev)
......@@ -808,6 +817,7 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
NETIF_F_HW_VLAN_STAG_TX |
NETIF_F_HW_TC;
dev->hw_features |= NETIF_F_HW_TC;
dev->priv_flags |= IFF_SEE_ALL_HWTSTAMP_REQUESTS;
dev->needed_headroom = IFH_LEN_BYTES;
eth_hw_addr_gen(dev, lan966x->base_mac, p + 1);
......
......@@ -298,7 +298,7 @@ struct lan966x_phc {
struct ptp_clock *clock;
struct ptp_clock_info info;
struct ptp_pin_desc pins[LAN966X_PHC_PINS_NUM];
struct hwtstamp_config hwtstamp_config;
struct kernel_hwtstamp_config hwtstamp_config;
struct lan966x *lan966x;
u8 index;
};
......@@ -578,8 +578,11 @@ void lan966x_mdb_restore_entries(struct lan966x *lan966x);
int lan966x_ptp_init(struct lan966x *lan966x);
void lan966x_ptp_deinit(struct lan966x *lan966x);
int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr);
int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr);
int lan966x_ptp_hwtstamp_set(struct lan966x_port *port,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack);
void lan966x_ptp_hwtstamp_get(struct lan966x_port *port,
struct kernel_hwtstamp_config *cfg);
void lan966x_ptp_rxtstamp(struct lan966x *lan966x, struct sk_buff *skb,
u64 src_port, u64 timestamp);
int lan966x_ptp_txtstamp_request(struct lan966x_port *port,
......@@ -590,7 +593,8 @@ irqreturn_t lan966x_ptp_irq_handler(int irq, void *args);
irqreturn_t lan966x_ptp_ext_irq_handler(int irq, void *args);
u32 lan966x_ptp_get_period_ps(void);
int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts);
int lan966x_ptp_setup_traps(struct lan966x_port *port, struct ifreq *ifr);
int lan966x_ptp_setup_traps(struct lan966x_port *port,
struct kernel_hwtstamp_config *cfg);
int lan966x_ptp_del_traps(struct lan966x_port *port);
int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev);
......
......@@ -248,29 +248,23 @@ int lan966x_ptp_del_traps(struct lan966x_port *port)
return err;
}
int lan966x_ptp_setup_traps(struct lan966x_port *port, struct ifreq *ifr)
int lan966x_ptp_setup_traps(struct lan966x_port *port,
struct kernel_hwtstamp_config *cfg)
{
struct hwtstamp_config cfg;
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
if (cfg.rx_filter == HWTSTAMP_FILTER_NONE)
if (cfg->rx_filter == HWTSTAMP_FILTER_NONE)
return lan966x_ptp_del_traps(port);
else
return lan966x_ptp_add_traps(port);
}
int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
int lan966x_ptp_hwtstamp_set(struct lan966x_port *port,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct lan966x *lan966x = port->lan966x;
struct hwtstamp_config cfg;
struct lan966x_phc *phc;
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
switch (cfg.tx_type) {
switch (cfg->tx_type) {
case HWTSTAMP_TX_ON:
port->ptp_tx_cmd = IFH_REW_OP_TWO_STEP_PTP;
break;
......@@ -284,7 +278,7 @@ int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
return -ERANGE;
}
switch (cfg.rx_filter) {
switch (cfg->rx_filter) {
case HWTSTAMP_FILTER_NONE:
port->ptp_rx_cmd = false;
break;
......@@ -303,7 +297,7 @@ int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
port->ptp_rx_cmd = true;
cfg.rx_filter = HWTSTAMP_FILTER_ALL;
cfg->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
......@@ -312,20 +306,20 @@ int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
/* Commit back the result & save it */
mutex_lock(&lan966x->ptp_lock);
phc = &lan966x->phc[LAN966X_PHC_PORT];
memcpy(&phc->hwtstamp_config, &cfg, sizeof(cfg));
phc->hwtstamp_config = *cfg;
mutex_unlock(&lan966x->ptp_lock);
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
return 0;
}
int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr)
void lan966x_ptp_hwtstamp_get(struct lan966x_port *port,
struct kernel_hwtstamp_config *cfg)
{
struct lan966x *lan966x = port->lan966x;
struct lan966x_phc *phc;
phc = &lan966x->phc[LAN966X_PHC_PORT];
return copy_to_user(ifr->ifr_data, &phc->hwtstamp_config,
sizeof(phc->hwtstamp_config)) ? -EFAULT : 0;
*cfg = phc->hwtstamp_config;
}
static int lan966x_ptp_classify(struct lan966x_port *port, struct sk_buff *skb)
......
......@@ -205,7 +205,7 @@ enum sparx5_core_clockfreq {
struct sparx5_phc {
struct ptp_clock *clock;
struct ptp_clock_info info;
struct hwtstamp_config hwtstamp_config;
struct kernel_hwtstamp_config hwtstamp_config;
struct sparx5 *sparx5;
u8 index;
};
......@@ -388,8 +388,11 @@ void sparx5_unregister_netdevs(struct sparx5 *sparx5);
/* sparx5_ptp.c */
int sparx5_ptp_init(struct sparx5 *sparx5);
void sparx5_ptp_deinit(struct sparx5 *sparx5);
int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr);
int sparx5_ptp_hwtstamp_get(struct sparx5_port *port, struct ifreq *ifr);
int sparx5_ptp_hwtstamp_set(struct sparx5_port *port,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack);
void sparx5_ptp_hwtstamp_get(struct sparx5_port *port,
struct kernel_hwtstamp_config *cfg);
void sparx5_ptp_rxtstamp(struct sparx5 *sparx5, struct sk_buff *skb,
u64 timestamp);
int sparx5_ptp_txtstamp_request(struct sparx5_port *port,
......
......@@ -210,22 +210,31 @@ static int sparx5_get_port_parent_id(struct net_device *dev,
return 0;
}
static int sparx5_port_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd)
static int sparx5_port_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
struct sparx5_port *sparx5_port = netdev_priv(dev);
struct sparx5 *sparx5 = sparx5_port->sparx5;
if (!phy_has_hwtstamp(dev->phydev) && sparx5->ptp) {
switch (cmd) {
case SIOCSHWTSTAMP:
return sparx5_ptp_hwtstamp_set(sparx5_port, ifr);
case SIOCGHWTSTAMP:
return sparx5_ptp_hwtstamp_get(sparx5_port, ifr);
}
}
if (!sparx5->ptp)
return -EOPNOTSUPP;
sparx5_ptp_hwtstamp_get(sparx5_port, cfg);
return 0;
}
static int sparx5_port_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct sparx5_port *sparx5_port = netdev_priv(dev);
struct sparx5 *sparx5 = sparx5_port->sparx5;
if (!sparx5->ptp)
return -EOPNOTSUPP;
return phy_mii_ioctl(dev->phydev, ifr, cmd);
return sparx5_ptp_hwtstamp_set(sparx5_port, cfg, extack);
}
static const struct net_device_ops sparx5_port_netdev_ops = {
......@@ -238,8 +247,10 @@ static const struct net_device_ops sparx5_port_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_get_stats64 = sparx5_get_stats64,
.ndo_get_port_parent_id = sparx5_get_port_parent_id,
.ndo_eth_ioctl = sparx5_port_ioctl,
.ndo_eth_ioctl = phy_do_ioctl,
.ndo_setup_tc = sparx5_port_setup_tc,
.ndo_hwtstamp_get = sparx5_port_hwtstamp_get,
.ndo_hwtstamp_set = sparx5_port_hwtstamp_set,
};
bool sparx5_netdevice_check(const struct net_device *dev)
......
......@@ -74,10 +74,11 @@ static u64 sparx5_ptp_get_nominal_value(struct sparx5 *sparx5)
return res;
}
int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
int sparx5_ptp_hwtstamp_set(struct sparx5_port *port,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct sparx5 *sparx5 = port->sparx5;
struct hwtstamp_config cfg;
struct sparx5_phc *phc;
/* For now don't allow to run ptp on ports that are part of a bridge,
......@@ -88,10 +89,7 @@ int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
if (test_bit(port->portno, sparx5->bridge_mask))
return -EINVAL;
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
switch (cfg.tx_type) {
switch (cfg->tx_type) {
case HWTSTAMP_TX_ON:
port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
break;
......@@ -105,7 +103,7 @@ int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
return -ERANGE;
}
switch (cfg.rx_filter) {
switch (cfg->rx_filter) {
case HWTSTAMP_FILTER_NONE:
break;
case HWTSTAMP_FILTER_ALL:
......@@ -122,7 +120,7 @@ int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
cfg.rx_filter = HWTSTAMP_FILTER_ALL;
cfg->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
......@@ -131,20 +129,20 @@ int sparx5_ptp_hwtstamp_set(struct sparx5_port *port, struct ifreq *ifr)
/* Commit back the result & save it */
mutex_lock(&sparx5->ptp_lock);
phc = &sparx5->phc[SPARX5_PHC_PORT];
memcpy(&phc->hwtstamp_config, &cfg, sizeof(cfg));
phc->hwtstamp_config = *cfg;
mutex_unlock(&sparx5->ptp_lock);
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
return 0;
}
int sparx5_ptp_hwtstamp_get(struct sparx5_port *port, struct ifreq *ifr)
void sparx5_ptp_hwtstamp_get(struct sparx5_port *port,
struct kernel_hwtstamp_config *cfg)
{
struct sparx5 *sparx5 = port->sparx5;
struct sparx5_phc *phc;
phc = &sparx5->phc[SPARX5_PHC_PORT];
return copy_to_user(ifr->ifr_data, &phc->hwtstamp_config,
sizeof(phc->hwtstamp_config)) ? -EFAULT : 0;
*cfg = phc->hwtstamp_config;
}
static void sparx5_ptp_classify(struct sparx5_port *port, struct sk_buff *skb,
......
......@@ -868,31 +868,24 @@ static int macvlan_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
static int macvlan_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static int macvlan_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
struct net_device *real_dev = macvlan_dev_real_dev(dev);
const struct net_device_ops *ops = real_dev->netdev_ops;
struct ifreq ifrr;
int err = -EOPNOTSUPP;
strscpy(ifrr.ifr_name, real_dev->name, IFNAMSIZ);
ifrr.ifr_ifru = ifr->ifr_ifru;
return generic_hwtstamp_get_lower(real_dev, cfg);
}
switch (cmd) {
case SIOCSHWTSTAMP:
if (!net_eq(dev_net(dev), &init_net))
break;
fallthrough;
case SIOCGHWTSTAMP:
if (netif_device_present(real_dev) && ops->ndo_eth_ioctl)
err = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
break;
}
static int macvlan_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct net_device *real_dev = macvlan_dev_real_dev(dev);
if (!err)
ifr->ifr_ifru = ifrr.ifr_ifru;
if (!net_eq(dev_net(dev), &init_net))
return -EOPNOTSUPP;
return err;
return generic_hwtstamp_set_lower(real_dev, cfg, extack);
}
/*
......@@ -1193,7 +1186,6 @@ static const struct net_device_ops macvlan_netdev_ops = {
.ndo_stop = macvlan_stop,
.ndo_start_xmit = macvlan_start_xmit,
.ndo_change_mtu = macvlan_change_mtu,
.ndo_eth_ioctl = macvlan_eth_ioctl,
.ndo_fix_features = macvlan_fix_features,
.ndo_change_rx_flags = macvlan_change_rx_flags,
.ndo_set_mac_address = macvlan_set_mac_address,
......@@ -1212,6 +1204,8 @@ static const struct net_device_ops macvlan_netdev_ops = {
#endif
.ndo_get_iflink = macvlan_dev_get_iflink,
.ndo_features_check = passthru_features_check,
.ndo_hwtstamp_get = macvlan_hwtstamp_get,
.ndo_hwtstamp_set = macvlan_hwtstamp_set,
};
static void macvlan_dev_free(struct net_device *dev)
......
......@@ -14,6 +14,8 @@ endif
# dedicated loadable module, so we bundle them all together into libphy.ko
ifdef CONFIG_PHYLIB
libphy-y += $(mdio-bus-y)
# the stubs are built-in whenever PHYLIB is built-in or module
obj-y += stubs.o
else
obj-$(CONFIG_MDIO_DEVICE) += mdio-bus.o
endif
......
......@@ -455,6 +455,40 @@ int phy_do_ioctl_running(struct net_device *dev, struct ifreq *ifr, int cmd)
}
EXPORT_SYMBOL(phy_do_ioctl_running);
/**
* __phy_hwtstamp_get - Get hardware timestamping configuration from PHY
*
* @phydev: the PHY device structure
* @config: structure holding the timestamping configuration
*
* Query the PHY device for its current hardware timestamping configuration.
*/
int __phy_hwtstamp_get(struct phy_device *phydev,
struct kernel_hwtstamp_config *config)
{
if (!phydev)
return -ENODEV;
return phy_mii_ioctl(phydev, config->ifr, SIOCGHWTSTAMP);
}
/**
* __phy_hwtstamp_set - Modify PHY hardware timestamping configuration
*
* @phydev: the PHY device structure
* @config: structure holding the timestamping configuration
* @extack: netlink extended ack structure, for error reporting
*/
int __phy_hwtstamp_set(struct phy_device *phydev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
if (!phydev)
return -ENODEV;
return phy_mii_ioctl(phydev, config->ifr, SIOCSHWTSTAMP);
}
/**
* phy_queue_state_machine - Trigger the state machine to run soon
*
......
......@@ -27,9 +27,11 @@
#include <linux/of.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include <linux/phylib_stubs.h>
#include <linux/phy_led_triggers.h>
#include <linux/pse-pd/pse.h>
#include <linux/property.h>
#include <linux/rtnetlink.h>
#include <linux/sfp.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
......@@ -3447,11 +3449,29 @@ static const struct ethtool_phy_ops phy_ethtool_phy_ops = {
.start_cable_test_tdr = phy_start_cable_test_tdr,
};
static const struct phylib_stubs __phylib_stubs = {
.hwtstamp_get = __phy_hwtstamp_get,
.hwtstamp_set = __phy_hwtstamp_set,
};
static void phylib_register_stubs(void)
{
phylib_stubs = &__phylib_stubs;
}
static void phylib_unregister_stubs(void)
{
phylib_stubs = NULL;
}
static int __init phy_init(void)
{
int rc;
rtnl_lock();
ethtool_set_ethtool_phy_ops(&phy_ethtool_phy_ops);
phylib_register_stubs();
rtnl_unlock();
rc = mdio_bus_init();
if (rc)
......@@ -3474,7 +3494,10 @@ static int __init phy_init(void)
err_mdio_bus:
mdio_bus_exit();
err_ethtool_phy_ops:
rtnl_lock();
phylib_unregister_stubs();
ethtool_set_ethtool_phy_ops(NULL);
rtnl_unlock();
return rc;
}
......@@ -3484,7 +3507,10 @@ static void __exit phy_exit(void)
phy_driver_unregister(&genphy_c45_driver);
phy_driver_unregister(&genphy_driver);
mdio_bus_exit();
rtnl_lock();
phylib_unregister_stubs();
ethtool_set_ethtool_phy_ops(NULL);
rtnl_unlock();
}
subsys_initcall(phy_init);
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Stubs for PHY library functionality called by the core network stack.
* These are necessary because CONFIG_PHYLIB can be a module, and built-in
* code cannot directly call symbols exported by modules.
*/
#include <linux/phylib_stubs.h>
const struct phylib_stubs *phylib_stubs;
EXPORT_SYMBOL_GPL(phylib_stubs);
......@@ -5,12 +5,23 @@
#include <uapi/linux/net_tstamp.h>
enum hwtstamp_source {
HWTSTAMP_SOURCE_NETDEV,
HWTSTAMP_SOURCE_PHYLIB,
};
/**
* struct kernel_hwtstamp_config - Kernel copy of struct hwtstamp_config
*
* @flags: see struct hwtstamp_config
* @tx_type: see struct hwtstamp_config
* @rx_filter: see struct hwtstamp_config
* @ifr: pointer to ifreq structure from the original ioctl request, to pass to
* a legacy implementation of a lower driver
* @copied_to_user: request was passed to a legacy implementation which already
* copied the ioctl request back to user space
* @source: indication whether timestamps should come from the netdev or from
* an attached phylib PHY
*
* Prefer using this structure for in-kernel processing of hardware
* timestamping configuration, over the inextensible struct hwtstamp_config
......@@ -20,6 +31,9 @@ struct kernel_hwtstamp_config {
int flags;
int tx_type;
int rx_filter;
struct ifreq *ifr;
bool copied_to_user;
enum hwtstamp_source source;
};
static inline void hwtstamp_config_to_kernel(struct kernel_hwtstamp_config *kernel_cfg,
......@@ -30,4 +44,20 @@ static inline void hwtstamp_config_to_kernel(struct kernel_hwtstamp_config *kern
kernel_cfg->rx_filter = cfg->rx_filter;
}
static inline void hwtstamp_config_from_kernel(struct hwtstamp_config *cfg,
const struct kernel_hwtstamp_config *kernel_cfg)
{
cfg->flags = kernel_cfg->flags;
cfg->tx_type = kernel_cfg->tx_type;
cfg->rx_filter = kernel_cfg->rx_filter;
}
static inline bool kernel_hwtstamp_config_changed(const struct kernel_hwtstamp_config *a,
const struct kernel_hwtstamp_config *b)
{
return a->flags != b->flags ||
a->tx_type != b->tx_type ||
a->rx_filter != b->rx_filter;
}
#endif /* _LINUX_NET_TIMESTAMPING_H_ */
......@@ -57,6 +57,7 @@
struct netpoll_info;
struct device;
struct ethtool_ops;
struct kernel_hwtstamp_config;
struct phy_device;
struct dsa_port;
struct ip_tunnel_parm;
......@@ -1418,6 +1419,16 @@ struct netdev_net_notifier {
* Get hardware timestamp based on normal/adjustable time or free running
* cycle counter. This function is required if physical clock supports a
* free running cycle counter.
*
* int (*ndo_hwtstamp_get)(struct net_device *dev,
* struct kernel_hwtstamp_config *kernel_config);
* Get the currently configured hardware timestamping parameters for the
* NIC device.
*
* int (*ndo_hwtstamp_set)(struct net_device *dev,
* struct kernel_hwtstamp_config *kernel_config,
* struct netlink_ext_ack *extack);
* Change the hardware timestamping parameters for NIC device.
*/
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
......@@ -1652,6 +1663,11 @@ struct net_device_ops {
ktime_t (*ndo_get_tstamp)(struct net_device *dev,
const struct skb_shared_hwtstamps *hwtstamps,
bool cycles);
int (*ndo_hwtstamp_get)(struct net_device *dev,
struct kernel_hwtstamp_config *kernel_config);
int (*ndo_hwtstamp_set)(struct net_device *dev,
struct kernel_hwtstamp_config *kernel_config,
struct netlink_ext_ack *extack);
};
struct xdp_metadata_ops {
......@@ -1708,6 +1724,9 @@ struct xdp_metadata_ops {
* @IFF_TX_SKB_NO_LINEAR: device/driver is capable of xmitting frames with
* skb_headlen(skb) == 0 (data starts from frag0)
* @IFF_CHANGE_PROTO_DOWN: device supports setting carrier via IFLA_PROTO_DOWN
* @IFF_SEE_ALL_HWTSTAMP_REQUESTS: device wants to see calls to
* ndo_hwtstamp_set() for all timestamp requests regardless of source,
* even if those aren't HWTSTAMP_SOURCE_NETDEV.
*/
enum netdev_priv_flags {
IFF_802_1Q_VLAN = 1<<0,
......@@ -1743,6 +1762,7 @@ enum netdev_priv_flags {
IFF_NO_ADDRCONF = BIT_ULL(30),
IFF_TX_SKB_NO_LINEAR = BIT_ULL(31),
IFF_CHANGE_PROTO_DOWN = BIT_ULL(32),
IFF_SEE_ALL_HWTSTAMP_REQUESTS = BIT_ULL(33),
};
#define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
......@@ -3934,6 +3954,11 @@ int put_user_ifreq(struct ifreq *ifr, void __user *arg);
int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
void __user *data, bool *need_copyout);
int dev_ifconf(struct net *net, struct ifconf __user *ifc);
int generic_hwtstamp_get_lower(struct net_device *dev,
struct kernel_hwtstamp_config *kernel_cfg);
int generic_hwtstamp_set_lower(struct net_device *dev,
struct kernel_hwtstamp_config *kernel_cfg,
struct netlink_ext_ack *extack);
int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *userdata);
unsigned int dev_get_flags(const struct net_device *);
int __dev_change_flags(struct net_device *dev, unsigned int flags,
......
......@@ -298,6 +298,7 @@ static inline const char *phy_modes(phy_interface_t interface)
#define MII_BUS_ID_SIZE 61
struct device;
struct kernel_hwtstamp_config;
struct phylink;
struct sfp_bus;
struct sfp_upstream_ops;
......@@ -1955,6 +1956,12 @@ int phy_ethtool_set_plca_cfg(struct phy_device *phydev,
int phy_ethtool_get_plca_status(struct phy_device *phydev,
struct phy_plca_status *plca_st);
int __phy_hwtstamp_get(struct phy_device *phydev,
struct kernel_hwtstamp_config *config);
int __phy_hwtstamp_set(struct phy_device *phydev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
{
struct phy_package_shared *shared = phydev->shared;
......
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Stubs for the Network PHY library
*/
#include <linux/rtnetlink.h>
struct kernel_hwtstamp_config;
struct netlink_ext_ack;
struct phy_device;
#if IS_ENABLED(CONFIG_PHYLIB)
extern const struct phylib_stubs *phylib_stubs;
struct phylib_stubs {
int (*hwtstamp_get)(struct phy_device *phydev,
struct kernel_hwtstamp_config *config);
int (*hwtstamp_set)(struct phy_device *phydev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
};
static inline int phy_hwtstamp_get(struct phy_device *phydev,
struct kernel_hwtstamp_config *config)
{
/* phylib_register_stubs() and phylib_unregister_stubs()
* also run under rtnl_lock().
*/
ASSERT_RTNL();
if (!phylib_stubs)
return -EOPNOTSUPP;
return phylib_stubs->hwtstamp_get(phydev, config);
}
static inline int phy_hwtstamp_set(struct phy_device *phydev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
/* phylib_register_stubs() and phylib_unregister_stubs()
* also run under rtnl_lock().
*/
ASSERT_RTNL();
if (!phylib_stubs)
return -EOPNOTSUPP;
return phylib_stubs->hwtstamp_set(phydev, config, extack);
}
#else
static inline int phy_hwtstamp_get(struct phy_device *phydev,
struct kernel_hwtstamp_config *config)
{
return -EOPNOTSUPP;
}
static inline int phy_hwtstamp_set(struct phy_device *phydev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
return -EOPNOTSUPP;
}
#endif
......@@ -354,6 +354,26 @@ static int vlan_dev_set_mac_address(struct net_device *dev, void *p)
return 0;
}
static int vlan_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
return generic_hwtstamp_get_lower(real_dev, cfg);
}
static int vlan_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
if (!net_eq(dev_net(dev), dev_net(real_dev)))
return -EOPNOTSUPP;
return generic_hwtstamp_set_lower(real_dev, cfg, extack);
}
static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
......@@ -365,14 +385,9 @@ static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
ifrr.ifr_ifru = ifr->ifr_ifru;
switch (cmd) {
case SIOCSHWTSTAMP:
if (!net_eq(dev_net(dev), dev_net(real_dev)))
break;
fallthrough;
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSMIIREG:
case SIOCGHWTSTAMP:
if (netif_device_present(real_dev) && ops->ndo_eth_ioctl)
err = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
break;
......@@ -1081,6 +1096,8 @@ static const struct net_device_ops vlan_netdev_ops = {
.ndo_fix_features = vlan_dev_fix_features,
.ndo_get_iflink = vlan_dev_get_iflink,
.ndo_fill_forward_path = vlan_dev_fill_forward_path,
.ndo_hwtstamp_get = vlan_hwtstamp_get,
.ndo_hwtstamp_set = vlan_hwtstamp_set,
};
static void vlan_dev_free(struct net_device *dev)
......
......@@ -5,6 +5,7 @@
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <linux/net_tstamp.h>
#include <linux/phylib_stubs.h>
#include <linux/wireless.h>
#include <linux/if_bridge.h>
#include <net/dsa_stubs.h>
......@@ -252,14 +253,118 @@ static int dev_eth_ioctl(struct net_device *dev,
return ops->ndo_eth_ioctl(dev, ifr, cmd);
}
/**
* dev_get_hwtstamp_phylib() - Get hardware timestamping settings of NIC
* or of attached phylib PHY
* @dev: Network device
* @cfg: Timestamping configuration structure
*
* Helper for enforcing a common policy that phylib timestamping, if available,
* should take precedence in front of hardware timestamping provided by the
* netdev.
*
* Note: phy_mii_ioctl() only handles SIOCSHWTSTAMP (not SIOCGHWTSTAMP), and
* there only exists a phydev->mii_ts->hwtstamp() method. So this will return
* -EOPNOTSUPP for phylib for now, which is still more accurate than letting
* the netdev handle the GET request.
*/
static int dev_get_hwtstamp_phylib(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
if (phy_has_hwtstamp(dev->phydev))
return phy_hwtstamp_get(dev->phydev, cfg);
return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg);
}
static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr)
{
return dev_eth_ioctl(dev, ifr, SIOCGHWTSTAMP);
const struct net_device_ops *ops = dev->netdev_ops;
struct kernel_hwtstamp_config kernel_cfg = {};
struct hwtstamp_config cfg;
int err;
if (!ops->ndo_hwtstamp_get)
return dev_eth_ioctl(dev, ifr, SIOCGHWTSTAMP); /* legacy */
if (!netif_device_present(dev))
return -ENODEV;
kernel_cfg.ifr = ifr;
err = dev_get_hwtstamp_phylib(dev, &kernel_cfg);
if (err)
return err;
/* If the request was resolved through an unconverted driver, omit
* the copy_to_user(), since the implementation has already done that
*/
if (!kernel_cfg.copied_to_user) {
hwtstamp_config_from_kernel(&cfg, &kernel_cfg);
if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)))
return -EFAULT;
}
return 0;
}
/**
* dev_set_hwtstamp_phylib() - Change hardware timestamping of NIC
* or of attached phylib PHY
* @dev: Network device
* @cfg: Timestamping configuration structure
* @extack: Netlink extended ack message structure, for error reporting
*
* Helper for enforcing a common policy that phylib timestamping, if available,
* should take precedence in front of hardware timestamping provided by the
* netdev. If the netdev driver needs to perform specific actions even for PHY
* timestamping to work properly (a switch port must trap the timestamped
* frames and not forward them), it must set IFF_SEE_ALL_HWTSTAMP_REQUESTS in
* dev->priv_flags.
*/
static int dev_set_hwtstamp_phylib(struct net_device *dev,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
const struct net_device_ops *ops = dev->netdev_ops;
bool phy_ts = phy_has_hwtstamp(dev->phydev);
struct kernel_hwtstamp_config old_cfg = {};
bool changed = false;
int err;
cfg->source = phy_ts ? HWTSTAMP_SOURCE_PHYLIB : HWTSTAMP_SOURCE_NETDEV;
if (!phy_ts || (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) {
err = ops->ndo_hwtstamp_get(dev, &old_cfg);
if (err)
return err;
err = ops->ndo_hwtstamp_set(dev, cfg, extack);
if (err) {
if (extack->_msg)
netdev_err(dev, "%s\n", extack->_msg);
return err;
}
changed = kernel_hwtstamp_config_changed(&old_cfg, cfg);
}
if (phy_ts) {
err = phy_hwtstamp_set(dev->phydev, cfg, extack);
if (err) {
if (changed)
ops->ndo_hwtstamp_set(dev, &old_cfg, NULL);
return err;
}
}
return 0;
}
static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)
{
struct kernel_hwtstamp_config kernel_cfg;
const struct net_device_ops *ops = dev->netdev_ops;
struct kernel_hwtstamp_config kernel_cfg = {};
struct netlink_ext_ack extack = {};
struct hwtstamp_config cfg;
int err;
......@@ -268,6 +373,7 @@ static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)
return -EFAULT;
hwtstamp_config_to_kernel(&kernel_cfg, &cfg);
kernel_cfg.ifr = ifr;
err = net_hwtstamp_validate(&kernel_cfg);
if (err)
......@@ -280,8 +386,80 @@ static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)
return err;
}
return dev_eth_ioctl(dev, ifr, SIOCSHWTSTAMP);
if (!ops->ndo_hwtstamp_set)
return dev_eth_ioctl(dev, ifr, SIOCSHWTSTAMP); /* legacy */
if (!netif_device_present(dev))
return -ENODEV;
err = dev_set_hwtstamp_phylib(dev, &kernel_cfg, &extack);
if (err)
return err;
/* The driver may have modified the configuration, so copy the
* updated version of it back to user space
*/
if (!kernel_cfg.copied_to_user) {
hwtstamp_config_from_kernel(&cfg, &kernel_cfg);
if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)))
return -EFAULT;
}
return 0;
}
static int generic_hwtstamp_ioctl_lower(struct net_device *dev, int cmd,
struct kernel_hwtstamp_config *kernel_cfg)
{
struct ifreq ifrr;
int err;
strscpy_pad(ifrr.ifr_name, dev->name, IFNAMSIZ);
ifrr.ifr_ifru = kernel_cfg->ifr->ifr_ifru;
err = dev_eth_ioctl(dev, &ifrr, cmd);
if (err)
return err;
kernel_cfg->ifr->ifr_ifru = ifrr.ifr_ifru;
kernel_cfg->copied_to_user = true;
return 0;
}
int generic_hwtstamp_get_lower(struct net_device *dev,
struct kernel_hwtstamp_config *kernel_cfg)
{
const struct net_device_ops *ops = dev->netdev_ops;
if (!netif_device_present(dev))
return -ENODEV;
if (ops->ndo_hwtstamp_get)
return dev_get_hwtstamp_phylib(dev, kernel_cfg);
/* Legacy path: unconverted lower driver */
return generic_hwtstamp_ioctl_lower(dev, SIOCGHWTSTAMP, kernel_cfg);
}
EXPORT_SYMBOL(generic_hwtstamp_get_lower);
int generic_hwtstamp_set_lower(struct net_device *dev,
struct kernel_hwtstamp_config *kernel_cfg,
struct netlink_ext_ack *extack)
{
const struct net_device_ops *ops = dev->netdev_ops;
if (!netif_device_present(dev))
return -ENODEV;
if (ops->ndo_hwtstamp_set)
return dev_set_hwtstamp_phylib(dev, kernel_cfg, extack);
/* Legacy path: unconverted lower driver */
return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg);
}
EXPORT_SYMBOL(generic_hwtstamp_set_lower);
static int dev_siocbond(struct net_device *dev,
struct ifreq *ifr, unsigned int cmd)
......
......@@ -665,9 +665,8 @@ const struct ethtool_phy_ops *ethtool_phy_ops;
void ethtool_set_ethtool_phy_ops(const struct ethtool_phy_ops *ops)
{
rtnl_lock();
ASSERT_RTNL();
ethtool_phy_ops = ops;
rtnl_unlock();
}
EXPORT_SYMBOL_GPL(ethtool_set_ethtool_phy_ops);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment