IB/mlx4: Add counter based implementation for QP multicast loopback block
[linux-2.6-block.git] / drivers / infiniband / hw / mlx4 / qp.c
index 382913e354861f2557ca89c1f85709c93d08b73f..28719494ad6e70765a36126c8d08fc8a25218a59 100644 (file)
@@ -617,6 +617,18 @@ static int qp0_enabled_vf(struct mlx4_dev *dev, int qpn)
        return 0;
 }
 
+static void mlx4_ib_free_qp_counter(struct mlx4_ib_dev *dev,
+                                   struct mlx4_ib_qp *qp)
+{
+       mutex_lock(&dev->counters_table[qp->port - 1].mutex);
+       mlx4_counter_free(dev->dev, qp->counter_index->index);
+       list_del(&qp->counter_index->list);
+       mutex_unlock(&dev->counters_table[qp->port - 1].mutex);
+
+       kfree(qp->counter_index);
+       qp->counter_index = NULL;
+}
+
 static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
                            struct ib_qp_init_attr *init_attr,
                            struct ib_udata *udata, int sqpn, struct mlx4_ib_qp **caller_qp,
@@ -1189,6 +1201,9 @@ int mlx4_ib_destroy_qp(struct ib_qp *qp)
                mutex_unlock(&dev->qp1_proxy_lock[mqp->port - 1]);
        }
 
+       if (mqp->counter_index)
+               mlx4_ib_free_qp_counter(dev, mqp);
+
        pd = get_pd(mqp);
        destroy_qp_common(dev, mqp, !!pd->ibpd.uobject);
 
@@ -1447,6 +1462,40 @@ static int handle_eth_ud_smac_index(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *
        return 0;
 }
 
+static int create_qp_lb_counter(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp)
+{
+       struct counter_index *new_counter_index;
+       int err;
+       u32 tmp_idx;
+
+       if (rdma_port_get_link_layer(&dev->ib_dev, qp->port) !=
+           IB_LINK_LAYER_ETHERNET ||
+           !(qp->flags & MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK) ||
+           !(dev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_LB_SRC_CHK))
+               return 0;
+
+       err = mlx4_counter_alloc(dev->dev, &tmp_idx);
+       if (err)
+               return err;
+
+       new_counter_index = kmalloc(sizeof(*new_counter_index), GFP_KERNEL);
+       if (!new_counter_index) {
+               mlx4_counter_free(dev->dev, tmp_idx);
+               return -ENOMEM;
+       }
+
+       new_counter_index->index = tmp_idx;
+       new_counter_index->allocated = 1;
+       qp->counter_index = new_counter_index;
+
+       mutex_lock(&dev->counters_table[qp->port - 1].mutex);
+       list_add_tail(&new_counter_index->list,
+                     &dev->counters_table[qp->port - 1].counters_list);
+       mutex_unlock(&dev->counters_table[qp->port - 1].mutex);
+
+       return 0;
+}
+
 static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
                               const struct ib_qp_attr *attr, int attr_mask,
                               enum ib_qp_state cur_state, enum ib_qp_state new_state)
@@ -1520,6 +1569,9 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
                context->sq_size_stride = ilog2(qp->sq.wqe_cnt) << 3;
        context->sq_size_stride |= qp->sq.wqe_shift - 4;
 
+       if (new_state == IB_QPS_RESET && qp->counter_index)
+               mlx4_ib_free_qp_counter(dev, qp);
+
        if (cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT) {
                context->sq_size_stride |= !!qp->sq_no_prefetch << 7;
                context->xrcd = cpu_to_be32((u32) qp->xrcdn);
@@ -1544,11 +1596,24 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
        }
 
        if (cur_state == IB_QPS_INIT && new_state == IB_QPS_RTR) {
+               err = create_qp_lb_counter(dev, qp);
+               if (err)
+                       goto out;
+
                counter_index =
                        dev->counters_table[qp->port - 1].default_counter;
+               if (qp->counter_index)
+                       counter_index = qp->counter_index->index;
+
                if (counter_index != -1) {
                        context->pri_path.counter_index = counter_index;
                        optpar |= MLX4_QP_OPTPAR_COUNTER_INDEX;
+                       if (qp->counter_index) {
+                               context->pri_path.fl |=
+                                       MLX4_FL_ETH_SRC_CHECK_MC_LB;
+                               context->pri_path.vlan_control |=
+                                       MLX4_CTRL_ETH_SRC_CHECK_IF_COUNTER;
+                       }
                } else
                        context->pri_path.counter_index =
                                MLX4_SINK_COUNTER_INDEX(dev->dev);
@@ -1850,6 +1915,8 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
                }
        }
 out:
+       if (err && qp->counter_index)
+               mlx4_ib_free_qp_counter(dev, qp);
        if (err && steer_qp)
                mlx4_ib_steer_qp_reg(dev, qp, 0);
        kfree(context);