diff mbox

[RFC] switchdev: generate phys_port_name in the core

Message ID 20170728023122.1674-1-jakub.kicinski@netronome.com
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Jakub Kicinski July 28, 2017, 2:31 a.m. UTC
On Thu, 27 Jul 2017 13:30:44 +0300, Or Gerlitz wrote:
> > want to add port splitting support, for example, reporting the name on
> > physical ports will become more of a necessity.  
> 
> > If we adopt Jiri's suggestion of returning structured data it will be
> > very easy to give user space type and indexes separately, but we should
> > probably still return the string for backwards compatibility.  
> 
> I am not still clear how the structured data would look like

I decided to just quickly write the code, that should be easier to 
understand.  We can probably leave out the netlink part of the API
if there is no need for it right now, but that's what I ment by
returning the information in a more structured way.

Tested-by: nobody :)
Suggested-by: Jiri (if I understood correctly)
---
 drivers/net/ethernet/mellanox/mlx5/core/en_rep.c |  8 ++-
 drivers/net/ethernet/mellanox/mlxsw/switchx2.c   | 10 ++--
 drivers/net/ethernet/netronome/nfp/nfp_port.c    | 26 ++++-----
 drivers/net/ethernet/netronome/nfp/nfp_port.h    |  4 +-
 include/linux/netdevice.h                        | 18 ++++++-
 include/uapi/linux/if_link.h                     | 16 ++++++
 net/core/dev.c                                   | 31 +++++++++--
 net/core/rtnetlink.c                             | 69 ++++++++++++++++++++++++
 8 files changed, 153 insertions(+), 29 deletions(-)

Comments

Jakub Kicinski July 28, 2017, 2:37 a.m. UTC | #1
On Thu, 27 Jul 2017 19:31:22 -0700, Jakub Kicinski wrote:
> +static size_t rtnl_port_info_size(void)
> +{
> +	size_t port_info_size = nla_total_size(0) + /* nest IFLA_PORT_INFO */

				nla_total_size(4) + /* TYPE */

> +				nla_total_size(4) + /* EXTERNAL_ID or PF_ID */
> +				nla_total_size(4);  /* SPLIT_ID or VF_ID*/
> +
> +	return port_info_size;
> +}
> +
Jiri Pirko July 28, 2017, 5:35 a.m. UTC | #2
Fri, Jul 28, 2017 at 04:31:22AM CEST, jakub.kicinski@netronome.com wrote:
>On Thu, 27 Jul 2017 13:30:44 +0300, Or Gerlitz wrote:
>> > want to add port splitting support, for example, reporting the name on
>> > physical ports will become more of a necessity.  
>> 
>> > If we adopt Jiri's suggestion of returning structured data it will be
>> > very easy to give user space type and indexes separately, but we should
>> > probably still return the string for backwards compatibility.  
>> 
>> I am not still clear how the structured data would look like
>
>I decided to just quickly write the code, that should be easier to 
>understand.  We can probably leave out the netlink part of the API
>if there is no need for it right now, but that's what I ment by
>returning the information in a more structured way.
>
>Tested-by: nobody :)
>Suggested-by: Jiri (if I understood correctly)

Yes, you did :) Couple of nits inlined.


>---
> drivers/net/ethernet/mellanox/mlx5/core/en_rep.c |  8 ++-
> drivers/net/ethernet/mellanox/mlxsw/switchx2.c   | 10 ++--
> drivers/net/ethernet/netronome/nfp/nfp_port.c    | 26 ++++-----
> drivers/net/ethernet/netronome/nfp/nfp_port.h    |  4 +-
> include/linux/netdevice.h                        | 18 ++++++-
> include/uapi/linux/if_link.h                     | 16 ++++++
> net/core/dev.c                                   | 31 +++++++++--
> net/core/rtnetlink.c                             | 69 ++++++++++++++++++++++++
> 8 files changed, 153 insertions(+), 29 deletions(-)
>
>diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
>index 45e60be9c277..7a71291b8ec3 100644
>--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
>+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
>@@ -637,16 +637,14 @@ static int mlx5e_rep_close(struct net_device *dev)
> }
> 
> static int mlx5e_rep_get_phys_port_name(struct net_device *dev,
>-					char *buf, size_t len)
>+					struct netdev_port_info *info)

Either we rename ndo to something like ndo_get_port_info or you rename
the struct to netdev_port_name_info. These 2 should be in sync


