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,
 
        if (!netif_device_present(dev))
                return -ENODEV;
 
+       kernel_cfg.ifr = ifr;
        err = ops->ndo_hwtstamp_get(dev, &kernel_cfg);
        if (err)
                return err;
 
-       hwtstamp_config_from_kernel(&cfg, &kernel_cfg);
+       /* 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;
+               if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)))
+                       return -EFAULT;
+       }
 
        return 0;
 }
 static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
-       struct kernel_hwtstamp_config kernel_cfg;
+       struct kernel_hwtstamp_config kernel_cfg = {};
        struct netlink_ext_ack extack = {};
        struct hwtstamp_config cfg;
        int err;
                return -EFAULT;
 
        hwtstamp_config_to_kernel(&kernel_cfg, &cfg);
+       kernel_cfg.ifr = ifr;
 
        err = net_hwtstamp_validate(&kernel_cfg);
        if (err)
        /* The driver may have modified the configuration, so copy the
         * updated version of it back to user space
         */
-       hwtstamp_config_from_kernel(&cfg, &kernel_cfg);
+       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;
+               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 ops->ndo_hwtstamp_get(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 ops->ndo_hwtstamp_set(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)
 {