net/mlx4: Adapt code for N-Port VF
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx4 / resource_tracker.c
index 57428a0..2a33513 100644 (file)
@@ -52,6 +52,8 @@
 struct mac_res {
        struct list_head list;
        u64 mac;
+       int ref_count;
+       u8 smac_index;
        u8 port;
 };
 
@@ -219,6 +221,11 @@ struct res_fs_rule {
        int                     qpn;
 };
 
+static int mlx4_is_eth(struct mlx4_dev *dev, int port)
+{
+       return dev->caps.port_mask[port] == MLX4_PORT_TYPE_IB ? 0 : 1;
+}
+
 static void *res_tracker_lookup(struct rb_root *root, u64 res_id)
 {
        struct rb_node *node = root->rb_node;
@@ -461,6 +468,8 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev)
 
                spin_lock_init(&res_alloc->alloc_lock);
                for (t = 0; t < dev->num_vfs + 1; t++) {
+                       struct mlx4_active_ports actv_ports =
+                               mlx4_get_active_ports(dev, t);
                        switch (i) {
                        case RES_QP:
                                initialize_res_quotas(dev, res_alloc, RES_QP,
@@ -490,10 +499,27 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev)
                                break;
                        case RES_MAC:
                                if (t == mlx4_master_func_num(dev)) {
-                                       res_alloc->quota[t] = MLX4_MAX_MAC_NUM;
+                                       int max_vfs_pport = 0;
+                                       /* Calculate the max vfs per port for */
+                                       /* both ports.                        */
+                                       for (j = 0; j < dev->caps.num_ports;
+                                            j++) {
+                                               struct mlx4_slaves_pport slaves_pport =
+                                                       mlx4_phys_to_slaves_pport(dev, j + 1);
+                                               unsigned current_slaves =
+                                                       bitmap_weight(slaves_pport.slaves,
+                                                                     dev->caps.num_ports) - 1;
+                                               if (max_vfs_pport < current_slaves)
+                                                       max_vfs_pport =
+                                                               current_slaves;
+                                       }
+                                       res_alloc->quota[t] =
+                                               MLX4_MAX_MAC_NUM -
+                                               2 * max_vfs_pport;
                                        res_alloc->guaranteed[t] = 2;
                                        for (j = 0; j < MLX4_MAX_PORTS; j++)
-                                               res_alloc->res_port_free[j] = MLX4_MAX_MAC_NUM;
+                                               res_alloc->res_port_free[j] =
+                                                       MLX4_MAX_MAC_NUM;
                                } else {
                                        res_alloc->quota[t] = MLX4_MAX_MAC_NUM;
                                        res_alloc->guaranteed[t] = 2;
@@ -521,9 +547,10 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev)
                                break;
                        }
                        if (i == RES_MAC || i == RES_VLAN) {
-                               for (j = 0; j < MLX4_MAX_PORTS; j++)
-                                       res_alloc->res_port_rsvd[j] +=
-                                               res_alloc->guaranteed[t];
+                               for (j = 0; j < dev->caps.num_ports; j++)
+                                       if (test_bit(j, actv_ports.ports))
+                                               res_alloc->res_port_rsvd[j] +=
+                                                       res_alloc->guaranteed[t];
                        } else {
                                res_alloc->res_reserved += res_alloc->guaranteed[t];
                        }
@@ -600,15 +627,37 @@ static void update_gid(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *inbox,
        struct mlx4_qp_context  *qp_ctx = inbox->buf + 8;
        enum mlx4_qp_optpar     optpar = be32_to_cpu(*(__be32 *) inbox->buf);
        u32                     ts = (be32_to_cpu(qp_ctx->flags) >> 16) & 0xff;
+       int port;
 
-       if (MLX4_QP_ST_UD == ts)
-               qp_ctx->pri_path.mgid_index = 0x80 | slave;
-
-       if (MLX4_QP_ST_RC == ts || MLX4_QP_ST_UC == ts) {
-               if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH)
-                       qp_ctx->pri_path.mgid_index = slave & 0x7F;
-               if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH)
-                       qp_ctx->alt_path.mgid_index = slave & 0x7F;
+       if (MLX4_QP_ST_UD == ts) {
+               port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1;
+               if (mlx4_is_eth(dev, port))
+                       qp_ctx->pri_path.mgid_index =
+                               mlx4_get_base_gid_ix(dev, slave, port) | 0x80;
+               else
+                       qp_ctx->pri_path.mgid_index = slave | 0x80;
+
+       } else if (MLX4_QP_ST_RC == ts || MLX4_QP_ST_XRC == ts || MLX4_QP_ST_UC == ts) {
+               if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH) {
+                       port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1;
+                       if (mlx4_is_eth(dev, port)) {
+                               qp_ctx->pri_path.mgid_index +=
+                                       mlx4_get_base_gid_ix(dev, slave, port);
+                               qp_ctx->pri_path.mgid_index &= 0x7f;
+                       } else {
+                               qp_ctx->pri_path.mgid_index = slave & 0x7F;
+                       }
+               }
+               if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH) {
+                       port = (qp_ctx->alt_path.sched_queue >> 6 & 1) + 1;
+                       if (mlx4_is_eth(dev, port)) {
+                               qp_ctx->alt_path.mgid_index +=
+                                       mlx4_get_base_gid_ix(dev, slave, port);
+                               qp_ctx->alt_path.mgid_index &= 0x7f;
+                       } else {
+                               qp_ctx->alt_path.mgid_index = slave & 0x7F;
+                       }
+               }
        }
 }
 