> {
> 	struct mlx5e_priv *priv = netdev_priv(dev);
> 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
> 	struct mlx5_eswitch_rep *rep = rpriv->rep;
>-	int ret;
> 
>-	ret = snprintf(buf, len, "%d", rep->vport - 1);
>-	if (ret >= len)
>-		return -EOPNOTSUPP;
>+	info->type = NETDEV_PORT_PCI_VF;

NETDEV_PORT_TYPE_PCI_VF
or
NETDEV_PORT_NAME_TYPE_PCI_VF
depends on the option you chose above.


>+	info->pci.vf_id = rep->vport - 1;
> 
> 	return 0;
> }
>diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
>index 3b0f72455681..383b8b5f41cf 100644
>--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
>+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
>@@ -413,15 +413,13 @@ mlxsw_sx_port_get_stats64(struct net_device *dev,
> 	stats->tx_dropped	= tx_dropped;
> }
> 
>-static int mlxsw_sx_port_get_phys_port_name(struct net_device *dev, char *name,
>-					    size_t len)
>+static int mlxsw_sx_port_get_phys_port_name(struct net_device *dev,
>+					    struct netdev_port_info *info)
> {
> 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
>-	int err;
> 
>-	err = snprintf(name, len, "p%d", mlxsw_sx_port->mapping.module + 1);
>-	if (err >= len)
>-		return -EINVAL;
>+	info->type = NETDEV_PORT_EXTERNAL;
>+	info->port.id = mlxsw_sx_port->mapping.module + 1;
> 
> 	return 0;
> }
>diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c
>index d16a7b78ba9b..8f5c37b9a79c 100644
>--- a/drivers/net/ethernet/netronome/nfp/nfp_port.c
>+++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c
>@@ -143,11 +143,11 @@ struct nfp_eth_table_port *nfp_port_get_eth_port(struct nfp_port *port)
> }
> 
> int
>-nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len)
>+nfp_port_get_phys_port_name(struct net_device *netdev,
>+			    struct netdev_port_info *info)
> {
> 	struct nfp_eth_table_port *eth_port;
> 	struct nfp_port *port;
>-	int n;
> 
> 	port = nfp_port_from_netdev(netdev);
> 	if (!port)
>@@ -159,25 +159,27 @@ nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len)
> 		if (!eth_port)
> 			return -EOPNOTSUPP;
> 
>-		if (!eth_port->is_split)
>-			n = snprintf(name, len, "p%d", eth_port->label_port);
>-		else
>-			n = snprintf(name, len, "p%ds%d", eth_port->label_port,
>-				     eth_port->label_subport);
>+		info->type = NETDEV_PORT_EXTERNAL;
>+		info->external.id = eth_port->label_port;
>+
>+		if (eth_port->is_split) {
>+			info->type = NETDEV_PORT_EXTERNAL_SPLIT;
>+			info->external.split_id = eth_port->label_subport;
>+		}
> 		break;
> 	case NFP_PORT_PF_PORT:
>-		n = snprintf(name, len, "pf%d", port->pf_id);
>+		info->type = NETDEV_PORT_PCI_PF;
>+		info->pci.pf_id = port->pf_id;
> 		break;
> 	case NFP_PORT_VF_PORT:
>-		n = snprintf(name, len, "pf%dvf%d", port->pf_id, port->vf_id);
>+		info->type = NETDEV_PORT_PCI_VF;
>+		info->pci.pf_id = port->pf_id;
>+		info->pci.vf_id = port->vf_id;
> 		break;
> 	default:
> 		return -EOPNOTSUPP;
> 	}
> 
>-	if (n >= len)
>-		return -EINVAL;
>-
> 	return 0;
> }
> 
>diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h
>index 56c76926c82a..03b8746feb29 100644
>--- a/drivers/net/ethernet/netronome/nfp/nfp_port.h
>+++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h
>@@ -118,8 +118,8 @@ nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id);
> struct nfp_eth_table_port *__nfp_port_get_eth_port(struct nfp_port *port);
> struct nfp_eth_table_port *nfp_port_get_eth_port(struct nfp_port *port);
> 
>-int
>-nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len);
>+int nfp_port_get_phys_port_name(struct net_device *netdev,
>+				struct netdev_port_info *info);
> int nfp_port_configure(struct net_device *netdev, bool configed);
> 
> struct nfp_port *
>diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>index 3a3cdc1b1f31..0a055df701ef 100644
>--- a/include/linux/netdevice.h
>+++ b/include/linux/netdevice.h
>@@ -845,6 +845,22 @@ struct xfrmdev_ops {
> };
> #endif
> 
>+struct netdev_port_info {
>+	int type;

enum please


>+
>+	union {
>+		struct {
>+			u32 id;
>+			u32 split_id;
>+		} external;
>+
>+		struct {
>+			u32 pf_id;
>+			u32 vf_id;
>+		} pci;
>+	};
>+};
>+
> /*
>  * This structure defines the management hooks for network devices.
>  * The following hooks can be defined; unless noted otherwise, they are
>@@ -1306,7 +1322,7 @@ struct net_device_ops {
> 	int			(*ndo_get_phys_port_id)(struct net_device *dev,
> 							struct netdev_phys_item_id *ppid);
> 	int			(*ndo_get_phys_port_name)(struct net_device *dev,
>-							  char *name, size_t len);
>+							  struct netdev_port_info *info);
> 	void			(*ndo_udp_tunnel_add)(struct net_device *dev,
> 						      struct udp_tunnel_info *ti);
> 	void			(*ndo_udp_tunnel_del)(struct net_device *dev,
>diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
>index 8d062c58d5cb..e00ff0333e3f 100644
>--- a/include/uapi/linux/if_link.h
>+++ b/include/uapi/linux/if_link.h
>@@ -158,6 +158,7 @@ enum {
> 	IFLA_PAD,
> 	IFLA_XDP,
> 	IFLA_EVENT,
>+	IFLA_PORT_INFO,
> 	__IFLA_MAX
> };
> 
>@@ -927,4 +928,19 @@ enum {
> 	IFLA_EVENT_BONDING_OPTIONS,	/* change in bonding options */
> };
> 
>+enum {
>+	NETDEV_PORT_EXTERNAL,
>+	NETDEV_PORT_EXTERNAL_SPLIT,
>+	NETDEV_PORT_PCI_PF,

Isn't PF also EXTERNAL? Cant VF be split? What I'm getting at, shoudn't
these be flags?


>+	NETDEV_PORT_PCI_VF,
>+};
>+
Jiri Pirko July 28, 2017, 5:58 a.m. UTC | #3
Fri, Jul 28, 2017 at 07:35:07AM CEST, jiri@resnulli.us wrote:
>Fri, Jul 28, 2017 at 04:31:22AM CEST, jakub.kicinski@netronome.com wrote:
>>On Thu, 27 Jul 2017 13:30:44 +0300, Or Gerlitz wrote:
>>> > want to add port splitting support, for example, reporting the name on
>>> > physical ports will become more of a necessity.  
>>> 
>>> > If we adopt Jiri's suggestion of returning structured data it will be
>>> > very easy to give user space type and indexes separately, but we should
>>> > probably still return the string for backwards compatibility.  
>>> 
>>> I am not still clear how the structured data would look like
>>
>>I decided to just quickly write the code, that should be easier to 
>>understand.  We can probably leave out the netlink part of the API
>>if there is no need for it right now, but that's what I ment by
>>returning the information in a more structured way.
>>
>>Tested-by: nobody :)
>>Suggested-by: Jiri (if I understood correctly)
>
>Yes, you did :) Couple of nits inlined.
>
>
>>---
>> drivers/net/ethernet/mellanox/mlx5/core/en_rep.c |  8 ++-
>> drivers/net/ethernet/mellanox/mlxsw/switchx2.c   | 10 ++--
>> drivers/net/ethernet/netronome/nfp/nfp_port.c    | 26 ++++-----
>> drivers/net/ethernet/netronome/nfp/nfp_port.h    |  4 +-
>> include/linux/netdevice.h                        | 18 ++++++-
>> include/uapi/linux/if_link.h                     | 16 ++++++
>> net/core/dev.c                                   | 31 +++++++++--
>> net/core/rtnetlink.c                             | 69 ++++++++++++++++++++++++
>> 8 files changed, 153 insertions(+), 29 deletions(-)
>>
>>diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
>>index 45e60be9c277..7a71291b8ec3 100644
>>--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
>>+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
>>@@ -637,16 +637,14 @@ static int mlx5e_rep_close(struct net_device *dev)
>> }
>> 
>> static int mlx5e_rep_get_phys_port_name(struct net_device *dev,
>>-					char *buf, size_t len)
>>+					struct netdev_port_info *info)
>
>Either we rename ndo to something like ndo_get_port_info or you rename
>the struct to netdev_port_name_info. These 2 should be in sync

