mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-16 12:14:06 +00:00
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.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
b23ec2bd7b
23 changed files with 630 additions and 209 deletions
|
@ -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;
|
||||
|
||||
return phy_mii_ioctl(dev->phydev, ifr, cmd);
|
||||
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 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 @@ err_c45:
|
|||
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);
|
||||
|
|
10
drivers/net/phy/stubs.c
Normal file
10
drivers/net/phy/stubs.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
// 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;
|
||||
|
|
68
include/linux/phylib_stubs.h
Normal file
68
include/linux/phylib_stubs.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/* 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 @@ out:
|
|||
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,9 +386,81 @@ 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);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue