diff mbox series

[net-next,RFC] Add NDOs for hardware timestamp get/set

Message ID 20230331045619.40256-1-glipus@gmail.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [net-next,RFC] Add NDOs for hardware timestamp get/set | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 4294 this patch: 4295
netdev/cc_maintainers warning 6 maintainers not CCed: intel-wired-lan@lists.osuosl.org pabeni@redhat.com jesse.brandeburg@intel.com anthony.l.nguyen@intel.com edumazet@google.com davem@davemloft.net
netdev/build_clang fail Errors and warnings before: 950 this patch: 952
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 4501 this patch: 4502
netdev/checkpatch warning WARNING: line length of 84 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Max Georgiev March 31, 2023, 4:56 a.m. UTC
Current NIC driver API demands drivers supporting hardware timestamping
to support SIOCGHWTSTAMP/SIOCSHWTSTAMP IOCTLs. Handling these IOCTLs
requires dirivers to implement request parameter structure translation
between user and kernel address spaces, handling possible
translation failures, etc. This translation code is pretty much
identical across most of the NIC drivers that support SIOCGHWTSTAMP/
SIOCSHWTSTAMP.
This patch extends NDO functiuon set with ndo_hwtstamp_get/set
functions, implements SIOCGHWTSTAMP/SIOCSHWTSTAMP IOCTL translation
to ndo_hwtstamp_get/set function calls including parameter structure
translation and translation error handling.

This patch is sent out as RFC.
It still pending on basic testing. Implementing ndo_hwtstamp_get/set
in netdevsim driver should allow manual testing of the request
translation logic. Also is there a way to automate this testing?

Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Maxim Georgiev <glipus@gmail.com>
---
 drivers/net/ethernet/intel/e1000e/netdev.c | 33 ++++++++---------
 drivers/net/netdevsim/netdev.c             | 20 +++++++++++
 drivers/net/netdevsim/netdevsim.h          |  1 +
 include/linux/netdevice.h                  | 12 +++++++
 net/core/dev_ioctl.c                       | 42 +++++++++++++++++++---
 5 files changed, 85 insertions(+), 23 deletions(-)

Comments

Jakub Kicinski March 31, 2023, 5:35 a.m. UTC | #1
On Thu, 30 Mar 2023 22:56:19 -0600 Maxim Georgiev wrote:
> @@ -1642,6 +1650,10 @@ 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 hwtstamp_config *config);
> +	int			(*ndo_hwtstamp_set)(struct net_device *dev,
> +						    struct hwtstamp_config *config);

I wonder if we should pass in 

	struct netlink_ext_ack *extack

and maybe another structure for future extensions?
So we don't have to change the drivers again when we extend uAPI.

>  };
>  
>  struct xdp_metadata_ops {
> diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
> index 5cdbfbf9a7dc..c90fac9a9b2e 100644
> --- a/net/core/dev_ioctl.c
> +++ b/net/core/dev_ioctl.c
> @@ -277,6 +277,39 @@ static int dev_siocbond(struct net_device *dev,
>  	return -EOPNOTSUPP;
>  }
>  
> +static int dev_hwtstamp(struct net_device *dev, struct ifreq *ifr,
> +			unsigned int cmd)
> +{
> +	const struct net_device_ops *ops = dev->netdev_ops;
> +	int err;
> +	struct hwtstamp_config config;

nit: reorder int err after config we like lines longest to shortest

> +
> +	if ((cmd == SIOCGHWTSTAMP && !ops->ndo_hwtstamp_get) ||
> +	    (cmd == SIOCSHWTSTAMP && !ops->ndo_hwtstamp_set))
> +		return dev_eth_ioctl(dev, ifr, cmd);
> +
> +	err = dsa_ndo_eth_ioctl(dev, ifr, cmd);
> +	if (err == 0 || err != -EOPNOTSUPP)
> +		return err;
> +
> +	if (!netif_device_present(dev))
> +		return -ENODEV;
> +
> +	if (cmd == SIOCSHWTSTAMP) {
> +		if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
> +			err = -EFAULT;
> +		else
> +			err = ops->ndo_hwtstamp_set(dev, &config);
> +	} else if (cmd == SIOCGHWTSTAMP) {
> +		err = ops->ndo_hwtstamp_get(dev, &config);
> +	}
> +
> +	if (err == 0)
> +		err = copy_to_user(ifr->ifr_data, &config,
> +				   sizeof(config)) ? -EFAULT : 0;

nit: just error check each return value, don't try to save LoC

> +	return err;
> +}
> +
>  static int dev_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
>  			      void __user *data, unsigned int cmd)
>  {
> @@ -391,11 +424,14 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
>  		rtnl_lock();
>  		return err;
>  
> +	case SIOCGHWTSTAMP:
> +		return dev_hwtstamp(dev, ifr, cmd);
> +
>  	case SIOCSHWTSTAMP:
>  		err = net_hwtstamp_validate(ifr);
>  		if (err)
>  			return err;
> -		fallthrough;
> +		return dev_hwtstamp(dev, ifr, cmd);

Let's refactor this differently, we need net_hwtstamp_validate()
to run on the same in-kernel copy as we'll send down to the driver.
If we copy_from_user() twice we may validate a different thing
than the driver will end up seeing (ToCToU).

TBH I'm not sure if keeping GET and SET in a common dev_hwtstamp()
ends up being beneficial. If we fold in the validation check half 
of the code will be under and if (GET) or if (SET)..
Max Georgiev March 31, 2023, 5:51 p.m. UTC | #2
Jakub, thank you for taking a look!

On Thu, Mar 30, 2023 at 11:35 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Thu, 30 Mar 2023 22:56:19 -0600 Maxim Georgiev wrote:
> > @@ -1642,6 +1650,10 @@ 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 hwtstamp_config *config);
> > +     int                     (*ndo_hwtstamp_set)(struct net_device *dev,
> > +                                                 struct hwtstamp_config *config);
>
> I wonder if we should pass in
>
>         struct netlink_ext_ack *extack
>
> and maybe another structure for future extensions?
> So we don't have to change the drivers again when we extend uAPI.

