mbox series

[RFC,V5,net-next,0/5] ethtool: Extend module EEPROM dump API

Message ID 1616684215-4701-1-git-send-email-moshe@nvidia.com
Headers show
Series ethtool: Extend module EEPROM dump API | expand

Message

Moshe Shemesh March 25, 2021, 2:56 p.m. UTC
Ethtool supports module EEPROM dumps via the `ethtool -m <dev>` command.
But in current state its functionality is limited - offset and length
parameters, which are used to specify a linear desired region of EEPROM
data to dump, is not enough, considering emergence of complex module
EEPROM layouts such as CMIS 4.0.
Moreover, CMIS 4.0 extends the amount of pages that may be accessible by
introducing another parameter for page addressing - banks.

Besides, currently module EEPROM is represented as a chunk of
concatenated pages, where lower 128 bytes of all pages, except page 00h,
are omitted. Offset and length are used to address parts of this fake
linear memory. But in practice drivers, which implement
get_module_info() and get_module_eeprom() ethtool ops still calculate
page number and set I2C address on their own.

This series tackles these issues by adding ethtool op, which allows to
pass page number, bank number and I2C address in addition to offset and
length parameters to the driver, adds corresponding netlink
infrastructure and implements the new interface in mlx5 driver.

This allows to extend userspace 'ethtool -m' CLI by adding new
parameters - page, bank and i2c. New command line format:
 ethtool -m <dev> [hex on|off] [raw on|off] [offset N] [length N] [page N] [bank N] [i2c N]

The consequence of this series is a possibility to dump arbitrary EEPROM
page at a time, in contrast to dumps of concatenated pages. Therefore,
offset and length change their semantics and may be used only to specify
a part of data within half page boundary, which size is currently limited
to 128 bytes.

As for drivers that support legacy get_module_info() and
get_module_eeprom() pair, the series addresses it by implementing a
fallback mechanism. As mentioned earlier, such drivers derive a page
number from 'global' offset, so this can be done vice versa without
their involvement thanks to standardization. If kernel netlink handler
of 'ethtool -m' command detects that new ethtool op is not supported by
the driver, it calculates offset from given page number and page offset
and calls old ndos, if they are available.

Change log:
v4 ->v5:
- Limited KAPI to only read 1/2 page at once.
- Redefined ETH_MODULE_EEPROM_PAGE_LEN as 128.
- Made page number mandatory for any request.
- Added extack messages for invalid parameters failures.

v3 -> v4:
- Renamed many identifiers to use 'eeprom' instead of 'eeprom_data'.
- Renamed netlink enums and defines to use 'MODULE_EEPROM' instead of
   'EEPROM_DATA'.
- Renamed struct ethtool_eeprom_data to ethtool_module_eeprom.
- Added MODULE_EEPROM_MAX_OFFSET (257 * 256) macro and check global offset
    against it to avoid overflow.
- Removed ndo pointer check from _parse_request().
- Removed separate length element from netlink response.
- Limited reads to 128 bytes without crossing half page bound.

v2 -> v3:
- Removed page number limitations
- Added length limit when page is present in fallback
- Changed page, bank and i2c_address type to u8 all over the patchset
- Added 0x51 I2C address usage increasing offset by 256 for SFP

v1 -> v2:
- Limited i2c_address values by 127
- Added page bound check for offset and length
- Added defines for these two points
- Added extack to ndo parameters
- Moved ethnl_ops_begin(dev) and set error path accordingly


Vladyslav Tarasiuk (5):
  ethtool: Allow network drivers to dump arbitrary EEPROM data
  net/mlx5: Refactor module EEPROM query
  net/mlx5: Implement get_module_eeprom_by_page()
  net/mlx5: Add support for DSFP module EEPROM dumps
  ethtool: Add fallback to get_module_eeprom from netlink command

 Documentation/networking/ethtool-netlink.rst  |  36 ++-
 .../ethernet/mellanox/mlx5/core/en_ethtool.c  |  44 ++++
 .../net/ethernet/mellanox/mlx5/core/port.c    | 101 +++++---
 include/linux/ethtool.h                       |  33 ++-
 include/linux/mlx5/port.h                     |  12 +
 include/uapi/linux/ethtool_netlink.h          |  19 ++
 net/ethtool/Makefile                          |   2 +-
 net/ethtool/eeprom.c                          | 234 ++++++++++++++++++
 net/ethtool/netlink.c                         |  10 +
 net/ethtool/netlink.h                         |   2 +
 10 files changed, 461 insertions(+), 32 deletions(-)
 create mode 100644 net/ethtool/eeprom.c

Comments