@@ -619,7 +668,6 @@ static int update_vport_qp_param(struct mlx4_dev *dev,
        struct mlx4_qp_context  *qpc = inbox->buf + 8;
        struct mlx4_vport_oper_state *vp_oper;
        struct mlx4_priv *priv;
-       u32 qp_type;
        int port;
 
        port = (qpc->pri_path.sched_queue & 0x40) ? 2 : 1;
@@ -627,12 +675,6 @@ static int update_vport_qp_param(struct mlx4_dev *dev,
        vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
 
        if (MLX4_VGT != vp_oper->state.default_vlan) {
-               qp_type = (be32_to_cpu(qpc->flags) >> 16) & 0xff;
-               if (MLX4_QP_ST_RC == qp_type ||
-                   (MLX4_QP_ST_UD == qp_type &&
-                    !mlx4_is_qp_reserved(dev, qpn)))
-                       return -EINVAL;
-
                /* the reserved QPs (special, proxy, tunnel)
                 * do not operate over vlans
                 */
@@ -1659,11 +1701,39 @@ static int srq_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        return err;
 }
 
-static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port)
+static int mac_find_smac_ix_in_slave(struct mlx4_dev *dev, int slave, int port,
+                                    u8 smac_index, u64 *mac)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+       struct list_head *mac_list =
+               &tracker->slave_list[slave].res_list[RES_MAC];
+       struct mac_res *res, *tmp;
+
+       list_for_each_entry_safe(res, tmp, mac_list, list) {
+               if (res->smac_index == smac_index && res->port == (u8) port) {
+                       *mac = res->mac;
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+
+static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port, u8 smac_index)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
        struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
-       struct mac_res *res;
+       struct list_head *mac_list =
+               &tracker->slave_list[slave].res_list[RES_MAC];
+       struct mac_res *res, *tmp;
+
+       list_for_each_entry_safe(res, tmp, mac_list, list) {
+               if (res->mac == mac && res->port == (u8) port) {
+                       /* mac found. update ref count */
+                       ++res->ref_count;
+                       return 0;
+               }
+       }
 
        if (mlx4_grant_resource(dev, slave, RES_MAC, 1, port))
                return -EINVAL;
@@ -1674,6 +1744,8 @@ static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port)
        }
        res->mac = mac;
        res->port = (u8) port;
+       res->smac_index = smac_index;
+       res->ref_count = 1;
        list_add_tail(&res->list,
                      &tracker->slave_list[slave].res_list[RES_MAC]);
        return 0;