Would these two extra parameters be ignored by drivers in this initial
version of NDO hw timestamp API implementation?

>
> >  };
> >
> >  struct xdp_metadata_ops {
> > diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
> > index 5cdbfbf9a7dc..c90fac9a9b2e 100644
> > --- a/net/core/dev_ioctl.c
> > +++ b/net/core/dev_ioctl.c
> > @@ -277,6 +277,39 @@ static int dev_siocbond(struct net_device *dev,
> >       return -EOPNOTSUPP;
> >  }
> >
> > +static int dev_hwtstamp(struct net_device *dev, struct ifreq *ifr,
> > +                     unsigned int cmd)
> > +{
> > +     const struct net_device_ops *ops = dev->netdev_ops;
> > +     int err;
> > +     struct hwtstamp_config config;
>
> nit: reorder int err after config we like lines longest to shortest

Will do. Thank you for pointing it out!

>
> > +
> > +     if ((cmd == SIOCGHWTSTAMP && !ops->ndo_hwtstamp_get) ||
> > +         (cmd == SIOCSHWTSTAMP && !ops->ndo_hwtstamp_set))
> > +             return dev_eth_ioctl(dev, ifr, cmd);
> > +
> > +     err = dsa_ndo_eth_ioctl(dev, ifr, cmd);
> > +     if (err == 0 || err != -EOPNOTSUPP)
> > +             return err;
> > +
> > +     if (!netif_device_present(dev))
> > +             return -ENODEV;
> > +
> > +     if (cmd == SIOCSHWTSTAMP) {
> > +             if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
> > +                     err = -EFAULT;
> > +             else
> > +                     err = ops->ndo_hwtstamp_set(dev, &config);
> > +     } else if (cmd == SIOCGHWTSTAMP) {
> > +             err = ops->ndo_hwtstamp_get(dev, &config);
> > +     }
> > +
> > +     if (err == 0)
> > +             err = copy_to_user(ifr->ifr_data, &config,
> > +                                sizeof(config)) ? -EFAULT : 0;
>
> nit: just error check each return value, don't try to save LoC

Will do!

>
> > +     return err;
> > +}
> > +
> >  static int dev_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
> >                             void __user *data, unsigned int cmd)
> >  {
> > @@ -391,11 +424,14 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
> >               rtnl_lock();
> >               return err;
> >
> > +     case SIOCGHWTSTAMP:
> > +             return dev_hwtstamp(dev, ifr, cmd);
> > +
> >       case SIOCSHWTSTAMP:
> >               err = net_hwtstamp_validate(ifr);
> >               if (err)
> >                       return err;
> > -             fallthrough;
> > +             return dev_hwtstamp(dev, ifr, cmd);
>
> Let's refactor this differently, we need net_hwtstamp_validate()
> to run on the same in-kernel copy as we'll send down to the driver.
> If we copy_from_user() twice we may validate a different thing
> than the driver will end up seeing (ToCToU).

Got it, that would be a nice optimization for the NDO execution path!
We still will need a version of net_hwtstamp_validate(struct ifreq *ifr)
to do validation for drivers not implementing ndo_hwtstamp_set().
Also we'll need to implement validation for dsa_ndo_eth_ioctl() which
usually has an empty implementation, but can do something
meaningful depending on kernel configuration if I understand
it correctly. I'm not sure where to insert the validation code for
the DSA code path, would greatly appreciate some advice here.

>
> TBH I'm not sure if keeping GET and SET in a common dev_hwtstamp()
> ends up being beneficial. If we fold in the validation check half
> of the code will be under and if (GET) or if (SET)..

I was on a fence about splitting dev_hwtstamp() into GET and SET versions.
If you believe separate implementations will provide a cleaner implementation
I'll be glad to split them.
Jakub Kicinski March 31, 2023, 6:10 p.m. UTC | #3
On Fri, 31 Mar 2023 11:51:06 -0600 Max Georgiev wrote:
> On Thu, Mar 30, 2023 at 11:35 PM Jakub Kicinski <kuba@kernel.org> wrote:
> >
> > On Thu, 30 Mar 2023 22:56:19 -0600 Maxim Georgiev wrote:  
> > > @@ -1642,6 +1650,10 @@ 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 hwtstamp_config *config);
> > > +     int                     (*ndo_hwtstamp_set)(struct net_device *dev,
> > > +                                                 struct hwtstamp_config *config);  
> >
> > I wonder if we should pass in
> >
> >         struct netlink_ext_ack *extack
> >
> > and maybe another structure for future extensions?
> > So we don't have to change the drivers again when we extend uAPI.  
> 
> Would these two extra parameters be ignored by drivers in this initial
> version of NDO hw timestamp API implementation?