Don Bollinger March 25, 2021, 6:13 p.m. UTC | #1
> From: Vladyslav Tarasiuk <vladyslavt@nvidia.com>
> 
> In case netlink get_module_eeprom_by_page() callback is not implemented
> by the driver, try to call old get_module_info() and get_module_eeprom()
> pair. Recalculate parameters to get_module_eeprom() offset and len using
> page number and their sizes. Return error if this can't be done.
> 
> Signed-off-by: Vladyslav Tarasiuk <vladyslavt@nvidia.com>
> ---
>  net/ethtool/eeprom.c | 66
> +++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 65 insertions(+), 1 deletion(-)
> 
> diff --git a/net/ethtool/eeprom.c b/net/ethtool/eeprom.c index
> 10d5f6b34f2f..9f773b778bbe 100644
> --- a/net/ethtool/eeprom.c
> +++ b/net/ethtool/eeprom.c
> @@ -25,6 +25,70 @@ struct eeprom_reply_data {  #define
> MODULE_EEPROM_REPDATA(__reply_base) \
>  	container_of(__reply_base, struct eeprom_reply_data, base)
> 
> +static int fallback_set_params(struct eeprom_req_info *request,
> +			       struct ethtool_modinfo *modinfo,
> +			       struct ethtool_eeprom *eeprom) {
> +	u32 offset = request->offset;
> +	u32 length = request->length;
> +
> +	if (request->page)
> +		offset = request->page *
> ETH_MODULE_EEPROM_PAGE_LEN + offset;

The test 'if (request->page)' is not necessary, the math works with page 0
as well.  Keep it if you like the style.

> +
> +	if (modinfo->type == ETH_MODULE_SFF_8079 &&
> +	    request->i2c_address == 0x51)
> +		offset += ETH_MODULE_EEPROM_PAGE_LEN;

offset += ETH_MODULE_EEPROM_PAGE_LEN * 2;

Now that PAGE_LEN is 128, you need two of them to account for both low
memory and high memory at 0x50.

> +
> +	if (offset >= modinfo->eeprom_len)
> +		return -EINVAL;
> +
> +	eeprom->cmd = ETHTOOL_GMODULEEEPROM;
> +	eeprom->len = length;
> +	eeprom->offset = offset;
> +
> +	return 0;
> +}
> +
> +static int eeprom_fallback(struct eeprom_req_info *request,
> +			   struct eeprom_reply_data *reply,
> +			   struct genl_info *info)
> +{
> +	struct net_device *dev = reply->base.dev;
> +	struct ethtool_modinfo modinfo = {0};
> +	struct ethtool_eeprom eeprom = {0};
> +	u8 *data;
> +	int err;
> +
> +	if (!dev->ethtool_ops->get_module_info ||
> +	    !dev->ethtool_ops->get_module_eeprom || request->bank) {
> +		return -EOPNOTSUPP;
> +	}
> +	modinfo.cmd = ETHTOOL_GMODULEINFO;
> +	err = dev->ethtool_ops->get_module_info(dev, &modinfo);
> +	if (err < 0)
> +		return err;
> +
> +	err = fallback_set_params(request, &modinfo, &eeprom);
> +	if (err < 0)
> +		return err;
> +
> +	data = kmalloc(eeprom.len, GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +	err = dev->ethtool_ops->get_module_eeprom(dev, &eeprom,
> data);
> +	if (err < 0)
> +		goto err_out;
> +
> +	reply->data = data;
> +	reply->length = eeprom.len;
> +
> +	return 0;
> +
> +err_out:
> +	kfree(data);
> +	return err;
> +}
> +
>  static int eeprom_prepare_data(const struct ethnl_req_info *req_base,
>  			       struct ethnl_reply_data *reply_base,
>  			       struct genl_info *info)
> @@ -36,7 +100,7 @@ static int eeprom_prepare_data(const struct
> ethnl_req_info *req_base,
>  	int ret;
> 
>  	if (!dev->ethtool_ops->get_module_eeprom_by_page)
> -		return -EOPNOTSUPP;
> +		return eeprom_fallback(request, reply, info);
> 
>  	page_data.offset = request->offset;
>  	page_data.length = request->length;
> --
> 2.18.2
Andrew Lunn March 25, 2021, 11:41 p.m. UTC | #2
On Thu, Mar 25, 2021 at 04:56:50PM +0200, Moshe Shemesh wrote:
> Ethtool supports module EEPROM dumps via the `ethtool -m <dev>` command.
> But in current state its functionality is limited - offset and length
> parameters, which are used to specify a linear desired region of EEPROM
> data to dump, is not enough, considering emergence of complex module
> EEPROM layouts such as CMIS 4.0.
> Moreover, CMIS 4.0 extends the amount of pages that may be accessible by
> introducing another parameter for page addressing - banks.

This is looking much better.

Do you have a version of ethtool using this new API? WIP code is
O.K. I will add basic support to sfp.c and test it out on the devices
i have.

  Andrew
Moshe Shemesh March 26, 2021, 12:40 p.m. UTC | #3
On 3/25/2021 8:13 PM, Don Bollinger wrote:
>> From: Vladyslav Tarasiuk <vladyslavt@nvidia.com>

>>

>> In case netlink get_module_eeprom_by_page() callback is not implemented

>> by the driver, try to call old get_module_info() and get_module_eeprom()

>> pair. Recalculate parameters to get_module_eeprom() offset and len using

>> page number and their sizes. Return error if this can't be done.

>>

>> Signed-off-by: Vladyslav Tarasiuk <vladyslavt@nvidia.com>

>> ---

>>   net/ethtool/eeprom.c | 66

>> +++++++++++++++++++++++++++++++++++++++++++-

>>   1 file changed, 65 insertions(+), 1 deletion(-)

>>

>> diff --git a/net/ethtool/eeprom.c b/net/ethtool/eeprom.c index

>> 10d5f6b34f2f..9f773b778bbe 100644

>> --- a/net/ethtool/eeprom.c

>> +++ b/net/ethtool/eeprom.c

>> @@ -25,6 +25,70 @@ struct eeprom_reply_data {  #define

>> MODULE_EEPROM_REPDATA(__reply_base) \

>>        container_of(__reply_base, struct eeprom_reply_data, base)

>>

>> +static int fallback_set_params(struct eeprom_req_info *request,

>> +                            struct ethtool_modinfo *modinfo,

>> +                            struct ethtool_eeprom *eeprom) {

>> +     u32 offset = request->offset;

>> +     u32 length = request->length;

>> +

>> +     if (request->page)

>> +             offset = request->page *

>> ETH_MODULE_EEPROM_PAGE_LEN + offset;

> The test 'if (request->page)' is not necessary, the math works with page 0

> as well.  Keep it if you like the style.

OK.
>> +

>> +     if (modinfo->type == ETH_MODULE_SFF_8079 &&

>> +         request->i2c_address == 0x51)

>> +             offset += ETH_MODULE_EEPROM_PAGE_LEN;

> offset += ETH_MODULE_EEPROM_PAGE_LEN * 2;

>

> Now that PAGE_LEN is 128, you need two of them to account for both low

> memory and high memory at 0x50.



Right, thanks.

>> +

>> +     if (offset >= modinfo->eeprom_len)

>> +             return -EINVAL;

>> +

>> +     eeprom->cmd = ETHTOOL_GMODULEEEPROM;

>> +     eeprom->len = length;

>> +     eeprom->offset = offset;

>> +

>> +     return 0;

>> +}

>> +

>> +static int eeprom_fallback(struct eeprom_req_info *request,

>> +                        struct eeprom_reply_data *reply,

>> +                        struct genl_info *info)

>> +{

>> +     struct net_device *dev = reply->base.dev;

>> +     struct ethtool_modinfo modinfo = {0};

>> +     struct ethtool_eeprom eeprom = {0};

>> +     u8 *data;

>> +     int err;

>> +

>> +     if (!dev->ethtool_ops->get_module_info ||

>> +         !dev->ethtool_ops->get_module_eeprom || request->bank) {

>> +             return -EOPNOTSUPP;

>> +     }

>> +     modinfo.cmd = ETHTOOL_GMODULEINFO;

>> +     err = dev->ethtool_ops->get_module_info(dev, &modinfo);

>> +     if (err < 0)

>> +             return err;

>> +

>> +     err = fallback_set_params(request, &modinfo, &eeprom);

>> +     if (err < 0)

>> +             return err;

>> +

>> +     data = kmalloc(eeprom.len, GFP_KERNEL);

>> +     if (!data)

>> +             return -ENOMEM;

>> +     err = dev->ethtool_ops->get_module_eeprom(dev, &eeprom,

>> data);

>> +     if (err < 0)

>> +             goto err_out;

>> +

>> +     reply->data = data;

>> +     reply->length = eeprom.len;

>> +

>> +     return 0;

>> +

>> +err_out:

>> +     kfree(data);

>> +     return err;

>> +}

>> +

>>   static int eeprom_prepare_data(const struct ethnl_req_info *req_base,

>>                               struct ethnl_reply_data *reply_base,

>>                               struct genl_info *info)

>> @@ -36,7 +100,7 @@ static int eeprom_prepare_data(const struct

>> ethnl_req_info *req_base,

>>        int ret;

>>

>>        if (!dev->ethtool_ops->get_module_eeprom_by_page)

>> -             return -EOPNOTSUPP;

>> +             return eeprom_fallback(request, reply, info);

>>

>>        page_data.offset = request->offset;

>>        page_data.length = request->length;

>> --

>> 2.18.2
Moshe Shemesh March 26, 2021, 12:41 p.m. UTC | #4
On 3/26/2021 1:41 AM, Andrew Lunn wrote:
> External email: Use caution opening links or attachments

>

>

> On Thu, Mar 25, 2021 at 04:56:50PM +0200, Moshe Shemesh wrote:

>> Ethtool supports module EEPROM dumps via the `ethtool -m <dev>` command.

>> But in current state its functionality is limited - offset and length

>> parameters, which are used to specify a linear desired region of EEPROM

>> data to dump, is not enough, considering emergence of complex module

>> EEPROM layouts such as CMIS 4.0.

>> Moreover, CMIS 4.0 extends the amount of pages that may be accessible by

>> introducing another parameter for page addressing - banks.

> This is looking much better.

>

> Do you have a version of ethtool using this new API? WIP code is

> O.K. I will add basic support to sfp.c and test it out on the devices

> i have.

Sure.
>    Andrew