diff mbox

[net-next,v3,2/3] ethtool: Add generic options for tunables

Message ID 1408008560-1067-3-git-send-email-_govind@gmx.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Govindarajulu Varadarajan Aug. 14, 2014, 9:29 a.m. UTC
This patch adds new ethtool cmd, ETHTOOL_GTUNABLE & ETHTOOL_STUNABLE for getting
tunable values from driver.

Add ethtool_tunable_ops array to ethtool_ops. The array index will be
the tunable tcmd as defined in enum tunable_cmd. ethtool_tunable_ops has two
function pointers set & get. This is set by the driver for getting/setting
particular tunable.

Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com>
---
 include/linux/ethtool.h      |  6 ++++++
 include/uapi/linux/ethtool.h | 17 +++++++++++++++
 net/core/ethtool.c           | 51 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 74 insertions(+)

Comments

David Miller Aug. 21, 2014, 11:18 p.m. UTC | #1
From: Govindarajulu Varadarajan <_govind@gmx.com>
Date: Thu, 14 Aug 2014 14:59:19 +0530

> @@ -1621,6 +1621,50 @@ static int ethtool_get_module_eeprom(struct net_device *dev,
>  				      modinfo.eeprom_len);
>  }
>  
> +static int ethtool_get_tunable(struct net_device *dev, void __user *useraddr)
> +{
> +	int ret;
> +	struct ethtool_tunable tuna;
> +	const struct ethtool_tunable_ops *ops;
> +
> +	if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
> +		return -EFAULT;
> +	if (tuna.tcmd >= ETHTOOL_TUNABLE_MAX)
> +		return -EOPNOTSUPP;
> +	ops = &dev->ethtool_ops->tunable_ops[tuna.tcmd];
> +	if (!ops->get)
> +		return -EOPNOTSUPP;
> +	ret = ops->get(dev, &tuna);
> +	if (ret)
> +		return ret;
> +	if (copy_to_user(useraddr, &tuna, sizeof(tuna)))
> +		return -EFAULT;
> +
> +	return 0;
> +}

You should be validating tuna.len here, it should not be happening in the drivers
as I see you are doing in your enic implementation.