Yup, and passed in as NULL.

See struct kernel_ethtool_coalesce for example of a kernel side
structure extending a fixed-size uAPI struct ethtool_coalesce.

So we would add a struct kernel_hwtstamp_config which would be 
empty for now, but we can make it not empty later.

Vladimir, does that sound reasonable or am I over-thinking?

> > > +     return err;
> > > +}
> > > +
> > >  static int dev_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
> > >                             void __user *data, unsigned int cmd)
> > >  {
> > > @@ -391,11 +424,14 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
> > >               rtnl_lock();
> > >               return err;
> > >
> > > +     case SIOCGHWTSTAMP:
> > > +             return dev_hwtstamp(dev, ifr, cmd);
> > > +
> > >       case SIOCSHWTSTAMP:
> > >               err = net_hwtstamp_validate(ifr);
> > >               if (err)
> > >                       return err;
> > > -             fallthrough;
> > > +             return dev_hwtstamp(dev, ifr, cmd);  
> >
> > Let's refactor this differently, we need net_hwtstamp_validate()
> > to run on the same in-kernel copy as we'll send down to the driver.
> > If we copy_from_user() twice we may validate a different thing
> > than the driver will end up seeing (ToCToU).  
> 
> Got it, that would be a nice optimization for the NDO execution path!
> We still will need a version of net_hwtstamp_validate(struct ifreq *ifr)
> to do validation for drivers not implementing ndo_hwtstamp_set().
> Also we'll need to implement validation for dsa_ndo_eth_ioctl() which
> usually has an empty implementation, but can do something
> meaningful depending on kernel configuration if I understand
> it correctly. I'm not sure where to insert the validation code for
> the DSA code path, would greatly appreciate some advice here.

You can copy from user space onto stack at the start of the new
dev_set_hwtstamp(), make validation run on the already-copied
version, and then either proceed to call the NDO with the on-stack
config which was validated or the legacy and DSA path with ifr.

> > TBH I'm not sure if keeping GET and SET in a common dev_hwtstamp()
> > ends up being beneficial. If we fold in the validation check half
> > of the code will be under and if (GET) or if (SET)..  
> 
> I was on a fence about splitting dev_hwtstamp() into GET and SET versions.
> If you believe separate implementations will provide a cleaner implementation
> I'll be glad to split them.
Vladimir Oltean April 1, 2023, 4:08 p.m. UTC | #4
On Thu, Mar 30, 2023 at 10:35:19PM -0700, Jakub Kicinski wrote:
> >  	case SIOCSHWTSTAMP:
> >  		err = net_hwtstamp_validate(ifr);
> >  		if (err)
> >  			return err;
> > -		fallthrough;
> > +		return dev_hwtstamp(dev, ifr, cmd);
> 
> Let's refactor this differently, we need net_hwtstamp_validate()
> to run on the same in-kernel copy as we'll send down to the driver.
> If we copy_from_user() twice we may validate a different thing
> than the driver will end up seeing (ToCToU).

I'm not sure I understand this. Since net_hwtstamp_validate() already
contains a copy_from_user() call, don't we already call copy_to_user()
twice (the second time being in all SIOCSHWTSTAMP handlers from drivers)?

Perhaps I don't understand what is it that can change the contents
of the ifreq structure, which would make this a potential issue for
ndo_hwtstamp_set() that isn't an issue for ndo_eth_ioctl()...
Jakub Kicinski April 1, 2023, 5:55 p.m. UTC | #5
On Sat, 1 Apr 2023 19:08:29 +0300 Vladimir Oltean wrote:
> > Let's refactor this differently, we need net_hwtstamp_validate()
> > to run on the same in-kernel copy as we'll send down to the driver.
> > If we copy_from_user() twice we may validate a different thing
> > than the driver will end up seeing (ToCToU).  
> 
> I'm not sure I understand this. Since net_hwtstamp_validate() already
> contains a copy_from_user() call, don't we already call copy_to_user()
> twice (the second time being in all SIOCSHWTSTAMP handlers from drivers)?

After this patch we'll be passing an in-kernel-space struct to drivers
rather than the ifr they have to copy themselves. I'm saying that we
should validate that exact copy, rather than copy, validate, copy, pass
to drivers, cause user space may change the values between the two
copies.

Unlikely to cause serious bugs but seems like a good code hygiene.

This is only for the drivers converted to the NDO, obviously, 
the legacy drivers will still have to copy themselves.
Vladimir Oltean April 1, 2023, 6:16 p.m. UTC | #6
On Fri, Mar 31, 2023 at 11:10:41AM -0700, Jakub Kicinski wrote:
> On Fri, 31 Mar 2023 11:51:06 -0600 Max Georgiev wrote:
> > On Thu, Mar 30, 2023 at 11:35 PM Jakub Kicinski <kuba@kernel.org> wrote:
> > > I wonder if we should pass in
> > >
> > >         struct netlink_ext_ack *extack
> > >
> > > and maybe another structure for future extensions?
> > > So we don't have to change the drivers again when we extend uAPI.  
> > 
> > Would these two extra parameters be ignored by drivers in this initial
> > version of NDO hw timestamp API implementation?
> 
> Yup, and passed in as NULL.
> 
> See struct kernel_ethtool_coalesce for example of a kernel side
> structure extending a fixed-size uAPI struct ethtool_coalesce.
> 
> So we would add a struct kernel_hwtstamp_config which would be 
> empty for now, but we can make it not empty later.
> 
> Vladimir, does that sound reasonable or am I over-thinking?

So in principle I'm okay with the NULL extack (even though we could consider
doing something with the netlink message instead of letting it go to waste;
a suggestion may be to print the _msg to the kernel log, like store_bridge_parm()
does).

