net/mlx4_core: Resource tracker for reg/unreg vlans
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx4 / resource_tracker.c
index dd68763..e18bfff 100644 (file)
@@ -55,6 +55,14 @@ struct mac_res {
        u8 port;
 };
 
+struct vlan_res {
+       struct list_head list;
+       u16 vlan;
+       int ref_count;
+       int vlan_index;
+       u8 port;
+};
+
 struct res_common {
        struct list_head        list;
        struct rb_node          node;
@@ -266,6 +274,7 @@ static const char *ResourceType(enum mlx4_resource rt)
        case RES_MPT: return "RES_MPT";
        case RES_MTT: return "RES_MTT";
        case RES_MAC: return  "RES_MAC";
+       case RES_VLAN: return  "RES_VLAN";
        case RES_EQ: return "RES_EQ";
        case RES_COUNTER: return "RES_COUNTER";
        case RES_FS_RULE: return "RES_FS_RULE";
@@ -274,6 +283,7 @@ static const char *ResourceType(enum mlx4_resource rt)
        };
 }
 
+static void rem_slave_vlans(struct mlx4_dev *dev, int slave);
 int mlx4_init_resource_tracker(struct mlx4_dev *dev)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
@@ -309,11 +319,18 @@ void mlx4_free_resource_tracker(struct mlx4_dev *dev,
        int i;
 
        if (priv->mfunc.master.res_tracker.slave_list) {
-               if (type != RES_TR_FREE_STRUCTS_ONLY)
-                       for (i = 0 ; i < dev->num_slaves; i++)
+               if (type != RES_TR_FREE_STRUCTS_ONLY) {
+                       for (i = 0; i < dev->num_slaves; i++) {
                                if (type == RES_TR_FREE_ALL ||
                                    dev->caps.function != i)
                                        mlx4_delete_all_resources_for_slave(dev, i);
+                       }
+                       /* free master's vlans */
+                       i = dev->caps.function;
+                       mutex_lock(&priv->mfunc.master.res_tracker.slave_list[i].mutex);
+                       rem_slave_vlans(dev, i);
+                       mutex_unlock(&priv->mfunc.master.res_tracker.slave_list[i].mutex);
+               }
 
                if (type != RES_TR_FREE_SLAVES_ONLY) {
                        kfree(priv->mfunc.master.res_tracker.slave_list);
@@ -1443,7 +1460,7 @@ static void rem_slave_macs(struct mlx4_dev *dev, int slave)
 }
 
 static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
-                        u64 in_param, u64 *out_param)
+                        u64 in_param, u64 *out_param, int in_port)
 {
        int err = -EINVAL;
        int port;
@@ -1452,7 +1469,7 @@ static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        if (op != RES_OP_RESERVE_AND_MAP)
                return err;
 
-       port = get_param_l(out_param);
+       port = !in_port ? get_param_l(out_param) : in_port;
        mac = in_param;
 
        err = __mlx4_register_mac(dev, port, mac);
@@ -1469,12 +1486,96 @@ static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        return err;
 }
 
-static int vlan_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
-                        u64 in_param, u64 *out_param)
+static int vlan_add_to_slave(struct mlx4_dev *dev, int slave, u16 vlan,
+                            int port, int vlan_index)
 {
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+       struct list_head *vlan_list =
+               &tracker->slave_list[slave].res_list[RES_VLAN];
+       struct vlan_res *res, *tmp;
+
+       list_for_each_entry_safe(res, tmp, vlan_list, list) {
+               if (res->vlan == vlan && res->port == (u8) port) {
+                       /* vlan found. update ref count */
+                       ++res->ref_count;
+                       return 0;
+               }
+       }
+
+       res = kzalloc(sizeof(*res), GFP_KERNEL);
+       if (!res)
+               return -ENOMEM;
+       res->vlan = vlan;
+       res->port = (u8) port;
+       res->vlan_index = vlan_index;
+       res->ref_count = 1;
+       list_add_tail(&res->list,
+                     &tracker->slave_list[slave].res_list[RES_VLAN]);
        return 0;
 }
 
