net: dsa: use switchdev obj for VLAN add/del ops

Simplify DSA by pushing the switchdev objects for VLAN add and delete
operations down to its drivers. Currently only mv88e6xxx is affected.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Vivien Didelot 2015-11-01 12:33:55 -05:00 committed by David S. Miller
parent ea3803c193
commit 76e398a627
6 changed files with 115 additions and 51 deletions

View File

@ -115,7 +115,7 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
.get_regs = mv88e6xxx_get_regs,
.port_stp_update = mv88e6xxx_port_stp_update,
.port_pvid_get = mv88e6xxx_port_pvid_get,
.port_pvid_set = mv88e6xxx_port_pvid_set,
.port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
.port_vlan_add = mv88e6xxx_port_vlan_add,
.port_vlan_del = mv88e6xxx_port_vlan_del,
.vlan_getnext = mv88e6xxx_vlan_getnext,

View File

@ -342,7 +342,7 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
.get_regs = mv88e6xxx_get_regs,
.port_stp_update = mv88e6xxx_port_stp_update,
.port_pvid_get = mv88e6xxx_port_pvid_get,
.port_pvid_set = mv88e6xxx_port_pvid_set,
.port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
.port_vlan_add = mv88e6xxx_port_vlan_add,
.port_vlan_del = mv88e6xxx_port_vlan_del,
.vlan_getnext = mv88e6xxx_vlan_getnext,

View File

@ -1121,6 +1121,19 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
return 0;
}
static int _mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
{
int ret;
ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN);
if (ret < 0)
return ret;
*pvid = ret & PORT_DEFAULT_VLAN_MASK;
return 0;
}
int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
{
int ret;
@ -1134,9 +1147,9 @@ int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
return 0;
}
int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
static int _mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
{
return mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
pvid & PORT_DEFAULT_VLAN_MASK);
}
@ -1441,61 +1454,87 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid,
return 0;
}
int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
bool untagged)
int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans)
{
/* We don't need any dynamic resource from the kernel (yet),
* so skip the prepare phase.
*/
return 0;
}
static int _mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
bool untagged)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_vtu_stu_entry vlan;
int err;
mutex_lock(&ps->smi_mutex);
err = _mv88e6xxx_vtu_vid_write(ds, vid - 1);
if (err)
goto unlock;
return err;
err = _mv88e6xxx_vtu_getnext(ds, &vlan);
if (err)
goto unlock;
return err;
if (vlan.vid != vid || !vlan.valid) {
err = _mv88e6xxx_vlan_init(ds, vid, &vlan);
if (err)
goto unlock;
return err;
}
vlan.data[port] = untagged ?
GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
return _mv88e6xxx_vtu_loadpurge(ds, &vlan);
}
int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
u16 vid;
int err = 0;
mutex_lock(&ps->smi_mutex);
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
err = _mv88e6xxx_port_vlan_add(ds, port, vid, untagged);
if (err)
goto unlock;
}
/* no PVID with ranges, otherwise it's a bug */
if (pvid)
err = _mv88e6xxx_port_pvid_set(ds, port, vid);
unlock:
mutex_unlock(&ps->smi_mutex);
return err;
}
int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
static int _mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_vtu_stu_entry vlan;
int i, err;
mutex_lock(&ps->smi_mutex);
err = _mv88e6xxx_vtu_vid_write(ds, vid - 1);
if (err)
goto unlock;
return err;
err = _mv88e6xxx_vtu_getnext(ds, &vlan);
if (err)
goto unlock;
return err;
if (vlan.vid != vid || !vlan.valid ||
vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
err = -ENOENT;
goto unlock;
}
vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
return -ENOENT;
vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
@ -1512,10 +1551,37 @@ int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
}
err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
if (err)
return err;
return _mv88e6xxx_atu_remove(ds, vlan.fid, port, false);
}
int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 pvid, vid;
int err = 0;
mutex_lock(&ps->smi_mutex);
err = _mv88e6xxx_port_pvid_get(ds, port, &pvid);
if (err)
goto unlock;
err = _mv88e6xxx_atu_remove(ds, vlan.fid, port, false);
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
err = _mv88e6xxx_port_vlan_del(ds, port, vid);
if (err)
goto unlock;
if (vid == pvid) {
err = _mv88e6xxx_port_pvid_set(ds, port, 0);
if (err)
goto unlock;
}
}
unlock:
mutex_unlock(&ps->smi_mutex);

View File

@ -457,11 +457,15 @@ int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
struct phy_device *phydev, struct ethtool_eee *e);
int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state);
int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans);
int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans);
int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan);
int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *vid);
int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 vid);
int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
bool untagged);
int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid);
int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
unsigned long *ports, unsigned long *untagged);
int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,

View File

@ -200,6 +200,7 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds)
struct switchdev_trans;
struct switchdev_obj;
struct switchdev_obj_port_fdb;
struct switchdev_obj_port_vlan;
struct dsa_switch_driver {
struct list_head list;
@ -309,11 +310,15 @@ struct dsa_switch_driver {
/*
* VLAN support
*/
int (*port_vlan_prepare)(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans);
int (*port_vlan_add)(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans);
int (*port_vlan_del)(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan);
int (*port_pvid_get)(struct dsa_switch *ds, int port, u16 *pvid);
int (*port_pvid_set)(struct dsa_switch *ds, int port, u16 pvid);
int (*port_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
bool untagged);
int (*port_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
int (*vlan_getnext)(struct dsa_switch *ds, u16 *vid,
unsigned long *ports, unsigned long *untagged);

View File

@ -247,11 +247,10 @@ static int dsa_slave_port_vlan_add(struct net_device *dev,
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
u16 vid;
int err;
if (switchdev_trans_ph_prepare(trans)) {
if (!ds->drv->port_vlan_add || !ds->drv->port_pvid_set)
if (!ds->drv->port_vlan_prepare || !ds->drv->port_vlan_add)
return -EOPNOTSUPP;
/* If the requested port doesn't belong to the same bridge as
@ -262,16 +261,14 @@ static int dsa_slave_port_vlan_add(struct net_device *dev,
vlan->vid_end);
if (err)
return err;
err = ds->drv->port_vlan_prepare(ds, p->port, vlan, trans);
if (err)
return err;
} else {
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
err = ds->drv->port_vlan_add(ds, p->port, vid,
vlan->flags &
BRIDGE_VLAN_INFO_UNTAGGED);
if (!err && vlan->flags & BRIDGE_VLAN_INFO_PVID)
err = ds->drv->port_pvid_set(ds, p->port, vid);
if (err)
return err;
}
err = ds->drv->port_vlan_add(ds, p->port, vlan, trans);
if (err)
return err;
}
return 0;
@ -282,19 +279,11 @@ static int dsa_slave_port_vlan_del(struct net_device *dev,
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
u16 vid;
int err;
if (!ds->drv->port_vlan_del)
return -EOPNOTSUPP;
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
err = ds->drv->port_vlan_del(ds, p->port, vid);
if (err)
return err;
}
return 0;
return ds->drv->port_vlan_del(ds, p->port, vlan);
}
static int dsa_slave_port_vlan_dump(struct net_device *dev,