As this is strictly related to port name, I think it should be:
struct netdev_phys_port_name_info

And the rest named in-sync with this
Jakub Kicinski July 28, 2017, 7:27 a.m. UTC | #4
On Fri, 28 Jul 2017 07:35:07 +0200, Jiri Pirko wrote:
> >+enum {
> >+	NETDEV_PORT_EXTERNAL,
> >+	NETDEV_PORT_EXTERNAL_SPLIT,
> >+	NETDEV_PORT_PCI_PF,  
> 
> Isn't PF also EXTERNAL? Cant VF be split? What I'm getting at, shoudn't
> these be flags?

By external PF do you mean not connected to the current host?  Yes we
could make a flag like that.  Right now I have picked "external" to
avoid too specific names like MAC/PHY/Ethernet...  There could be a
MAC-to-MAC connection for instance, so no PHY involved.  Should we go
with NETDEV_PORT_ETH?

I will make SPLIT into a flag.
Jakub Kicinski July 28, 2017, 7:28 a.m. UTC | #5
On Fri, 28 Jul 2017 07:58:15 +0200, Jiri Pirko wrote:
> Fri, Jul 28, 2017 at 07:35:07AM CEST, jiri@resnulli.us wrote:
> >Fri, Jul 28, 2017 at 04:31:22AM CEST, jakub.kicinski@netronome.com wrote:  
> >>On Thu, 27 Jul 2017 13:30:44 +0300, Or Gerlitz wrote:  
> >>> > want to add port splitting support, for example, reporting the name on
> >>> > physical ports will become more of a necessity.    
> >>>   
> >>> > If we adopt Jiri's suggestion of returning structured data it will be
> >>> > very easy to give user space type and indexes separately, but we should
> >>> > probably still return the string for backwards compatibility.    
> >>> 
> >>> I am not still clear how the structured data would look like  
> >>
> >>I decided to just quickly write the code, that should be easier to 
> >>understand.  We can probably leave out the netlink part of the API
> >>if there is no need for it right now, but that's what I ment by
> >>returning the information in a more structured way.
> >>
> >>Tested-by: nobody :)
> >>Suggested-by: Jiri (if I understood correctly)  
> >
> >Yes, you did :) Couple of nits inlined.
> >
> >  
> >>---
> >> drivers/net/ethernet/mellanox/mlx5/core/en_rep.c |  8 ++-
> >> drivers/net/ethernet/mellanox/mlxsw/switchx2.c   | 10 ++--
> >> drivers/net/ethernet/netronome/nfp/nfp_port.c    | 26 ++++-----
> >> drivers/net/ethernet/netronome/nfp/nfp_port.h    |  4 +-
> >> include/linux/netdevice.h                        | 18 ++++++-
> >> include/uapi/linux/if_link.h                     | 16 ++++++
> >> net/core/dev.c                                   | 31 +++++++++--
> >> net/core/rtnetlink.c                             | 69 ++++++++++++++++++++++++
> >> 8 files changed, 153 insertions(+), 29 deletions(-)
> >>
> >>diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
> >>index 45e60be9c277..7a71291b8ec3 100644
> >>--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
> >>+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
> >>@@ -637,16 +637,14 @@ static int mlx5e_rep_close(struct net_device *dev)
> >> }
> >> 
> >> static int mlx5e_rep_get_phys_port_name(struct net_device *dev,
> >>-					char *buf, size_t len)
> >>+					struct netdev_port_info *info)  
> >
> >Either we rename ndo to something like ndo_get_port_info or you rename
> >the struct to netdev_port_name_info. These 2 should be in sync  
> 
> As this is strictly related to port name, I think it should be:
> struct netdev_phys_port_name_info
> 
> And the rest named in-sync with this

