diff mbox series

[net-next,v20,10/13] rtase: Implement ethtool function

Message ID 20240607084321.7254-11-justinlai0215@realtek.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series Add Realtek automotive PCIe driver | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
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 success Errors and warnings before: 8 this patch: 8
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 5 of 5 maintainers
netdev/build_clang success Errors and warnings before: 8 this patch: 8
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 success Errors and warnings before: 8 this patch: 8
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 112 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Justin Lai June 7, 2024, 8:43 a.m. UTC
Implement the ethtool function to support users to obtain network card
information, including obtaining various device settings, Report whether
physical link is up, Report pause parameters, Set pause parameters,
Return a set of strings that describe the requested objects, Get number
of strings that @get_strings will write, Return extended statistics
about the device.

Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
 .../net/ethernet/realtek/rtase/rtase_main.c   | 103 ++++++++++++++++++
 1 file changed, 103 insertions(+)

Comments

Jakub Kicinski June 13, 2024, 12:35 a.m. UTC | #1
On Fri, 7 Jun 2024 16:43:18 +0800 Justin Lai wrote:
> Implement the ethtool function to support users to obtain network card
> information, including obtaining various device settings, Report whether
> physical link is up, Report pause parameters, Set pause parameters,
> Return a set of strings that describe the requested objects, Get number
> of strings that @get_strings will write, Return extended statistics
> about the device.

You don't implement get_strings any more.

> +static void rtase_get_drvinfo(struct net_device *dev,
> +			      struct ethtool_drvinfo *drvinfo)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +
> +	strscpy(drvinfo->driver, KBUILD_MODNAME, 32);

sizeof(drvinfo->driver) instead of the literal 32?

> +	strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);

Can you double check that overwriting these fields is actually needed?
I think core will fill this in for you in ethtool_get_drvinfo()