@@ -1690,9 +1762,11 @@ static void mac_del_from_slave(struct mlx4_dev *dev, int slave, u64 mac,
 
        list_for_each_entry_safe(res, tmp, mac_list, list) {
                if (res->mac == mac && res->port == (u8) port) {
-                       list_del(&res->list);
-                       mlx4_release_resource(dev, slave, RES_MAC, 1, port);
-                       kfree(res);
+                       if (!--res->ref_count) {
+                               list_del(&res->list);
+                               mlx4_release_resource(dev, slave, RES_MAC, 1, port);
+                               kfree(res);
+                       }
                        break;
                }
        }
@@ -1705,10 +1779,13 @@ static void rem_slave_macs(struct mlx4_dev *dev, int slave)
        struct list_head *mac_list =
                &tracker->slave_list[slave].res_list[RES_MAC];
        struct mac_res *res, *tmp;
+       int i;
 
        list_for_each_entry_safe(res, tmp, mac_list, list) {
                list_del(&res->list);
-               __mlx4_unregister_mac(dev, res->port, res->mac);
+               /* dereference the mac the num times the slave referenced it */
+               for (i = 0; i < res->ref_count; i++)
+                       __mlx4_unregister_mac(dev, res->port, res->mac);
                mlx4_release_resource(dev, slave, RES_MAC, 1, res->port);
                kfree(res);
        }
@@ -1720,21 +1797,28 @@ static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        int err = -EINVAL;
        int port;
        u64 mac;
+       u8 smac_index;
 
        if (op != RES_OP_RESERVE_AND_MAP)
                return err;
 
        port = !in_port ? get_param_l(out_param) : in_port;
+       port = mlx4_slave_convert_port(
+                       dev, slave, port);
+
+       if (port < 0)
+               return -EINVAL;
        mac = in_param;
 
        err = __mlx4_register_mac(dev, port, mac);
        if (err >= 0) {
+               smac_index = err;
                set_param_l(out_param, err);
                err = 0;
        }
 
        if (!err) {
-               err = mac_add_to_slave(dev, slave, mac, port);
+               err = mac_add_to_slave(dev, slave, mac, port, smac_index);
                if (err)
                        __mlx4_unregister_mac(dev, port, mac);
        }
@@ -1831,6 +1915,11 @@ static int vlan_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        if (!port || op != RES_OP_RESERVE_AND_MAP)
                return -EINVAL;
 
+       port = mlx4_slave_convert_port(
+                       dev, slave, port);
+
+       if (port < 0)
+               return -EINVAL;
        /* upstream kernels had NOP for reg/unreg vlan. Continue this. */
        if (!in_port && port > 0 && port <= dev->caps.num_ports) {
                slave_state[slave].old_vlan_api = true;
@@ -2128,6 +2217,11 @@ static int mac_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        switch (op) {
        case RES_OP_RESERVE_AND_MAP:
                port = !in_port ? get_param_l(out_param) : in_port;
+               port = mlx4_slave_convert_port(
+                               dev, slave, port);
+
+               if (port < 0)
+                       return -EINVAL;
                mac_del_from_slave(dev, slave, in_param, port);
                __mlx4_unregister_mac(dev, port, in_param);
                break;
@@ -2147,6 +2241,11 @@ static int vlan_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        struct mlx4_slave_state *slave_state = priv->mfunc.master.slave_state;
        int err = 0;
 
+       port = mlx4_slave_convert_port(
+                       dev, slave, port);
+
+       if (port < 0)
+               return -EINVAL;
        switch (op) {
        case RES_OP_RESERVE_AND_MAP:
                if (slave_state[slave].old_vlan_api)
@@ -2734,6 +2833,8 @@ static int verify_qp_parameters(struct mlx4_dev *dev,
        u32                     qp_type;
        struct mlx4_qp_context  *qp_ctx;
        enum mlx4_qp_optpar     optpar;
+       int port;
+       int num_gids;
 
        qp_ctx  = inbox->buf + 8;
        qp_type = (be32_to_cpu(qp_ctx->flags) >> 16) & 0xff;
@@ -2741,6 +2842,7 @@ static int verify_qp_parameters(struct mlx4_dev *dev,
 
        switch (qp_type) {
        case MLX4_QP_ST_RC:
+       case MLX4_QP_ST_XRC:
        case MLX4_QP_ST_UC:
                switch (transition) {
                case QP_TRANS_INIT2RTR:
@@ -2749,13 +2851,24 @@ static int verify_qp_parameters(struct mlx4_dev *dev,
                case QP_TRANS_SQD2SQD:
                case QP_TRANS_SQD2RTS:
                        if (slave != mlx4_master_func_num(dev))
-                               /* slaves have only gid index 0 */
-                               if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH)
-                                       if (qp_ctx->pri_path.mgid_index)
+                               if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH) {
+                                       port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1;
+                                       if (dev->caps.port_mask[port] != MLX4_PORT_TYPE_IB)
+                                               num_gids = mlx4_get_slave_num_gids(dev, slave, port);
+                                       else
+                                               num_gids = 1;
+                                       if (qp_ctx->pri_path.mgid_index >= num_gids)
                                                return -EINVAL;
-                               if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH)
-                                       if (qp_ctx->alt_path.mgid_index)
+                               }
+                               if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH) {
+                                       port = (qp_ctx->alt_path.sched_queue >> 6 & 1) + 1;
+                                       if (dev->caps.port_mask[port] != MLX4_PORT_TYPE_IB)
+                                               num_gids = mlx4_get_slave_num_gids(dev, slave, port);
+                                       else
+                                               num_gids = 1;
+                                       if (qp_ctx->alt_path.mgid_index >= num_gids)
                                                return -EINVAL;
+                               }
                        break;
                default:
                        break;
@@ -3268,6 +3381,58 @@ int mlx4_INIT2INIT_QP_wrapper(struct mlx4_dev *dev, int slave,
        return mlx4_GEN_QP_wrapper(dev, slave, vhcr, inbox, outbox, cmd);
 }
 
+static int adjust_qp_sched_queue(struct mlx4_dev *dev, int slave,
+                                 struct mlx4_qp_context *qpc,
+                                 struct mlx4_cmd_mailbox *inbox)
+{
+       enum mlx4_qp_optpar optpar = be32_to_cpu(*(__be32 *)inbox->buf);
+       u8 pri_sched_queue;
+       int port = mlx4_slave_convert_port(
+                  dev, slave, (qpc->pri_path.sched_queue >> 6 & 1) + 1) - 1;
+
+       if (port < 0)
+               return -EINVAL;
+
+       pri_sched_queue = (qpc->pri_path.sched_queue & ~(1 << 6)) |
+                         ((port & 1) << 6);
+
+       if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH ||
+           mlx4_is_eth(dev, port + 1)) {
+               qpc->pri_path.sched_queue = pri_sched_queue;
+       }
+
+       if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH) {
+               port = mlx4_slave_convert_port(
+                               dev, slave, (qpc->alt_path.sched_queue >> 6 & 1)
+                               + 1) - 1;
+               if (port < 0)
+                       return -EINVAL;
+               qpc->alt_path.sched_queue =
+                       (qpc->alt_path.sched_queue & ~(1 << 6)) |
+                       (port & 1) << 6;
+       }
+       return 0;
+}
+
+static int roce_verify_mac(struct mlx4_dev *dev, int slave,
+                               struct mlx4_qp_context *qpc,
+                               struct mlx4_cmd_mailbox *inbox)
+{
+       u64 mac;
+       int port;
+       u32 ts = (be32_to_cpu(qpc->flags) >> 16) & 0xff;
+       u8 sched = *(u8 *)(inbox->buf + 64);
+       u8 smac_ix;
+
+       port = (sched >> 6 & 1) + 1;
+       if (mlx4_is_eth(dev, port) && (ts != MLX4_QP_ST_MLX)) {
+               smac_ix = qpc->pri_path.grh_mylmc & 0x7f;
+               if (mac_find_smac_ix_in_slave(dev, slave, port, smac_ix, &mac))
+                       return -ENOENT;
+       }
+       return 0;
+}
+
 int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave,
                             struct mlx4_vhcr *vhcr,
                             struct mlx4_cmd_mailbox *inbox,
@@ -3286,10 +3451,16 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave,
        u8 orig_vlan_index = qpc->pri_path.vlan_index;
        u8 orig_feup = qpc->pri_path.feup;
 
+       err = adjust_qp_sched_queue(dev, slave, qpc, inbox);
+       if (err)
+               return err;
        err = verify_qp_parameters(dev, inbox, QP_TRANS_INIT2RTR, slave);
        if (err)
                return err;
 
+       if (roce_verify_mac(dev, slave, qpc, inbox))
+               return -EINVAL;
+
        update_pkey_index(dev, slave, inbox);
        update_gid(dev, inbox, (u8)slave);
        adjust_proxy_tun_qkey(dev, vhcr, qpc);
@@ -3334,6 +3505,9 @@ int mlx4_RTR2RTS_QP_wrapper(struct mlx4_dev *dev, int slave,
        int err;
        struct mlx4_qp_context *context = inbox->buf + 8;
 
+       err = adjust_qp_sched_queue(dev, slave, context, inbox);
+       if (err)
+               return err;
        err = verify_qp_parameters(dev, inbox, QP_TRANS_RTR2RTS, slave);
        if (err)
                return err;
@@ -3353,6 +3527,9 @@ int mlx4_RTS2RTS_QP_wrapper(struct mlx4_dev *dev, int slave,
        int err;
        struct mlx4_qp_context *context = inbox->buf + 8;
 
+       err = adjust_qp_sched_queue(dev, slave, context, inbox);
+       if (err)
+               return err;
        err = verify_qp_parameters(dev, inbox, QP_TRANS_RTS2RTS, slave);
        if (err)
                return err;
@@ -3371,6 +3548,9 @@ int mlx4_SQERR2RTS_QP_wrapper(struct mlx4_dev *dev, int slave,
                              struct mlx4_cmd_info *cmd)
 {
        struct mlx4_qp_context *context = inbox->buf + 8;
+       int err = adjust_qp_sched_queue(dev, slave, context, inbox);
+       if (err)
+               return err;
        adjust_proxy_tun_qkey(dev, vhcr, context);
        return mlx4_GEN_QP_wrapper(dev, slave, vhcr, inbox, outbox, cmd);
 }
@@ -3384,6 +3564,9 @@ int mlx4_SQD2SQD_QP_wrapper(struct mlx4_dev *dev, int slave,
        int err;
        struct mlx4_qp_context *context = inbox->buf + 8;
 
+       err = adjust_qp_sched_queue(dev, slave, context, inbox);
+       if (err)
+               return err;
        err = verify_qp_parameters(dev, inbox, QP_TRANS_SQD2SQD, slave);
        if (err)
                return err;
@@ -3403,6 +3586,9 @@ int mlx4_SQD2RTS_QP_wrapper(struct mlx4_dev *dev, int slave,
        int err;
        struct mlx4_qp_context *context = inbox->buf + 8;
 
+       err = adjust_qp_sched_queue(dev, slave, context, inbox);
+       if (err)
+               return err;
        err = verify_qp_parameters(dev, inbox, QP_TRANS_SQD2RTS, slave);
        if (err)
                return err;
@@ -3506,16 +3692,26 @@ static int rem_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp,
        return err;
 }
 
-static int qp_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
-                    int block_loopback, enum mlx4_protocol prot,
+static int qp_attach(struct mlx4_dev *dev, int slave, struct mlx4_qp *qp,
+                    u8 gid[16], int block_loopback, enum mlx4_protocol prot,
                     enum mlx4_steer_type type, u64 *reg_id)
 {
        switch (dev->caps.steering_mode) {
-       case MLX4_STEERING_MODE_DEVICE_MANAGED:
-               return mlx4_trans_to_dmfs_attach(dev, qp, gid, gid[5],
+       case MLX4_STEERING_MODE_DEVICE_MANAGED: {
+               int port = mlx4_slave_convert_port(dev, slave, gid[5]);
+               if (port < 0)
+                       return port;
+               return mlx4_trans_to_dmfs_attach(dev, qp, gid, port,
                                                block_loopback, prot,
                                                reg_id);
+       }
        case MLX4_STEERING_MODE_B0:
+               if (prot == MLX4_PROT_ETH) {
+                       int port = mlx4_slave_convert_port(dev, slave, gid[5]);
+                       if (port < 0)
+                               return port;
+                       gid[5] = port;
+               }
                return mlx4_qp_attach_common(dev, qp, gid,
                                            block_loopback, prot, type);
        default:
@@ -3523,9 +3719,9 @@ static int qp_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
        }
 }
 
-static int qp_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
-                    enum mlx4_protocol prot, enum mlx4_steer_type type,
-                    u64 reg_id)
+static int qp_detach(struct mlx4_dev *dev, struct mlx4_qp *qp,
+                    u8 gid[16], enum mlx4_protocol prot,
+                    enum mlx4_steer_type type, u64 reg_id)
 {
        switch (dev->caps.steering_mode) {
        case MLX4_STEERING_MODE_DEVICE_MANAGED:
@@ -3562,7 +3758,7 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
 
        qp.qpn = qpn;
        if (attach) {
-               err = qp_attach(dev, &qp, gid, block_loopback, prot,
+               err = qp_attach(dev, slave, &qp, gid, block_loopback, prot,
                                type, &reg_id);
                if (err) {
                        pr_err("Fail to attach rule to qp 0x%x\n", qpn);
@@ -3698,6 +3894,9 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
                return -EOPNOTSUPP;
 
        ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf;
+       ctrl->port = mlx4_slave_convert_port(dev, slave, ctrl->port);
+       if (ctrl->port <= 0)
+               return -EINVAL;
        qpn = be32_to_cpu(ctrl->qpn) & 0xffffff;
        err = get_res(dev, slave, qpn, RES_QP, &rqp);
        if (err) {