Skip to content

Commit

Permalink
net: dsa: use switchdev obj for VLAN add/del ops
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Vivien Didelot authored and David S. Miller committed Nov 1, 2015
1 parent ea3803c commit 76e398a
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 51 deletions.
2 changes: 1 addition & 1 deletion drivers/net/dsa/mv88e6171.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/dsa/mv88e6352.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
108 changes: 87 additions & 21 deletions drivers/net/dsa/mv88e6xxx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;

Expand All @@ -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);

Expand Down
12 changes: 8 additions & 4 deletions drivers/net/dsa/mv88e6xxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
13 changes: 9 additions & 4 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
29 changes: 9 additions & 20 deletions net/dsa/slave.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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,
Expand Down

0 comments on commit 76e398a

Please sign in to comment.