+
+static void vlan_del_from_slave(struct mlx4_dev *dev, int slave, u16 vlan,
+                               int port)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+       struct list_head *vlan_list =
+               &tracker->slave_list[slave].res_list[RES_VLAN];
+       struct vlan_res *res, *tmp;
+
+       list_for_each_entry_safe(res, tmp, vlan_list, list) {
+               if (res->vlan == vlan && res->port == (u8) port) {
+                       if (!--res->ref_count) {
+                               list_del(&res->list);
+                               kfree(res);
+                       }
+                       break;
+               }
+       }
+}
+
+static void rem_slave_vlans(struct mlx4_dev *dev, int slave)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+       struct list_head *vlan_list =
+               &tracker->slave_list[slave].res_list[RES_VLAN];
+       struct vlan_res *res, *tmp;
+       int i;
+
+       list_for_each_entry_safe(res, tmp, vlan_list, list) {
+               list_del(&res->list);
+               /* dereference the vlan the num times the slave referenced it */
+               for (i = 0; i < res->ref_count; i++)
+                       __mlx4_unregister_vlan(dev, res->port, res->vlan);
+               kfree(res);
+       }
+}
+
+static int vlan_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
+                         u64 in_param, u64 *out_param, int port)
+{
+       int err;
+       u16 vlan;
+       int vlan_index;
+
+       if (!port || op != RES_OP_RESERVE_AND_MAP)
+               return -EINVAL;
+
+       vlan = (u16) in_param;
+
+       err = __mlx4_register_vlan(dev, port, vlan, &vlan_index);
+       if (!err) {
+               set_param_l(out_param, (u32) vlan_index);
+               err = vlan_add_to_slave(dev, slave, vlan, port, vlan_index);
+               if (err)
+                       __mlx4_unregister_vlan(dev, port, vlan);
+       }
+       return err;
+}
+
 static int counter_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
                             u64 in_param, u64 *out_param)
 {
@@ -1528,7 +1629,7 @@ int mlx4_ALLOC_RES_wrapper(struct mlx4_dev *dev, int slave,
        int err;
        int alop = vhcr->op_modifier;
 
-       switch (vhcr->in_modifier) {
+       switch (vhcr->in_modifier & 0xFF) {
        case RES_QP:
                err = qp_alloc_res(dev, slave, vhcr->op_modifier, alop,
                                   vhcr->in_param, &vhcr->out_param);
@@ -1556,12 +1657,14 @@ int mlx4_ALLOC_RES_wrapper(struct mlx4_dev *dev, int slave,
 
        case RES_MAC:
                err = mac_alloc_res(dev, slave, vhcr->op_modifier, alop,
-                                   vhcr->in_param, &vhcr->out_param);
+                                   vhcr->in_param, &vhcr->out_param,
+                                   (vhcr->in_modifier >> 8) & 0xFF);
                break;
 
        case RES_VLAN:
                err = vlan_alloc_res(dev, slave, vhcr->op_modifier, alop,
-                                   vhcr->in_param, &vhcr->out_param);
+                                    vhcr->in_param, &vhcr->out_param,
+                                    (vhcr->in_modifier >> 8) & 0xFF);
                break;
 
        case RES_COUNTER:
@@ -1730,14 +1833,14 @@ static int srq_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
 }
 
 static int mac_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
-                           u64 in_param, u64 *out_param)
+                           u64 in_param, u64 *out_param, int in_port)
 {
        int port;
        int err = 0;
 
        switch (op) {
        case RES_OP_RESERVE_AND_MAP:
-               port = get_param_l(out_param);
+               port = !in_port ? get_param_l(out_param) : in_port;
                mac_del_from_slave(dev, slave, in_param, port);
                __mlx4_unregister_mac(dev, port, in_param);
                break;
@@ -1751,9 +1854,23 @@ static int mac_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
 }
 
 static int vlan_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
-                           u64 in_param, u64 *out_param)
+                           u64 in_param, u64 *out_param, int port)
 {
-       return 0;
+       int err = 0;
+
+       switch (op) {
+       case RES_OP_RESERVE_AND_MAP:
+               if (!port)
+                       return -EINVAL;
+               vlan_del_from_slave(dev, slave, in_param, port);
+               __mlx4_unregister_vlan(dev, port, in_param);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+
+       return err;
 }
 
 static int counter_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
@@ -1803,7 +1920,7 @@ int mlx4_FREE_RES_wrapper(struct mlx4_dev *dev, int slave,
        int err = -EINVAL;
        int alop = vhcr->op_modifier;
 
-       switch (vhcr->in_modifier) {
+       switch (vhcr->in_modifier & 0xFF) {
        case RES_QP:
                err = qp_free_res(dev, slave, vhcr->op_modifier, alop,
                                  vhcr->in_param);
@@ -1831,12 +1948,14 @@ int mlx4_FREE_RES_wrapper(struct mlx4_dev *dev, int slave,
 
        case RES_MAC:
                err = mac_free_res(dev, slave, vhcr->op_modifier, alop,
-                                  vhcr->in_param, &vhcr->out_param);
+                                  vhcr->in_param, &vhcr->out_param,
+                                  (vhcr->in_modifier >> 8) & 0xFF);
                break;
 
        case RES_VLAN:
                err = vlan_free_res(dev, slave, vhcr->op_modifier, alop,
-                                  vhcr->in_param, &vhcr->out_param);
+                                   vhcr->in_param, &vhcr->out_param,
+                                   (vhcr->in_modifier >> 8) & 0xFF);
                break;
 
        case RES_COUNTER:
@@ -3964,7 +4083,7 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave)
        struct mlx4_priv *priv = mlx4_priv(dev);
 
        mutex_lock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex);
-       /*VLAN*/
+       rem_slave_vlans(dev, slave);
        rem_slave_macs(dev, slave);
        rem_slave_fs_rule(dev, slave);
        rem_slave_qps(dev, slave);
@@ -4081,7 +4200,7 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work)
        if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_VLAN && !errors &&
            NO_INDX != work->orig_vlan_ix)
                __mlx4_unregister_vlan(&work->priv->dev, work->port,
-                                      work->orig_vlan_ix);
+                                      work->orig_vlan_id);
 out:
        kfree(work);
        return;