> +	if ((value & (RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN)) ==
> +	    (RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN)) {
> +		pause->rx_pause = 1;
> +		pause->tx_pause = 1;
> +	} else if ((value & RTASE_FORCE_TXFLOW_EN)) {

unnecessary parenthesis

> +		pause->tx_pause = 1;
> +	} else if ((value & RTASE_FORCE_RXFLOW_EN)) {

same here

> +		pause->rx_pause = 1;
> +	}
> +}
> +
> +static int rtase_set_pauseparam(struct net_device *dev,
> +				struct ethtool_pauseparam *pause)
> +{
> +	const struct rtase_private *tp = netdev_priv(dev);
> +	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> +
> +	if (pause->autoneg)
> +		return -EOPNOTSUPP;
> +
> +	value &= ~(RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN);
> +
> +	if (pause->tx_pause)
> +		value |= RTASE_FORCE_TXFLOW_EN;
> +
> +	if (pause->rx_pause)
> +		value |= RTASE_FORCE_RXFLOW_EN;
> +
> +	rtase_w16(tp, RTASE_CPLUS_CMD, value);
> +	return 0;
> +}
> +
> +static void rtase_get_eth_mac_stats(struct net_device *dev,
> +				    struct ethtool_eth_mac_stats *stats)
> +{
> +	struct rtase_private *tp = netdev_priv(dev);
> +	const struct rtase_counters *counters;
> +
> +	counters = tp->tally_vaddr;
> +	if (!counters)

you fail probe if this is NULL, why check if here?

> +		return;
> +
> +	rtase_dump_tally_counter(tp);
> +
> +	stats->FramesTransmittedOK = le64_to_cpu(counters->tx_packets);
> +	stats->SingleCollisionFrames = le32_to_cpu(counters->tx_one_collision);
> +	stats->MultipleCollisionFrames =
> +		le32_to_cpu(counters->tx_multi_collision);
> +	stats->FramesReceivedOK = le64_to_cpu(counters->rx_packets);
> +	stats->FrameCheckSequenceErrors = le32_to_cpu(counters->rx_errors);

You dont report this in rtase_get_stats64() as crc errors, are these
really CRC / FCS errors or other errors?

> +	stats->AlignmentErrors = le16_to_cpu(counters->align_errors);
> +	stats->FramesAbortedDueToXSColls = le16_to_cpu(counters->tx_aborted);
> +	stats->FramesLostDueToIntMACXmitError =
> +		le64_to_cpu(counters->tx_errors);
> +	stats->FramesLostDueToIntMACRcvError =
> +		le16_to_cpu(counters->rx_missed);

Are you sure this is the correct statistic to report as?
What's the definition of rx_missed in the datasheet?

Also is 16 bits enough for a packet counter at 5Gbps?
Don't you have to periodically accumulate this counter so that it
doesn't wrap around?

> +	stats->MulticastFramesReceivedOK = le32_to_cpu(counters->rx_multicast);
> +	stats->BroadcastFramesReceivedOK = le64_to_cpu(counters->rx_broadcast);
> +}
> +
> +static const struct ethtool_ops rtase_ethtool_ops = {
> +	.get_drvinfo = rtase_get_drvinfo,
> +	.get_link = ethtool_op_get_link,
> +	.get_link_ksettings = rtase_get_settings,
> +	.get_pauseparam = rtase_get_pauseparam,
> +	.set_pauseparam = rtase_set_pauseparam,
> +	.get_eth_mac_stats = rtase_get_eth_mac_stats,
> +	.get_ts_info = ethtool_op_get_ts_info,
> +};
> +
>  static void rtase_init_netdev_ops(struct net_device *dev)
>  {
>  	dev->netdev_ops = &rtase_netdev_ops;
> +	dev->ethtool_ops = &rtase_ethtool_ops;
>  }
>  
>  static void rtase_reset_interrupt(struct pci_dev *pdev,
Justin Lai June 17, 2024, 6:54 a.m. UTC | #2
> On Fri, 7 Jun 2024 16:43:18 +0800 Justin Lai wrote:
> > Implement the ethtool function to support users to obtain network card
> > information, including obtaining various device settings, Report
> > whether physical link is up, Report pause parameters, Set pause
> > parameters, Return a set of strings that describe the requested
> > objects, Get number of strings that @get_strings will write, Return
> > extended statistics about the device.
> 
> You don't implement get_strings any more.

Sorry, I will modify it.

> 
> > +static void rtase_get_drvinfo(struct net_device *dev,
> > +                           struct ethtool_drvinfo *drvinfo) {
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +
> > +     strscpy(drvinfo->driver, KBUILD_MODNAME, 32);
> 
> sizeof(drvinfo->driver) instead of the literal 32?

It seems like a better approach, I'll switch to using
sizeof(drvinfo->driver), thank you.

> 
> > +     strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);
> 
> Can you double check that overwriting these fields is actually needed?
> I think core will fill this in for you in ethtool_get_drvinfo()

I have removed this line of code for testing. Before removing the code,
I could obtain bus info by entering "ethtool -i". However, after removing
the code, entering "ethtool -i" no longer retrieves the bus info.
Therefore, I believe that this line of code is still necessary.

> 
> > +     if ((value & (RTASE_FORCE_TXFLOW_EN |
> RTASE_FORCE_RXFLOW_EN)) ==
> > +         (RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN)) {
> > +             pause->rx_pause = 1;
> > +             pause->tx_pause = 1;
> > +     } else if ((value & RTASE_FORCE_TXFLOW_EN)) {
> 
> unnecessary parenthesis
> 
> > +             pause->tx_pause = 1;
> > +     } else if ((value & RTASE_FORCE_RXFLOW_EN)) {
> 
> same here
> 

Sorry, I will remove the unnecessary parentheses, thank you.

> > +             pause->rx_pause = 1;
> > +     }
> > +}
> > +
> > +static int rtase_set_pauseparam(struct net_device *dev,
> > +                             struct ethtool_pauseparam *pause) {
> > +     const struct rtase_private *tp = netdev_priv(dev);
> > +     u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
> > +
> > +     if (pause->autoneg)
> > +             return -EOPNOTSUPP;
> > +
> > +     value &= ~(RTASE_FORCE_TXFLOW_EN |
> RTASE_FORCE_RXFLOW_EN);
> > +
> > +     if (pause->tx_pause)
> > +             value |= RTASE_FORCE_TXFLOW_EN;
> > +
> > +     if (pause->rx_pause)
> > +             value |= RTASE_FORCE_RXFLOW_EN;
> > +
> > +     rtase_w16(tp, RTASE_CPLUS_CMD, value);
> > +     return 0;
> > +}
> > +
> > +static void rtase_get_eth_mac_stats(struct net_device *dev,
> > +                                 struct ethtool_eth_mac_stats
> *stats)
> > +{
> > +     struct rtase_private *tp = netdev_priv(dev);
> > +     const struct rtase_counters *counters;
> > +
> > +     counters = tp->tally_vaddr;
> > +     if (!counters)
> 
> you fail probe if this is NULL, why check if here?
> 

Sorry, this check seems unnecessary, I will remove it.

> > +             return;
> > +
> > +     rtase_dump_tally_counter(tp);
> > +
> > +     stats->FramesTransmittedOK = le64_to_cpu(counters->tx_packets);
> > +     stats->SingleCollisionFrames =
> le32_to_cpu(counters->tx_one_collision);
> > +     stats->MultipleCollisionFrames =
> > +             le32_to_cpu(counters->tx_multi_collision);
> > +     stats->FramesReceivedOK = le64_to_cpu(counters->rx_packets);
> > +     stats->FrameCheckSequenceErrors =
> > + le32_to_cpu(counters->rx_errors);
> 
> You dont report this in rtase_get_stats64() as crc errors, are these really CRC /
> FCS errors or other errors?

Our rx_error indeed refers to crc_error. I will assign the value of
rx_error to the crc_error in rtase_get_stats64().

> 
> > +     stats->AlignmentErrors = le16_to_cpu(counters->align_errors);
> > +     stats->FramesAbortedDueToXSColls =
> le16_to_cpu(counters->tx_aborted);
> > +     stats->FramesLostDueToIntMACXmitError =
> > +             le64_to_cpu(counters->tx_errors);
> > +     stats->FramesLostDueToIntMACRcvError =
> > +             le16_to_cpu(counters->rx_missed);
> 
> Are you sure this is the correct statistic to report as?
> What's the definition of rx_missed in the datasheet?

What we refer to as rx miss is the packets that can't be received because
the fifo in the MAC is full. We consider this a type of MAC error, identical
to the definition of FramesLostDueToIntMACRcvError.

> 
> Also is 16 bits enough for a packet counter at 5Gbps?
> Don't you have to periodically accumulate this counter so that it doesn't wrap
> around?

Indeed, this counter may wrap, but we don't need to accumulate it, because
an increase in the number of rx_miss largely indicates that the system
processing speed is not fast enough. Therefore, the size of this counter
doesn't need to be too large.

> 
> > +     stats->MulticastFramesReceivedOK =
> le32_to_cpu(counters->rx_multicast);
> > +     stats->BroadcastFramesReceivedOK =
> > +le64_to_cpu(counters->rx_broadcast);
> > +}
> > +
> > +static const struct ethtool_ops rtase_ethtool_ops = {
> > +     .get_drvinfo = rtase_get_drvinfo,
> > +     .get_link = ethtool_op_get_link,
> > +     .get_link_ksettings = rtase_get_settings,
> > +     .get_pauseparam = rtase_get_pauseparam,
> > +     .set_pauseparam = rtase_set_pauseparam,
> > +     .get_eth_mac_stats = rtase_get_eth_mac_stats,
> > +     .get_ts_info = ethtool_op_get_ts_info, };
> > +
> >  static void rtase_init_netdev_ops(struct net_device *dev)  {
> >       dev->netdev_ops = &rtase_netdev_ops;
> > +     dev->ethtool_ops = &rtase_ethtool_ops;
> >  }
> >
> >  static void rtase_reset_interrupt(struct pci_dev *pdev,
Andrew Lunn June 17, 2024, 2:07 p.m. UTC | #3
> > > +     strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);
> > 
> > Can you double check that overwriting these fields is actually needed?
> > I think core will fill this in for you in ethtool_get_drvinfo()
> 
> I have removed this line of code for testing. Before removing the code,
> I could obtain bus info by entering "ethtool -i". However, after removing
> the code, entering "ethtool -i" no longer retrieves the bus info.