Hm..  Since we would return the type and IDs via netlink, I was
actually leaning towards ndo_get_port_info.  "Name" somehow implies a
string in my mind.  Perhaps the "info" part is where I went wrong, could
we go with netdev_port_label?  Or netdev_port_ident?  I would also
prefer to avoid the phys in name, since it doesn't sit well with PFs
vs VFs.

Ah, naming things...
Andrew Lunn July 28, 2017, 2:13 p.m. UTC | #6
On Thu, Jul 27, 2017 at 07:31:22PM -0700, Jakub Kicinski wrote:
> On Thu, 27 Jul 2017 13:30:44 +0300, Or Gerlitz wrote:
> > > want to add port splitting support, for example, reporting the name on
> > > physical ports will become more of a necessity.  
> > 
> > > If we adopt Jiri's suggestion of returning structured data it will be
> > > very easy to give user space type and indexes separately, but we should
> > > probably still return the string for backwards compatibility.  
> > 
> > I am not still clear how the structured data would look like
> 
> I decided to just quickly write the code, that should be easier to 
> understand.  We can probably leave out the netlink part of the API
> if there is no need for it right now, but that's what I ment by
> returning the information in a more structured way.
> 
> Tested-by: nobody :)
> Suggested-by: Jiri (if I understood correctly)
> ---
>  drivers/net/ethernet/mellanox/mlx5/core/en_rep.c |  8 ++-
>  drivers/net/ethernet/mellanox/mlxsw/switchx2.c   | 10 ++--
>  drivers/net/ethernet/netronome/nfp/nfp_port.c    | 26 ++++-----
>  drivers/net/ethernet/netronome/nfp/nfp_port.h    |  4 +-
>  include/linux/netdevice.h                        | 18 ++++++-
>  include/uapi/linux/if_link.h                     | 16 ++++++
>  net/core/dev.c                                   | 31 +++++++++--
>  net/core/rtnetlink.c                             | 69 ++++++++++++++++++++++++

