Merge branch 'net-dsa-Setup-dsa_netdev_ops'

Florian Fainelli says:

====================
net: dsa: Setup dsa_netdev_ops

This patch series addresses the overloading of a DSA CPU/management
interface's netdev_ops for the purpose of providing useful information
from the switch side.

Up until now we had duplicated the existing netdev_ops structure and
added specific function pointers to return information of interest. Here
we have a more controlled way of doing this by involving the specific
netdev_ops function pointers that we want to be patched, which is easier
for auditing code in the future. As a byproduct we can now maintain
netdev_ops pointer comparisons which would be failing before (no known
in tree problems because of that though).

Let me know if this approach looks reasonable to you and we might do the
same with our ethtool_ops overloading as well.

Changes in v2:

- use static inline int vs. static int inline (Kbuild robot)
- fixed typos in patch 4 (Andrew)
- avoid using macros (Andrew)
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2020-07-20 16:48:22 -07:00
commit 202a5d5a7a
4 changed files with 110 additions and 47 deletions

View File

@ -86,6 +86,18 @@ struct dsa_device_ops {
enum dsa_tag_protocol proto;
};
/* This structure defines the control interfaces that are overlayed by the
* DSA layer on top of the DSA CPU/management net_device instance. This is
* used by the core net_device layer while calling various net_device_ops
* function pointers.
*/
struct dsa_netdevice_ops {
int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr,
int cmd);
int (*ndo_get_phys_port_name)(struct net_device *dev, char *name,
size_t len);
};
#define DSA_TAG_DRIVER_ALIAS "dsa_tag-"
#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \
MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE))
@ -217,7 +229,7 @@ struct dsa_port {
/*
* Original copy of the master netdev net_device_ops
*/
const struct net_device_ops *orig_ndo_ops;
const struct dsa_netdevice_ops *netdev_ops;
bool setup;
};
@ -679,6 +691,63 @@ static inline bool dsa_can_decode(const struct sk_buff *skb,
return false;
}
#if IS_ENABLED(CONFIG_NET_DSA)
static inline int __dsa_netdevice_ops_check(struct net_device *dev)
{
int err = -EOPNOTSUPP;
if (!dev->dsa_ptr)
return err;
if (!dev->dsa_ptr->netdev_ops)
return err;
return 0;
}
static inline int dsa_ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd)
{
const struct dsa_netdevice_ops *ops;
int err;
err = __dsa_netdevice_ops_check(dev);
if (err)
return err;
ops = dev->dsa_ptr->netdev_ops;
return ops->ndo_do_ioctl(dev, ifr, cmd);
}
static inline int dsa_ndo_get_phys_port_name(struct net_device *dev,
char *name, size_t len)
{
const struct dsa_netdevice_ops *ops;
int err;
err = __dsa_netdevice_ops_check(dev);
if (err)
return err;
ops = dev->dsa_ptr->netdev_ops;
return ops->ndo_get_phys_port_name(dev, name, len);
}
#else
static inline int dsa_ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd)
{
return -EOPNOTSUPP;
}
static inline int dsa_ndo_get_phys_port_name(struct net_device *dev,
char *name, size_t len)
{
return -EOPNOTSUPP;
}
#endif
void dsa_unregister_switch(struct dsa_switch *ds);
int dsa_register_switch(struct dsa_switch *ds);
struct dsa_switch *dsa_switch_find(int tree_index, int sw_index);

View File

