net: dsa: use switchdev obj for VLAN add/del ops
authorVivien Didelot <vivien.didelot@savoirfairelinux.com>
Sun, 1 Nov 2015 17:33:55 +0000 (12:33 -0500)
committerDavid S. Miller <davem@davemloft.net>
Sun, 1 Nov 2015 20:56:11 +0000 (15:56 -0500)
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>
drivers/net/dsa/mv88e6171.c
drivers/net/dsa/mv88e6352.c
drivers/net/dsa/mv88e6xxx.c
drivers/net/dsa/mv88e6xxx.h
include/net/dsa.h
net/dsa/slave.c

index 2c8eb6f76ebe02a54f71539d3799f21f879e01f8..1bd876e3f9908809443e04f3224d5038d0b17b80 100644 (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,
index cbf4dd8721a6a27e4fdd0ed7963304472486cda8..4458d6ae7b69d490e5b4e5982d49178237891e97 100644 (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,
index b1b14f519d8b195a28e608e49bb94bb751e226e3..9ee1be20a946263ee734a044c55999577f3bccff 100644 (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);
 
index 6f9ed5d45012597d732df6c591bbe0686a1a49af..e0692b95b2c96854e4c84d7aa4d7ba66cdc6d835 100644 (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,
index 98ccbdef646f9acde302f0b87c8a3ac309be6d6e..82a4c60111733e98310a12d569fb999c8e5ac412 100644 (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);
 
index 481754ee062a58bc0f644492c714e4718f9fcb6e..7bc787b095c897922751c3eaa5c2b32d5bca95fa 100644 (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,