Hi Jakub

Don't forget net/dsa/slave.c when you go from RFC to a real patch for
submission.

	Andrew
diff mbox

Patch

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
index 45e60be9c277..7a71291b8ec3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
@@ -637,16 +637,14 @@  static int mlx5e_rep_close(struct net_device *dev)
 }
 
 static int mlx5e_rep_get_phys_port_name(struct net_device *dev,
-					char *buf, size_t len)
+					struct netdev_port_info *info)
 {
 	struct mlx5e_priv *priv = netdev_priv(dev);
 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
 	struct mlx5_eswitch_rep *rep = rpriv->rep;
-	int ret;
 
-	ret = snprintf(buf, len, "%d", rep->vport - 1);
-	if (ret >= len)
-		return -EOPNOTSUPP;
+	info->type = NETDEV_PORT_PCI_VF;
+	info->pci.vf_id = rep->vport - 1;
 
 	return 0;
 }
diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
index 3b0f72455681..383b8b5f41cf 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
@@ -413,15 +413,13 @@  mlxsw_sx_port_get_stats64(struct net_device *dev,
 	stats->tx_dropped	= tx_dropped;
 }
 
-static int mlxsw_sx_port_get_phys_port_name(struct net_device *dev, char *name,
-					    size_t len)
+static int mlxsw_sx_port_get_phys_port_name(struct net_device *dev,
+					    struct netdev_port_info *info)
 {
 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
-	int err;
 
-	err = snprintf(name, len, "p%d", mlxsw_sx_port->mapping.module + 1);
-	if (err >= len)
-		return -EINVAL;
+	info->type = NETDEV_PORT_EXTERNAL;
+	info->port.id = mlxsw_sx_port->mapping.module + 1;
 
 	return 0;
 }
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c
index d16a7b78ba9b..8f5c37b9a79c 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_port.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c
@@ -143,11 +143,11 @@  struct nfp_eth_table_port *nfp_port_get_eth_port(struct nfp_port *port)
 }
 
 int
-nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len)
+nfp_port_get_phys_port_name(struct net_device *netdev,
+			    struct netdev_port_info *info)
 {
 	struct nfp_eth_table_port *eth_port;
 	struct nfp_port *port;
-	int n;
 
 	port = nfp_port_from_netdev(netdev);
 	if (!port)
@@ -159,25 +159,27 @@  nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len)
 		if (!eth_port)
 			return -EOPNOTSUPP;
 
-		if (!eth_port->is_split)
-			n = snprintf(name, len, "p%d", eth_port->label_port);
-		else
-			n = snprintf(name, len, "p%ds%d", eth_port->label_port,
-				     eth_port->label_subport);
+		info->type = NETDEV_PORT_EXTERNAL;
+		info->external.id = eth_port->label_port;
+
+		if (eth_port->is_split) {
+			info->type = NETDEV_PORT_EXTERNAL_SPLIT;
+			info->external.split_id = eth_port->label_subport;
+		}
 		break;
 	case NFP_PORT_PF_PORT:
-		n = snprintf(name, len, "pf%d", port->pf_id);
+		info->type = NETDEV_PORT_PCI_PF;
+		info->pci.pf_id = port->pf_id;
 		break;
 	case NFP_PORT_VF_PORT:
-		n = snprintf(name, len, "pf%dvf%d", port->pf_id, port->vf_id);
+		info->type = NETDEV_PORT_PCI_VF;
+		info->pci.pf_id = port->pf_id;
+		info->pci.vf_id = port->vf_id;
 		break;
 	default:
 		return -EOPNOTSUPP;
 	}
 
-	if (n >= len)
-		return -EINVAL;
-
 	return 0;
 }
 
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h
index 56c76926c82a..03b8746feb29 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_port.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h
@@ -118,8 +118,8 @@  nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id);
 struct nfp_eth_table_port *__nfp_port_get_eth_port(struct nfp_port *port);
 struct nfp_eth_table_port *nfp_port_get_eth_port(struct nfp_port *port);
 
-int
-nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len);
+int nfp_port_get_phys_port_name(struct net_device *netdev,
+				struct netdev_port_info *info);
 int nfp_port_configure(struct net_device *netdev, bool configed);
 
 struct nfp_port *
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 3a3cdc1b1f31..0a055df701ef 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -845,6 +845,22 @@  struct xfrmdev_ops {
 };
 #endif
 
+struct netdev_port_info {
+	int type;
+
+	union {
+		struct {
+			u32 id;
+			u32 split_id;
+		} external;
+
+		struct {
+			u32 pf_id;
+			u32 vf_id;
+		} pci;
+	};
+};
+
 /*
  * This structure defines the management hooks for network devices.
  * The following hooks can be defined; unless noted otherwise, they are
@@ -1306,7 +1322,7 @@  struct net_device_ops {
 	int			(*ndo_get_phys_port_id)(struct net_device *dev,
 							struct netdev_phys_item_id *ppid);
 	int			(*ndo_get_phys_port_name)(struct net_device *dev,
-							  char *name, size_t len);
+							  struct netdev_port_info *info);
 	void			(*ndo_udp_tunnel_add)(struct net_device *dev,
 						      struct udp_tunnel_info *ti);
 	void			(*ndo_udp_tunnel_del)(struct net_device *dev,
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 8d062c58d5cb..e00ff0333e3f 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -158,6 +158,7 @@  enum {
 	IFLA_PAD,
 	IFLA_XDP,
 	IFLA_EVENT,
+	IFLA_PORT_INFO,
 	__IFLA_MAX
 };
 
@@ -927,4 +928,19 @@  enum {
 	IFLA_EVENT_BONDING_OPTIONS,	/* change in bonding options */
 };
 
+enum {
+	NETDEV_PORT_EXTERNAL,
+	NETDEV_PORT_EXTERNAL_SPLIT,
+	NETDEV_PORT_PCI_PF,
+	NETDEV_PORT_PCI_VF,
+};
+
+enum {
+	IFLA_PORT_INFO_TYPE,
+	IFLA_PORT_INFO_EXTERNAL_ID,
+	IFLA_PORT_INFO_EXTERNAL_SPLIT_ID,
+	IFLA_PORT_INFO_PCI_PF_ID,
+	IFLA_PORT_INFO_PCI_VF_ID,
+};
+
 #endif /* _UAPI_LINUX_IF_LINK_H */
diff --git a/net/core/dev.c b/net/core/dev.c
index 8ea6b4b42611..8fe3f697234e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6947,14 +6947,39 @@  EXPORT_SYMBOL(dev_get_phys_port_id);
  *
  *	Get device physical port name
  */