I missed the discussions around the creation of struct kernel_ethtool_coalesce,
but I imagine that ethtool_ops->set_coalesce() now takes both struct ethtool_coalesce,
as well as struct kernel_ethtool_coalesce, to avoid refactoring drivers even further
than just patching their function prototype? I don't think that argument would
apply here, for a completely new API?

I'm also okay, in principle, with having a struct kernel_hwtstamp_config, but
I have two problems with the way in which you've suggested it be implemented.
The first is not really only my problem, but rather, I don't think you
can have empty structures in C - I tried!

/* C doesn't allow empty structures, bah! */
struct sja1105_tas_data {
	u8 dummy;
};

The second is that I would actively dislike an ndo_hwtstamp_set() API
where half of the arguments are in one structure and half in the other.
I believe it's much easier, and cleaner, to make struct kernel_hwtstamp_config
duplicate the exact same fields from struct hwtstamp_config, and copy
those fields one by one from one structure to the other (to avoid issues
with UAPI field alignment mismatches). So we could pass only the extensible
kernel_hwtstamp_config to ndo_hwtstamp_set() and ndo_hwtstamp_get().
Vladimir Oltean April 1, 2023, 6:20 p.m. UTC | #7
On Sat, Apr 01, 2023 at 10:55:33AM -0700, Jakub Kicinski wrote:
> On Sat, 1 Apr 2023 19:08:29 +0300 Vladimir Oltean wrote:
> > > Let's refactor this differently, we need net_hwtstamp_validate()
> > > to run on the same in-kernel copy as we'll send down to the driver.
> > > If we copy_from_user() twice we may validate a different thing
> > > than the driver will end up seeing (ToCToU).  
> > 
> > I'm not sure I understand this. Since net_hwtstamp_validate() already
> > contains a copy_from_user() call, don't we already call copy_to_user()
> > twice (the second time being in all SIOCSHWTSTAMP handlers from drivers)?
> 
> After this patch we'll be passing an in-kernel-space struct to drivers
> rather than the ifr they have to copy themselves. I'm saying that we
> should validate that exact copy, rather than copy, validate, copy, pass
> to drivers, cause user space may change the values between the two
> copies.
> 
> Unlikely to cause serious bugs but seems like a good code hygiene.
> 
> This is only for the drivers converted to the NDO, obviously, 
> the legacy drivers will still have to copy themselves.

Could you answer my second paragraph too, please?

| Perhaps I don't understand what is it that can change the contents
| of the ifreq structure, which would make this a potential issue for
| ndo_hwtstamp_set() that isn't an issue for ndo_eth_ioctl()...