Also, having a seperate OP for each tunable might be overkill.
Consider instead one "get_tunable" and one "set_tunable" callback,
which contains a switch statement over 'tcmd'.  The generic
ethtool_{g,s}et_tunable() would still validate the length, so that the
driver method does not need to do so.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings Aug. 22, 2014, 11:26 p.m. UTC | #2
On Thu, 2014-08-14 at 14:59 +0530, Govindarajulu Varadarajan wrote:
> This patch adds new ethtool cmd, ETHTOOL_GTUNABLE & ETHTOOL_STUNABLE for getting
> tunable values from driver.
> 
> Add ethtool_tunable_ops array to ethtool_ops. The array index will be
> the tunable tcmd as defined in enum tunable_cmd. ethtool_tunable_ops has two
> function pointers set & get. This is set by the driver for getting/setting
> particular tunable.
> 
> Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com>
> ---
>  include/linux/ethtool.h      |  6 ++++++
>  include/uapi/linux/ethtool.h | 17 +++++++++++++++
>  net/core/ethtool.c           | 51 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 74 insertions(+)
> 
> diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
> index e658229..33bff94 100644
> --- a/include/linux/ethtool.h
> +++ b/include/linux/ethtool.h
> @@ -77,6 +77,11 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
>  	return index % n_rx_rings;
>  }
>  
> +struct ethtool_tunable_ops {
> +	int (*set)(struct net_device *, struct ethtool_tunable *);

The second pointer type should be const-qualified.

> +	int (*get)(struct net_device *, struct ethtool_tunable *);
> +};
> +
>  /**
>   * struct ethtool_ops - optional netdev operations
>   * @get_settings: Get various device settings including Ethernet link
> @@ -257,6 +262,7 @@ struct ethtool_ops {
>  				     struct ethtool_eeprom *, u8 *);
>  	int	(*get_eee)(struct net_device *, struct ethtool_eee *);
>  	int	(*set_eee)(struct net_device *, struct ethtool_eee *);
> +	struct ethtool_tunable_ops tunable_ops[ETHTOOL_TUNABLE_MAX];

This is OK but if we add a lot of tunables then it bloats up each driver
with a (probably quite sparse) array of function pointers.

>  
> 
>  };
> diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
> index e3c7a71..99e43ca 100644
> --- a/include/uapi/linux/ethtool.h
> +++ b/include/uapi/linux/ethtool.h
> @@ -209,6 +209,21 @@ struct ethtool_value {
>  	__u32	data;
>  };
>  
> +enum tunable_cmd {
> +	ETHTOOL_RX_COPYBREAK = 0,
> +	ETHTOOL_TUNABLE_MAX,
> +};
> +
> +struct ethtool_tunable {
> +	u32 cmd;
> +	u32 tcmd;
> +	u32 len;
> +	union {
> +		u32 rx_copybreak;
> +		u32 data[48];
> +	} data;
[...]

This is not at all generic.  If tunables don't all have the same type,
then the type - not just the length - should be explicit.

I also think that if the length of a value can vary then we should not
declare a data member at all.  So we would have something like:

struct ethtool_tunable {
	__u32	cmd;
	__u32	id;
	__u32	type_id;
	__u32	len;
	__u8	data[0];
};

Then the value buffer would be passed to the driver functions separately
(as for other variable-length command structures).

By the way, you must use double-underscore prefixes on fixed-width
integer type names in UAPI headers.

Ben.
Govindarajulu Varadarajan Aug. 26, 2014, 9:33 p.m. UTC | #3
On Fri, 22 Aug 2014, Ben Hutchings wrote:
> On Thu, 2014-08-14 at 14:59 +0530, Govindarajulu Varadarajan wrote:
>> @@ -257,6 +262,7 @@ struct ethtool_ops {
>>  				     struct ethtool_eeprom *, u8 *);
>>  	int	(*get_eee)(struct net_device *, struct ethtool_eee *);
>>  	int	(*set_eee)(struct net_device *, struct ethtool_eee *);
>> +	struct ethtool_tunable_ops tunable_ops[ETHTOOL_TUNABLE_MAX];
>
> This is OK but if we add a lot of tunables then it bloats up each driver
> with a (probably quite sparse) array of function pointers.
>

Will fix with David's suggestion.

>> +struct ethtool_tunable {
>> +	u32 cmd;
>> +	u32 tcmd;
>> +	u32 len;
>> +	union {
>> +		u32 rx_copybreak;
>> +		u32 data[48];
>> +	} data;
> [...]
>
> This is not at all generic.  If tunables don't all have the same type,
> then the type - not just the length - should be explicit.
>
> I also think that if the length of a value can vary then we should not
> declare a data member at all.  So we would have something like:
>
> struct ethtool_tunable {
> 	__u32	cmd;
> 	__u32	id;
> 	__u32	type_id;
> 	__u32	len;
> 	__u8	data[0];
> };
>
> Then the value buffer would be passed to the driver functions separately
> (as for other variable-length command structures).
>

OK. id show be the tunable command type right? Like RX_COPYBREAK, TX_COPYBREAK
etc. What is the type_id?

> By the way, you must use double-underscore prefixes on fixed-width
> integer type names in UAPI headers.
>

will fix it.

Thanks
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings Aug. 27, 2014, 6:59 a.m. UTC | #4
On Wed, 2014-08-27 at 03:03 +0530, Govindarajulu Varadarajan wrote:
> On Fri, 22 Aug 2014, Ben Hutchings wrote:
> > On Thu, 2014-08-14 at 14:59 +0530, Govindarajulu Varadarajan wrote:
> >> @@ -257,6 +262,7 @@ struct ethtool_ops {
> >>  				     struct ethtool_eeprom *, u8 *);
> >>  	int	(*get_eee)(struct net_device *, struct ethtool_eee *);
> >>  	int	(*set_eee)(struct net_device *, struct ethtool_eee *);
> >> +	struct ethtool_tunable_ops tunable_ops[ETHTOOL_TUNABLE_MAX];
> >
> > This is OK but if we add a lot of tunables then it bloats up each driver
> > with a (probably quite sparse) array of function pointers.
> >
> 
> Will fix with David's suggestion.
> 
> >> +struct ethtool_tunable {
> >> +	u32 cmd;
> >> +	u32 tcmd;
> >> +	u32 len;
> >> +	union {
> >> +		u32 rx_copybreak;
> >> +		u32 data[48];
> >> +	} data;
> > [...]
> >
> > This is not at all generic.  If tunables don't all have the same type,
> > then the type - not just the length - should be explicit.
> >
> > I also think that if the length of a value can vary then we should not
> > declare a data member at all.  So we would have something like:
> >
> > struct ethtool_tunable {
> > 	__u32	cmd;
> > 	__u32	id;
> > 	__u32	type_id;
> > 	__u32	len;
> > 	__u8	data[0];
> > };
> >
> > Then the value buffer would be passed to the driver functions separately
> > (as for other variable-length command structures).
> >
> 
> OK. id show be the tunable command type right? Like RX_COPYBREAK, TX_COPYBREAK
> etc. What is the type_id?

A number that identifies the type of the tunable (it might be u32, u64,
link-layer address, text, etc.).

Ben.

> > By the way, you must use double-underscore prefixes on fixed-width
> > integer type names in UAPI headers.
> >
> 
> will fix it.
> 
> Thanks
diff mbox

Patch

diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index e658229..33bff94 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -77,6 +77,11 @@  static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
 	return index % n_rx_rings;
 }
 
+struct ethtool_tunable_ops {
+	int (*set)(struct net_device *, struct ethtool_tunable *);
+	int (*get)(struct net_device *, struct ethtool_tunable *);
+};
+
 /**
  * struct ethtool_ops - optional netdev operations
  * @get_settings: Get various device settings including Ethernet link
@@ -257,6 +262,7 @@  struct ethtool_ops {
 				     struct ethtool_eeprom *, u8 *);
 	int	(*get_eee)(struct net_device *, struct ethtool_eee *);
 	int	(*set_eee)(struct net_device *, struct ethtool_eee *);
+	struct ethtool_tunable_ops tunable_ops[ETHTOOL_TUNABLE_MAX];
 
 
 };
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index e3c7a71..99e43ca 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -209,6 +209,21 @@  struct ethtool_value {
 	__u32	data;
 };
 
+enum tunable_cmd {
+	ETHTOOL_RX_COPYBREAK = 0,
+	ETHTOOL_TUNABLE_MAX,
+};
+
+struct ethtool_tunable {
+	u32 cmd;
+	u32 tcmd;
+	u32 len;
+	union {
+		u32 rx_copybreak;
+		u32 data[48];
+	} data;
+};
+
 /**
  * struct ethtool_regs - hardware register dump
  * @cmd: Command number = %ETHTOOL_GREGS
@@ -1152,6 +1167,8 @@  enum ethtool_sfeatures_retval_bits {
 
 #define ETHTOOL_GRSSH		0x00000046 /* Get RX flow hash configuration */
 #define ETHTOOL_SRSSH		0x00000047 /* Set RX flow hash configuration */