https://elixir.bootlin.com/linux/latest/source/net/ethtool/ioctl.c#L710

	if (ops->get_drvinfo) {
		ops->get_drvinfo(dev, &rsp->info);
		if (!rsp->info.bus_info[0] && parent)
			strscpy(rsp->info.bus_info, dev_name(parent),
				sizeof(rsp->info.bus_info));

This suggests you have not set the parent device.

	Andrew
Jakub Kicinski June 17, 2024, 3:10 p.m. UTC | #4
On Mon, 17 Jun 2024 06:54:59 +0000 Justin Lai wrote:
> > Are you sure this is the correct statistic to report as?
> > What's the definition of rx_missed in the datasheet?  
> 
> What we refer to as rx miss is the packets that can't be received because
> the fifo in the MAC is full. We consider this a type of MAC error, identical
> to the definition of FramesLostDueToIntMACRcvError.

Is this a FIFO which silicon designers say can't overflow?
Or it will overflow if the host is busy and doesn't pick up packets?
If it's the latter I recommend using stats64::rx_missed_errors.
That's the start we try to steer all NIC drivers towards for "host 
is too slow".

> > Also is 16 bits enough for a packet counter at 5Gbps?
> > Don't you have to periodically accumulate this counter so that it doesn't wrap
> > around?  
> 
> Indeed, this counter may wrap, but we don't need to accumulate it, because
> an increase in the number of rx_miss largely indicates that the system
> processing speed is not fast enough. Therefore, the size of this counter
> doesn't need to be too large.

Are you basically saying that since its an error it only matters if 
its zero or not? It's not going to be a great experience for anyone
trying to use this driver. You can read this counter periodically from
a timer and accumulate a fuller value in the driver. There's even
struct ethtool_coalesce::stats_block_coalesce_usecs if you want to let
user configure the period.
Justin Lai June 18, 2024, 7:28 a.m. UTC | #5
> On Mon, 17 Jun 2024 06:54:59 +0000 Justin Lai wrote:
> > > Are you sure this is the correct statistic to report as?
> > > What's the definition of rx_missed in the datasheet?
> >
> > What we refer to as rx miss is the packets that can't be received because
> > the fifo in the MAC is full. We consider this a type of MAC error, identical
> > to the definition of FramesLostDueToIntMACRcvError.
> 
> Is this a FIFO which silicon designers say can't overflow?
> Or it will overflow if the host is busy and doesn't pick up packets?
> If it's the latter I recommend using stats64::rx_missed_errors.
> That's the start we try to steer all NIC drivers towards for "host
> is too slow".

Our fifo falls under the second situation you mentioned, and we are
currently already using stats64::rx_missed_errors. Therefore, we will
remove the parts that use FramesLostDueToIntMACRcvError.

> 
> > > Also is 16 bits enough for a packet counter at 5Gbps?
> > > Don't you have to periodically accumulate this counter so that it doesn't
> wrap
> > > around?
> >
> > Indeed, this counter may wrap, but we don't need to accumulate it, because
> > an increase in the number of rx_miss largely indicates that the system
> > processing speed is not fast enough. Therefore, the size of this counter
> > doesn't need to be too large.
> 
> Are you basically saying that since its an error it only matters if
> its zero or not? It's not going to be a great experience for anyone
> trying to use this driver. You can read this counter periodically from
> a timer and accumulate a fuller value in the driver. There's even
> struct ethtool_coalesce::stats_block_coalesce_usecs if you want to let
> user configure the period.

As we've discussed, as long as this counter has a value, it can inform
the user that the host speed is too slow, and it will not affect other
transmission functions. Can we add this periodic reading function
after this patch version is merged?
Justin Lai June 18, 2024, 9:56 a.m. UTC | #6
> 
> > > > +     strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);
> > >
> > > Can you double check that overwriting these fields is actually needed?
> > > I think core will fill this in for you in ethtool_get_drvinfo()
> >
> > I have removed this line of code for testing. Before removing the
> > code, I could obtain bus info by entering "ethtool -i". However, after
> > removing the code, entering "ethtool -i" no longer retrieves the bus info.
> 
> https://elixir.bootlin.com/linux/latest/source/net/ethtool/ioctl.c#L710
> 
>         if (ops->get_drvinfo) {
>                 ops->get_drvinfo(dev, &rsp->info);
>                 if (!rsp->info.bus_info[0] && parent)
>                         strscpy(rsp->info.bus_info, dev_name(parent),
>                                 sizeof(rsp->info.bus_info));
> 
> This suggests you have not set the parent device.
> 
>         Andrew
Hi Andrew,
I understand your explanation. However, when we input ethtool -i,
shouldn't we aim to get the bus info of the actual device rather than
the parent device? That's why I think we need to add this line.
Jakub Kicinski June 18, 2024, 2:24 p.m. UTC | #7
On Tue, 18 Jun 2024 07:28:13 +0000 Justin Lai wrote:
> > Are you basically saying that since its an error it only matters if
> > its zero or not? It's not going to be a great experience for anyone
> > trying to use this driver. You can read this counter periodically from
> > a timer and accumulate a fuller value in the driver. There's even
> > struct ethtool_coalesce::stats_block_coalesce_usecs if you want to let
> > user configure the period.  
> 
> As we've discussed, as long as this counter has a value, it can inform
> the user that the host speed is too slow, and it will not affect other
> transmission functions. Can we add this periodic reading function
> after this patch version is merged?

You'd have to remove reporting of all 16b packet statistics and 32b
byte statistics completely for now, and then follow up adding them
with the periodic overflow checks.
Justin Lai June 19, 2024, 3:40 a.m. UTC | #8
> 
> On Tue, 18 Jun 2024 07:28:13 +0000 Justin Lai wrote:
> > > Are you basically saying that since its an error it only matters if
> > > its zero or not? It's not going to be a great experience for anyone
> > > trying to use this driver. You can read this counter periodically
> > > from a timer and accumulate a fuller value in the driver. There's
> > > even struct ethtool_coalesce::stats_block_coalesce_usecs if you want
> > > to let user configure the period.
> >
> > As we've discussed, as long as this counter has a value, it can inform
> > the user that the host speed is too slow, and it will not affect other
> > transmission functions. Can we add this periodic reading function
> > after this patch version is merged?
> 
> You'd have to remove reporting of all 16b packet statistics and 32b byte
> statistics completely for now, and then follow up adding them with the
> periodic overflow checks.

Thank you for your reply, I will remove the 16b packet statistics and 32b
byte statistics report in this version.
diff mbox series

Patch

diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index e9bbafc212c5..da2627ea99fe 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -1758,9 +1758,112 @@  static void rtase_get_mac_address(struct net_device *dev)
 	rtase_rar_set(tp, dev->dev_addr);
 }
 