I don't disagree with minimizing the number of copy_to_user() calls, but
I don't understand the ToCToU argument that you're bringing....
Vladimir Oltean April 1, 2023, 6:22 p.m. UTC | #8
On Sat, Apr 01, 2023 at 09:20:58PM +0300, Vladimir Oltean wrote:
> I don't disagree with minimizing the number of copy_to_user() calls

from* user
Vladimir Oltean April 1, 2023, 7:12 p.m. UTC | #9
On Fri, Mar 31, 2023 at 11:10:41AM -0700, Jakub Kicinski wrote:
> > We still will need a version of net_hwtstamp_validate(struct ifreq *ifr)
> > to do validation for drivers not implementing ndo_hwtstamp_set().
> > Also we'll need to implement validation for dsa_ndo_eth_ioctl() which
> > usually has an empty implementation, but can do something
> > meaningful depending on kernel configuration if I understand
> > it correctly. I'm not sure where to insert the validation code for
> > the DSA code path, would greatly appreciate some advice here.
> 
> You can copy from user space onto stack at the start of the new
> dev_set_hwtstamp(), make validation run on the already-copied
> version, and then either proceed to call the NDO with the on-stack
> config which was validated or the legacy and DSA path with ifr.

Actually, and here is the problem, DSA will want to see the timestamping
request with the new code path too, not just with the legacy one.
But, in this form, the dsa_ndo_eth_ioctl() -> dsa_master_ioctl() code
path wants to do one of two things: it either denies the configuration,
or passes it further, unchanged, to the master's netdev_ops->ndo_eth_ioctl().

By being written around the legacy ndo_eth_ioctl(), dsa_ndo_eth_ioctl()
places a requirement which conflicts with any attempt to convert any
kernel driver to the new API, because basically any net device can serve
as a DSA master, and simply put, DSA wants to see timestamping requests
to the DSA master, old or new API.

The only "useful" piece of logic from dsa_master_ioctl() is to deny the
hwtstamp_set operation in some cases, so it's clear that it's useless
for dsa_master_ioctl() to have to call the master's netdev_ops->ndo_eth_ioctl()
when dev_eth_ioctl() already would have done it anyway.

I can make dsa_ndo_eth_ioctl() disappear and replace it with a netdev
notifier as per this patch:
https://lore.kernel.org/netdev/20220317225035.3475538-1-vladimir.oltean@nxp.com/

My understanding of Jakub's objection is that the scope of the
NETDEV_ETH_IOCTL is too wide, and as such, it would need to change to
something like NETDEV_HWTSTAMP_SET. I can make that change if that is
the only objection, and resubmit that as preparation work for the
ndo_hwtstamp_set() effort.
Jakub Kicinski April 1, 2023, 7:14 p.m. UTC | #10
On Sat, 1 Apr 2023 21:20:58 +0300 Vladimir Oltean wrote:
> > After this patch we'll be passing an in-kernel-space struct to drivers
> > rather than the ifr they have to copy themselves. I'm saying that we
> > should validate that exact copy, rather than copy, validate, copy, pass
> > to drivers, cause user space may change the values between the two
> > copies.
> > 
> > Unlikely to cause serious bugs but seems like a good code hygiene.
> > 
> > This is only for the drivers converted to the NDO, obviously, 
> > the legacy drivers will still have to copy themselves.  
> 
> Could you answer my second paragraph too, please?
> 
> | Perhaps I don't understand what is it that can change the contents
> | of the ifreq structure, which would make this a potential issue for
> | ndo_hwtstamp_set() that isn't an issue for ndo_eth_ioctl()...
> 
> I don't disagree with minimizing the number of copy_to_user() calls, but
> I don't understand the ToCToU argument that you're bringing....

As I said, just code hygiene, I haven't looked at the drivers.
Seems too obvious to invest time trying to come up with an exact
scenario.

Do you see a reason not to code this the right way?
Vladimir Oltean April 1, 2023, 7:19 p.m. UTC | #11
On Sat, Apr 01, 2023 at 12:14:51PM -0700, Jakub Kicinski wrote:
> Do you see a reason not to code this the right way?

