IB/mlx4: Fix possible vl/sl field mismatch in LRH header in QP1 packets
[linux-2.6-block.git] / drivers / infiniband / hw / mlx4 / main.c
index 3c6d6103d18bf67669135bb202a4fb433ed08456..1811eb5b6aabfca9246bd6271f17dab27c9358dd 100644 (file)
@@ -832,6 +832,66 @@ static int mlx4_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
        return ret;
 }
 
+static int mlx4_ib_query_sl2vl(struct ib_device *ibdev, u8 port, u64 *sl2vl_tbl)
+{
+       union sl2vl_tbl_to_u64 sl2vl64;
+       struct ib_smp *in_mad  = NULL;
+       struct ib_smp *out_mad = NULL;
+       int mad_ifc_flags = MLX4_MAD_IFC_IGNORE_KEYS;
+       int err = -ENOMEM;
+       int jj;
+
+       if (mlx4_is_slave(to_mdev(ibdev)->dev)) {
+               *sl2vl_tbl = 0;
+               return 0;
+       }
+
+       in_mad  = kzalloc(sizeof(*in_mad), GFP_KERNEL);
+       out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
+       if (!in_mad || !out_mad)
+               goto out;
+
+       init_query_mad(in_mad);
+       in_mad->attr_id  = IB_SMP_ATTR_SL_TO_VL_TABLE;
+       in_mad->attr_mod = 0;
+
+       if (mlx4_is_mfunc(to_mdev(ibdev)->dev))
+               mad_ifc_flags |= MLX4_MAD_IFC_NET_VIEW;
+
+       err = mlx4_MAD_IFC(to_mdev(ibdev), mad_ifc_flags, port, NULL, NULL,
+                          in_mad, out_mad);
+       if (err)
+               goto out;
+
+       for (jj = 0; jj < 8; jj++)
+               sl2vl64.sl8[jj] = ((struct ib_smp *)out_mad)->data[jj];
+       *sl2vl_tbl = sl2vl64.sl64;
+
+out:
+       kfree(in_mad);
+       kfree(out_mad);
+       return err;
+}
+
+static void mlx4_init_sl2vl_tbl(struct mlx4_ib_dev *mdev)
+{
+       u64 sl2vl;
+       int i;
+       int err;
+
+       for (i = 1; i <= mdev->dev->caps.num_ports; i++) {
+               if (mdev->dev->caps.port_type[i] == MLX4_PORT_TYPE_ETH)
+                       continue;
+               err = mlx4_ib_query_sl2vl(&mdev->ib_dev, i, &sl2vl);
+               if (err) {
+                       pr_err("Unable to get default sl to vl mapping for port %d.  Using all zeroes (%d)\n",
+                              i, err);
+                       sl2vl = 0;
+               }
+               atomic64_set(&mdev->sl2vl[i - 1], sl2vl);
+       }
+}
+
 int __mlx4_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index,
                         u16 *pkey, int netw_view)
 {
@@ -2675,6 +2735,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
 
        if (init_node_data(ibdev))
                goto err_map;
+       mlx4_init_sl2vl_tbl(ibdev);
 
        for (i = 0; i < ibdev->num_ports; ++i) {
                mutex_init(&ibdev->counters_table[i].mutex);
@@ -3123,6 +3184,47 @@ static void handle_bonded_port_state_event(struct work_struct *work)
        ib_dispatch_event(&ibev);
 }
 
+void mlx4_ib_sl2vl_update(struct mlx4_ib_dev *mdev, int port)
+{
+       u64 sl2vl;
+       int err;
+
+       err = mlx4_ib_query_sl2vl(&mdev->ib_dev, port, &sl2vl);
+       if (err) {
+               pr_err("Unable to get current sl to vl mapping for port %d.  Using all zeroes (%d)\n",
+                      port, err);
+               sl2vl = 0;
+       }
+       atomic64_set(&mdev->sl2vl[port - 1], sl2vl);
+}
+
+static void ib_sl2vl_update_work(struct work_struct *work)
+{
+       struct ib_event_work *ew = container_of(work, struct ib_event_work, work);
+       struct mlx4_ib_dev *mdev = ew->ib_dev;
+       int port = ew->port;
+
+       mlx4_ib_sl2vl_update(mdev, port);
+
+       kfree(ew);
+}
+
+void mlx4_sched_ib_sl2vl_update_work(struct mlx4_ib_dev *ibdev,
+                                    int port)
+{
+       struct ib_event_work *ew;
+
+       ew = kmalloc(sizeof(*ew), GFP_ATOMIC);
+       if (ew) {
+               INIT_WORK(&ew->work, ib_sl2vl_update_work);
+               ew->port = port;
+               ew->ib_dev = ibdev;
+               queue_work(wq, &ew->work);
+       } else {
+               pr_err("failed to allocate memory for sl2vl update work\n");
+       }
+}
+
 static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
                          enum mlx4_dev_event event, unsigned long param)
 {
@@ -3153,10 +3255,14 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
        case MLX4_DEV_EVENT_PORT_UP:
                if (p > ibdev->num_ports)
                        return;
-               if (mlx4_is_master(dev) &&
+               if (!mlx4_is_slave(dev) &&
                    rdma_port_get_link_layer(&ibdev->ib_dev, p) ==
                        IB_LINK_LAYER_INFINIBAND) {
-                       mlx4_ib_invalidate_all_guid_record(ibdev, p);
+                       if (mlx4_is_master(dev))
+                               mlx4_ib_invalidate_all_guid_record(ibdev, p);
+                       if (ibdev->dev->flags & MLX4_FLAG_SECURE_HOST &&
+                           !(ibdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_SL_TO_VL_CHANGE_EVENT))
+                               mlx4_sched_ib_sl2vl_update_work(ibdev, p);
                }
                ibev.event = IB_EVENT_PORT_ACTIVE;
                break;