+#define ETHTOOL_GTUNABLE	0x00000048 /* Get Tunable  */
+#define ETHTOOL_STUNABLE	0x00000049 /* Set Tunable */
 
 /* compatibility with older code */
 #define SPARC_ETH_GSET		ETHTOOL_GSET
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 17cb912..0574c66 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -1621,6 +1621,50 @@  static int ethtool_get_module_eeprom(struct net_device *dev,
 				      modinfo.eeprom_len);
 }
 
+static int ethtool_get_tunable(struct net_device *dev, void __user *useraddr)
+{
+	int ret;
+	struct ethtool_tunable tuna;
+	const struct ethtool_tunable_ops *ops;
+
+	if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
+		return -EFAULT;
+	if (tuna.tcmd >= ETHTOOL_TUNABLE_MAX)
+		return -EOPNOTSUPP;
+	ops = &dev->ethtool_ops->tunable_ops[tuna.tcmd];
+	if (!ops->get)
+		return -EOPNOTSUPP;
+	ret = ops->get(dev, &tuna);
+	if (ret)
+		return ret;
+	if (copy_to_user(useraddr, &tuna, sizeof(tuna)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int ethtool_set_tunable(struct net_device *dev, void __user *useraddr)
+{
+	int ret;
+	struct ethtool_tunable tuna;
+	const struct ethtool_tunable_ops *ops;
+
+	if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
+		return -EFAULT;
+	if (tuna.tcmd >= ETHTOOL_TUNABLE_MAX)
+		return -EOPNOTSUPP;
+	ops = &dev->ethtool_ops->tunable_ops[tuna.tcmd];
+	if (!ops->set)
+		return -EOPNOTSUPP;
+	ret = ops->set(dev, &tuna);
+	if (ret)
+		return ret;
+	if (copy_to_user(useraddr, &tuna, sizeof(tuna)))
+		return -EFAULT;
+
+	return 0;
+}
+
 /* The main entry point in this file.  Called from net/core/dev_ioctl.c */
 
 int dev_ethtool(struct net *net, struct ifreq *ifr)
@@ -1670,6 +1714,7 @@  int dev_ethtool(struct net *net, struct ifreq *ifr)
 	case ETHTOOL_GCHANNELS:
 	case ETHTOOL_GET_TS_INFO:
 	case ETHTOOL_GEEE:
+	case ETHTOOL_GTUNABLE:
 		break;
 	default:
 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
@@ -1857,6 +1902,12 @@  int dev_ethtool(struct net *net, struct ifreq *ifr)
 	case ETHTOOL_GMODULEEEPROM:
 		rc = ethtool_get_module_eeprom(dev, useraddr);
 		break;
+	case ETHTOOL_GTUNABLE:
+		rc = ethtool_get_tunable(dev, useraddr);
+		break;
+	case ETHTOOL_STUNABLE:
+		rc = ethtool_set_tunable(dev, useraddr);
+		break;
 	default:
 		rc = -EOPNOTSUPP;
 	}