@ -98,6 +98,7 @@
#include <net/busy_poll.h>
#include <linux/rtnetlink.h>
#include <linux/stat.h>
#include <net/dsa.h>
#include <net/dst.h>
#include <net/dst_metadata.h>
#include <net/pkt_sched.h>
@ -8602,6 +8603,10 @@ int dev_get_phys_port_name(struct net_device *dev,
const struct net_device_ops *ops = dev->netdev_ops;
int err;
err = dsa_ndo_get_phys_port_name(dev, name, len);
if (err == 0 || err != -EOPNOTSUPP)
return err;
if (ops->ndo_get_phys_port_name) {
err = ops->ndo_get_phys_port_name(dev, name, len);
if (err != -EOPNOTSUPP)

View File

@ -5,6 +5,7 @@
#include <linux/rtnetlink.h>
#include <linux/net_tstamp.h>
#include <linux/wireless.h>
#include <net/dsa.h>
#include <net/wext.h>
/*
@ -225,6 +226,26 @@ static int net_hwtstamp_validate(struct ifreq *ifr)
return 0;
}
static int dev_do_ioctl(struct net_device *dev,
struct ifreq *ifr, unsigned int cmd)
{
const struct net_device_ops *ops = dev->netdev_ops;
int err = -EOPNOTSUPP;
err = dsa_ndo_do_ioctl(dev, ifr, cmd);
if (err == 0 || err != -EOPNOTSUPP)
return err;
if (ops->ndo_do_ioctl) {
if (netif_device_present(dev))
err = ops->ndo_do_ioctl(dev, ifr, cmd);
else
err = -ENODEV;
}
return err;
}
/*
* Perform the SIOCxIFxxx calls, inside rtnl_lock()
*/
@ -323,13 +344,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
cmd == SIOCSHWTSTAMP ||
cmd == SIOCGHWTSTAMP ||
cmd == SIOCWANDEV) {
err = -EOPNOTSUPP;
if (ops->ndo_do_ioctl) {
if (netif_device_present(dev))
err = ops->ndo_do_ioctl(dev, ifr, cmd);
else
err = -ENODEV;
}
err = dev_do_ioctl(dev, ifr, cmd);
} else
err = -EINVAL;

View File

@ -220,12 +220,17 @@ static int dsa_master_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
}
if (cpu_dp->orig_ndo_ops && cpu_dp->orig_ndo_ops->ndo_do_ioctl)
err = cpu_dp->orig_ndo_ops->ndo_do_ioctl(dev, ifr, cmd);
if (dev->netdev_ops->ndo_do_ioctl)
err = dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
return err;
}
static const struct dsa_netdevice_ops dsa_netdev_ops = {
.ndo_do_ioctl = dsa_master_ioctl,
.ndo_get_phys_port_name = dsa_master_get_phys_port_name,
};
static int dsa_master_ethtool_setup(struct net_device *dev)
{
struct dsa_port *cpu_dp = dev->dsa_ptr;
@ -260,38 +265,10 @@ static void dsa_master_ethtool_teardown(struct net_device *dev)
cpu_dp->orig_ethtool_ops = NULL;
}
static int dsa_master_ndo_setup(struct net_device *dev)
static void dsa_netdev_ops_set(struct net_device *dev,
const struct dsa_netdevice_ops *ops)
{
struct dsa_port *cpu_dp = dev->dsa_ptr;
struct dsa_switch *ds = cpu_dp->ds;
struct net_device_ops *ops;
if (dev->netdev_ops->ndo_get_phys_port_name)
return 0;
ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL);
if (!ops)
return -ENOMEM;
cpu_dp->orig_ndo_ops = dev->netdev_ops;
if (cpu_dp->orig_ndo_ops)
memcpy(ops, cpu_dp->orig_ndo_ops, sizeof(*ops));
ops->ndo_get_phys_port_name = dsa_master_get_phys_port_name;
ops->ndo_do_ioctl = dsa_master_ioctl;
dev->netdev_ops = ops;
return 0;
}
static void dsa_master_ndo_teardown(struct net_device *dev)
{
struct dsa_port *cpu_dp = dev->dsa_ptr;
if (cpu_dp->orig_ndo_ops)
dev->netdev_ops = cpu_dp->orig_ndo_ops;
cpu_dp->orig_ndo_ops = NULL;
dev->dsa_ptr->netdev_ops = ops;
}
static ssize_t tagging_show(struct device *d, struct device_attribute *attr,
@ -353,9 +330,7 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
if (ret)
return ret;
ret = dsa_master_ndo_setup(dev);
if (ret)
goto out_err_ethtool_teardown;
dsa_netdev_ops_set(dev, &dsa_netdev_ops);
ret = sysfs_create_group(&dev->dev.kobj, &dsa_group);
if (ret)
@ -364,8 +339,7 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
return ret;
out_err_ndo_teardown:
dsa_master_ndo_teardown(dev);
out_err_ethtool_teardown:
dsa_netdev_ops_set(dev, NULL);
dsa_master_ethtool_teardown(dev);
return ret;
}
@ -373,7 +347,7 @@ out_err_ethtool_teardown:
void dsa_master_teardown(struct net_device *dev)
{
sysfs_remove_group(&dev->dev.kobj, &dsa_group);
dsa_master_ndo_teardown(dev);
dsa_netdev_ops_set(dev, NULL);
dsa_master_ethtool_teardown(dev);
dsa_master_reset_mtu(dev);