No. I didn't understand the justification you brought, and asked for
clarifications.
Jakub Kicinski April 1, 2023, 7:24 p.m. UTC | #12
On Sat, 1 Apr 2023 22:12:15 +0300 Vladimir Oltean wrote:
> Actually, and here is the problem, DSA will want to see the timestamping
> request with the new code path too, not just with the legacy one.
> But, in this form, the dsa_ndo_eth_ioctl() -> dsa_master_ioctl() code
> path wants to do one of two things: it either denies the configuration,
> or passes it further, unchanged, to the master's netdev_ops->ndo_eth_ioctl().
> 
> By being written around the legacy ndo_eth_ioctl(), dsa_ndo_eth_ioctl()
> places a requirement which conflicts with any attempt to convert any
> kernel driver to the new API, because basically any net device can serve
> as a DSA master, and simply put, DSA wants to see timestamping requests
> to the DSA master, old or new API.
> 
> The only "useful" piece of logic from dsa_master_ioctl() is to deny the
> hwtstamp_set operation in some cases, so it's clear that it's useless
> for dsa_master_ioctl() to have to call the master's netdev_ops->ndo_eth_ioctl()
> when dev_eth_ioctl() already would have done it anyway.

So the current patch can only convert drivers which can't be a DSA
master :( (realistically any big iron NIC for example)
It should be relatively easy to plumb both the ifr and the in-kernel
config thru all the DSA APIs and have it call the right helper, too,
tho? SMOC?

> I can make dsa_ndo_eth_ioctl() disappear and replace it with a netdev
> notifier as per this patch:
> https://lore.kernel.org/netdev/20220317225035.3475538-1-vladimir.oltean@nxp.com/
> 
> My understanding of Jakub's objection is that the scope of the
> NETDEV_ETH_IOCTL is too wide, and as such, it would need to change to
> something like NETDEV_HWTSTAMP_SET. I can make that change if that is
> the only objection, and resubmit that as preparation work for the
> ndo_hwtstamp_set() effort.

My objection to the IOCTL is that there's a lot of boilerplate that 
the drivers have to copy and that it makes it harder to do meaningful
work in the core.
Vladimir Oltean April 1, 2023, 7:30 p.m. UTC | #13
On Sat, Apr 01, 2023 at 12:24:50PM -0700, Jakub Kicinski wrote:
> On Sat, 1 Apr 2023 22:12:15 +0300 Vladimir Oltean wrote:
> > My understanding of Jakub's objection is that the scope of the
> > NETDEV_ETH_IOCTL is too wide, and as such, it would need to change to
> > something like NETDEV_HWTSTAMP_SET. I can make that change if that is
> > the only objection, and resubmit that as preparation work for the
> > ndo_hwtstamp_set() effort.
> 
> My objection to the IOCTL is that there's a lot of boilerplate that 
> the drivers have to copy and that it makes it harder to do meaningful
> work in the core.

By "NETDEV_ETH_IOCTL" I mean a netdev notifier with this name, and so,
I was expecting objections to that...

I can change that notifier proposal to be scoped only to the hwtstamping
operation, and that notifier would be 100% orthogonal to which API is
used to get/set hwtstamping, ndo_eth_ioctl() or ndo_hwtstamp_set().
Vladimir Oltean April 1, 2023, 8:18 p.m. UTC | #14
On Sat, Apr 01, 2023 at 12:24:50PM -0700, Jakub Kicinski wrote:
> It should be relatively easy to plumb both the ifr and the in-kernel
> config thru all the DSA APIs and have it call the right helper, too,
> tho? SMOC?

Sorry, this does not compute.

"Plumbing the in-kernel config thru all the DSA APIs" would be the
netdev notifier that I proposed one year ago. I just need you to say
"yes, sounds ok, let's see how that looks with last year's feedback
addressed".
Max Georgiev April 2, 2023, 2:28 p.m. UTC | #15
On Sat, Apr 1, 2023 at 2:18 PM Vladimir Oltean <vladimir.oltean@nxp.com> wrote:
>
> On Sat, Apr 01, 2023 at 12:24:50PM -0700, Jakub Kicinski wrote:
> > It should be relatively easy to plumb both the ifr and the in-kernel
> > config thru all the DSA APIs and have it call the right helper, too,
> > tho? SMOC?
>
> Sorry, this does not compute.
>
> "Plumbing the in-kernel config thru all the DSA APIs" would be the
> netdev notifier that I proposed one year ago. I just need you to say
> "yes, sounds ok, let's see how that looks with last year's feedback
> addressed".

I sent out a second iteration of the ndo_hwtstamp_get/set patch.
I tried to address all the comments except fixing DSA API - I still
don't have a full understanding of what is the plan there.
Vladimir Oltean April 2, 2023, 4:56 p.m. UTC | #16
On Sun, Apr 02, 2023 at 08:28:08AM -0600, Max Georgiev wrote:
> On Sat, Apr 1, 2023 at 2:18 PM Vladimir Oltean <vladimir.oltean@nxp.com> wrote:
> >
> > On Sat, Apr 01, 2023 at 12:24:50PM -0700, Jakub Kicinski wrote:
> > > It should be relatively easy to plumb both the ifr and the in-kernel
> > > config thru all the DSA APIs and have it call the right helper, too,
> > > tho? SMOC?
> >
> > Sorry, this does not compute.
> >
> > "Plumbing the in-kernel config thru all the DSA APIs" would be the
> > netdev notifier that I proposed one year ago. I just need you to say
> > "yes, sounds ok, let's see how that looks with last year's feedback
> > addressed".
> 
> I sent out a second iteration of the ndo_hwtstamp_get/set patch.
> I tried to address all the comments except fixing DSA API - I still
> don't have a full understanding of what is the plan there.

Well, my plan is for this patch set (to which you were copied before
sending out your v2) to be merged, then you could see what remains to be
done on top of that, and that should be easy-peasy:
https://patchwork.kernel.org/project/netdevbpf/cover/20230402123755.2592507-1-vladimir.oltean@nxp.com/

I think that patch set explains quite clearly what's with DSA's API and
what I intend it to become.
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 6f5c16aebcbf..887be333349b 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -6161,7 +6161,7 @@  static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr,
 /**
  * e1000e_hwtstamp_set - control hardware time stamping
  * @netdev: network interface device structure
- * @ifr: interface request
+ * @config: hwtstamp_config structure containing request parameters
  *
  * Outgoing time stamping can be enabled and disabled. Play nice and
  * disable it when requested, although it shouldn't cause any overhead
@@ -6174,20 +6174,17 @@  static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr,
  * specified. Matching the kind of event packet is not supported, with the
  * exception of "all V2 events regardless of level 2 or 4".
  **/
-static int e1000e_hwtstamp_set(struct net_device *netdev, struct ifreq *ifr)
+static int e1000e_hwtstamp_set(struct net_device *netdev,
+			       struct hwtstamp_config *config)
 {
 	struct e1000_adapter *adapter = netdev_priv(netdev);
-	struct hwtstamp_config config;
 	int ret_val;
 
-	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
-		return -EFAULT;
-
-	ret_val = e1000e_config_hwtstamp(adapter, &config);
+	ret_val = e1000e_config_hwtstamp(adapter, config);
 	if (ret_val)
 		return ret_val;
 
-	switch (config.rx_filter) {
+	switch (config->rx_filter) {
 	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
 	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
 	case HWTSTAMP_FILTER_PTP_V2_SYNC:
@@ -6199,22 +6196,22 @@  static int e1000e_hwtstamp_set(struct net_device *netdev, struct ifreq *ifr)
 		 * by hardware so notify the caller the requested packets plus
 		 * some others are time stamped.
 		 */
-		config.rx_filter = HWTSTAMP_FILTER_SOME;
+		config->rx_filter = HWTSTAMP_FILTER_SOME;
 		break;
 	default:
 		break;
 	}
-
-	return copy_to_user(ifr->ifr_data, &config,
-			    sizeof(config)) ? -EFAULT : 0;
+	return ret_val;
 }
 
-static int e1000e_hwtstamp_get(struct net_device *netdev, struct ifreq *ifr)
+static int e1000e_hwtstamp_get(struct net_device *netdev,
+			       struct hwtstamp_config *config)
 {
 	struct e1000_adapter *adapter = netdev_priv(netdev);
 
-	return copy_to_user(ifr->ifr_data, &adapter->hwtstamp_config,
-			    sizeof(adapter->hwtstamp_config)) ? -EFAULT : 0;
+	memcpy(config, &adapter->hwtstamp_config,
+	       sizeof(adapter->hwtstamp_config));
+	return 0;
 }
 
 static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
@@ -6224,10 +6221,6 @@  static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
 	case SIOCGMIIREG:
 	case SIOCSMIIREG:
 		return e1000_mii_ioctl(netdev, ifr, cmd);
-	case SIOCSHWTSTAMP:
-		return e1000e_hwtstamp_set(netdev, ifr);
-	case SIOCGHWTSTAMP:
-		return e1000e_hwtstamp_get(netdev, ifr);
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -7365,6 +7358,8 @@  static const struct net_device_ops e1000e_netdev_ops = {
 	.ndo_set_features = e1000_set_features,
 	.ndo_fix_features = e1000_fix_features,
 	.ndo_features_check	= passthru_features_check,
+	.ndo_hwtstamp_get	= e1000e_hwtstamp_get,
+	.ndo_hwtstamp_set	= e1000e_hwtstamp_set,
 };
 
 /**
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 35fa1ca98671..c5c1835e3904 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -238,6 +238,24 @@  nsim_set_features(struct net_device *dev, netdev_features_t features)
 	return 0;
 }
 
+static int
+nsim_hwtstamp_get(struct net_device *dev, struct hwtstamp_config *config)
+{
+	struct netdevsim *ns = netdev_priv(dev);
+
+	memcpy(config, &ns->hw_tstamp_config, sizeof(ns->hw_tstamp_config));
+	return 0;
+}
+
+static int
+nsim_hwtstamp_ges(struct net_device *dev, struct hwtstamp_config *config)
+{
+	struct netdevsim *ns = netdev_priv(dev);
+
+	memcpy(&ns->hw_tstamp_config, config, sizeof(ns->hw_tstamp_config));
+	return 0;
+}
+
 static const struct net_device_ops nsim_netdev_ops = {
 	.ndo_start_xmit		= nsim_start_xmit,
 	.ndo_set_rx_mode	= nsim_set_rx_mode,
@@ -256,6 +274,8 @@  static const struct net_device_ops nsim_netdev_ops = {
 	.ndo_setup_tc		= nsim_setup_tc,
 	.ndo_set_features	= nsim_set_features,
 	.ndo_bpf		= nsim_bpf,
+	.ndo_hwtstamp_get	= nsim_hwtstamp_get,
+	.ndo_hwtstamp_set	= nsim_hwtstamp_get,
 };
 
 static const struct net_device_ops nsim_vf_netdev_ops = {
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index 7d8ed8d8df5c..c6efd2383552 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -102,6 +102,7 @@  struct netdevsim {
 	} udp_ports;
 
 	struct nsim_ethtool ethtool;
+	struct hwtstamp_config hw_tstamp_config;
 };
 
 struct netdevsim *
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 7621c512765f..aeab126baa22 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -57,6 +57,7 @@ 
 struct netpoll_info;
 struct device;
 struct ethtool_ops;
+struct hwtstamp_config;
 struct phy_device;
 struct dsa_port;
 struct ip_tunnel_parm;
@@ -1408,6 +1409,13 @@  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 hwtstamp_config *config);
+ *	Get hardware timestamping parameters currently configured  for NIC
+ *	device.
+ *	int (*ndo_hwtstamp_set)(struct net_device *dev,
+ *				struct hwtstamp_config *config);
+ *	Set hardware timestamping parameters for NIC device.
  */
 struct net_device_ops {
 	int			(*ndo_init)(struct net_device *dev);
@@ -1642,6 +1650,10 @@  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 hwtstamp_config *config);
+	int			(*ndo_hwtstamp_set)(struct net_device *dev,
+						    struct hwtstamp_config *config);
 };
 
 struct xdp_metadata_ops {
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 5cdbfbf9a7dc..c90fac9a9b2e 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -277,6 +277,39 @@  static int dev_siocbond(struct net_device *dev,
 	return -EOPNOTSUPP;
 }
 
+static int dev_hwtstamp(struct net_device *dev, struct ifreq *ifr,
+			unsigned int cmd)
+{
+	const struct net_device_ops *ops = dev->netdev_ops;
+	int err;
+	struct hwtstamp_config config;
+
+	if ((cmd == SIOCGHWTSTAMP && !ops->ndo_hwtstamp_get) ||
+	    (cmd == SIOCSHWTSTAMP && !ops->ndo_hwtstamp_set))
+		return dev_eth_ioctl(dev, ifr, cmd);
+
+	err = dsa_ndo_eth_ioctl(dev, ifr, cmd);
+	if (err == 0 || err != -EOPNOTSUPP)
+		return err;
+
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	if (cmd == SIOCSHWTSTAMP) {
+		if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+			err = -EFAULT;
+		else
+			err = ops->ndo_hwtstamp_set(dev, &config);
+	} else if (cmd == SIOCGHWTSTAMP) {
+		err = ops->ndo_hwtstamp_get(dev, &config);
+	}
+
+	if (err == 0)
+		err = copy_to_user(ifr->ifr_data, &config,
+				   sizeof(config)) ? -EFAULT : 0;
+	return err;
+}
+
 static int dev_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
 			      void __user *data, unsigned int cmd)
 {
@@ -391,11 +424,14 @@  static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
 		rtnl_lock();
 		return err;
 
+	case SIOCGHWTSTAMP:
+		return dev_hwtstamp(dev, ifr, cmd);
+
 	case SIOCSHWTSTAMP:
 		err = net_hwtstamp_validate(ifr);
 		if (err)
 			return err;
-		fallthrough;
+		return dev_hwtstamp(dev, ifr, cmd);
 
 	/*
 	 *	Unknown or private ioctl
@@ -407,9 +443,7 @@  static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
 
 		if (cmd == SIOCGMIIPHY ||
 		    cmd == SIOCGMIIREG ||
-		    cmd == SIOCSMIIREG ||
-		    cmd == SIOCSHWTSTAMP ||
-		    cmd == SIOCGHWTSTAMP) {
+		    cmd == SIOCSMIIREG) {
 			err = dev_eth_ioctl(dev, ifr, cmd);
 		} else if (cmd == SIOCBONDENSLAVE ||
 		    cmd == SIOCBONDRELEASE ||