+static void rtase_get_drvinfo(struct net_device *dev,
+			      struct ethtool_drvinfo *drvinfo)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+
+	strscpy(drvinfo->driver, KBUILD_MODNAME, 32);
+	strscpy(drvinfo->bus_info, pci_name(tp->pdev), 32);
+}
+
+static int rtase_get_settings(struct net_device *dev,
+			      struct ethtool_link_ksettings *cmd)
+{
+	u32 supported = SUPPORTED_MII | SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+
+	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
+						supported);
+	cmd->base.speed = SPEED_5000;
+	cmd->base.duplex = DUPLEX_FULL;
+	cmd->base.port = PORT_MII;
+	cmd->base.autoneg = AUTONEG_DISABLE;
+
+	return 0;
+}
+
+static void rtase_get_pauseparam(struct net_device *dev,
+				 struct ethtool_pauseparam *pause)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
+
+	pause->autoneg = AUTONEG_DISABLE;
+
+	if ((value & (RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN)) ==
+	    (RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN)) {
+		pause->rx_pause = 1;
+		pause->tx_pause = 1;
+	} else if ((value & RTASE_FORCE_TXFLOW_EN)) {
+		pause->tx_pause = 1;
+	} else if ((value & RTASE_FORCE_RXFLOW_EN)) {
+		pause->rx_pause = 1;
+	}
+}
+
+static int rtase_set_pauseparam(struct net_device *dev,
+				struct ethtool_pauseparam *pause)
+{
+	const struct rtase_private *tp = netdev_priv(dev);
+	u16 value = rtase_r16(tp, RTASE_CPLUS_CMD);
+
+	if (pause->autoneg)
+		return -EOPNOTSUPP;
+
+	value &= ~(RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN);
+
+	if (pause->tx_pause)
+		value |= RTASE_FORCE_TXFLOW_EN;
+
+	if (pause->rx_pause)
+		value |= RTASE_FORCE_RXFLOW_EN;
+
+	rtase_w16(tp, RTASE_CPLUS_CMD, value);
+	return 0;
+}
+
+static void rtase_get_eth_mac_stats(struct net_device *dev,
+				    struct ethtool_eth_mac_stats *stats)
+{
+	struct rtase_private *tp = netdev_priv(dev);
+	const struct rtase_counters *counters;
+
+	counters = tp->tally_vaddr;
+	if (!counters)
+		return;
+
+	rtase_dump_tally_counter(tp);
+
+	stats->FramesTransmittedOK = le64_to_cpu(counters->tx_packets);
+	stats->SingleCollisionFrames = le32_to_cpu(counters->tx_one_collision);
+	stats->MultipleCollisionFrames =
+		le32_to_cpu(counters->tx_multi_collision);
+	stats->FramesReceivedOK = le64_to_cpu(counters->rx_packets);
+	stats->FrameCheckSequenceErrors = le32_to_cpu(counters->rx_errors);
+	stats->AlignmentErrors = le16_to_cpu(counters->align_errors);
+	stats->FramesAbortedDueToXSColls = le16_to_cpu(counters->tx_aborted);
+	stats->FramesLostDueToIntMACXmitError =
+		le64_to_cpu(counters->tx_errors);
+	stats->FramesLostDueToIntMACRcvError =
+		le16_to_cpu(counters->rx_missed);
+	stats->MulticastFramesReceivedOK = le32_to_cpu(counters->rx_multicast);
+	stats->BroadcastFramesReceivedOK = le64_to_cpu(counters->rx_broadcast);
+}
+
+static const struct ethtool_ops rtase_ethtool_ops = {
+	.get_drvinfo = rtase_get_drvinfo,
+	.get_link = ethtool_op_get_link,
+	.get_link_ksettings = rtase_get_settings,
+	.get_pauseparam = rtase_get_pauseparam,
+	.set_pauseparam = rtase_set_pauseparam,
+	.get_eth_mac_stats = rtase_get_eth_mac_stats,
+	.get_ts_info = ethtool_op_get_ts_info,
+};
+
 static void rtase_init_netdev_ops(struct net_device *dev)
 {
 	dev->netdev_ops = &rtase_netdev_ops;
+	dev->ethtool_ops = &rtase_ethtool_ops;
 }
 
 static void rtase_reset_interrupt(struct pci_dev *pdev,