-int dev_get_phys_port_name(struct net_device *dev,
-			   char *name, size_t len)
+int dev_get_phys_port_name(struct net_device *dev, char *name, size_t len)
 {
 	const struct net_device_ops *ops = dev->netdev_ops;
+	struct netdev_port_info info = {};
+	int ret;
 
 	if (!ops->ndo_get_phys_port_name)
 		return -EOPNOTSUPP;
-	return ops->ndo_get_phys_port_name(dev, name, len);
+	ret = ops->ndo_get_phys_port_name(dev, &info);
+	if (ret)
+		return ret;
+
+	switch (info.type) {
+	case NETDEV_PORT_EXTERNAL:
+		ret = snprintf(name, len, "p%d", info.external.id);
+		break;
+	case NETDEV_PORT_EXTERNAL_SPLIT:
+		ret = snprintf(name, len, "p%ds%d",
+			       info.external.id, info.external.split_id);
+		break;
+	case NETDEV_PORT_PCI_PF:
+		ret = snprintf(name, len, "pf%d", info.pci.pf_id);
+		break;
+	case NETDEV_PORT_PCI_VF:
+		ret = snprintf(name, len, "pf%dvf%d",
+			       info.pci.pf_id, info.pci.vf_id);
+		break;
+	}
+
+	if (ret > len)
+		return -EINVAL;
+
+	return 0;
 }
 EXPORT_SYMBOL(dev_get_phys_port_name);
 
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 9201e3621351..1eb181ef705f 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -907,6 +907,15 @@  static size_t rtnl_xdp_size(void)
 	return xdp_size;
 }
 
+static size_t rtnl_port_info_size(void)
+{
+	size_t port_info_size = nla_total_size(0) + /* nest IFLA_PORT_INFO */
+				nla_total_size(4) + /* EXTERNAL_ID or PF_ID */
+				nla_total_size(4);  /* SPLIT_ID or VF_ID*/
+
+	return port_info_size;
+}
+
 static noinline size_t if_nlmsg_size(const struct net_device *dev,
 				     u32 ext_filter_mask)
 {
@@ -946,6 +955,7 @@  static noinline size_t if_nlmsg_size(const struct net_device *dev,
 	       + nla_total_size(IFNAMSIZ) /* IFLA_PHYS_PORT_NAME */
 	       + rtnl_xdp_size() /* IFLA_XDP */
 	       + nla_total_size(4)  /* IFLA_EVENT */
+	       + rtnl_port_info_size() /* IFLA_PORT_INFO */
 	       + nla_total_size(1); /* IFLA_PROTO_DOWN */
 
 }
@@ -1330,6 +1340,62 @@  static u32 rtnl_get_event(unsigned long event)
 	return rtnl_event_type;
 }
 
+static int rtnl_port_info_fill(struct sk_buff *skb, struct net_device *dev)
+{
+	const struct net_device_ops *ops = dev->netdev_ops;
+	struct netdev_port_info info = {};
+	struct nlattr *port_info;
+	int err;
+
+	if (!ops->ndo_get_phys_port_name)
+		return 0;
+
+	port_info = nla_nest_start(skb, IFLA_PORT_INFO);
+	if (!port_info)
+		return -EMSGSIZE;
+
+	err = ops->ndo_get_phys_port_name(dev, &info);
+	if (err)
+		goto err_cancel;
+
+	err = nla_put_u32(skb, IFLA_PORT_INFO_TYPE, info.type);
+	if (err)
+		goto err_cancel;
+
+	switch (info.type) {
+	case NETDEV_PORT_EXTERNAL_SPLIT:
+		err = nla_put_u32(skb, IFLA_PORT_INFO_EXTERNAL_SPLIT_ID,
+				  info.external.split_id);
+		if (err)
+			goto err_cancel;
+		/* fall through */
+	case NETDEV_PORT_EXTERNAL:
+		err = nla_put_u32(skb, IFLA_PORT_INFO_EXTERNAL_ID,
+				  info.external.id);
+		if (err)
+			goto err_cancel;
+		break;
+	case NETDEV_PORT_PCI_VF:
+		err = nla_put_u32(skb, IFLA_PORT_INFO_PCI_VF_ID,
+				  info.pci.vf_id);
+		if (err)
+			goto err_cancel;
+		/* fall through */
+	case NETDEV_PORT_PCI_PF:
+		err = nla_put_u32(skb, IFLA_PORT_INFO_PCI_PF_ID,
+				  info.pci.pf_id);
+		if (err)
+			goto err_cancel;
+		break;
+	}
+
+	return 0;
+
+err_cancel:
+	nla_nest_cancel(skb, port_info);
+	return err;
+}
+
 static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
 			    int type, u32 pid, u32 seq, u32 change,
 			    unsigned int flags, u32 ext_filter_mask,
@@ -1435,6 +1501,9 @@  static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
 	if (rtnl_xdp_fill(skb, dev))
 		goto nla_put_failure;
 
+	if (rtnl_port_info_fill(skb, dev))
+		goto nla_put_failure;
+
 	if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) {
 		if (rtnl_link_fill(skb, dev) < 0)
 			goto nla_put_failure;