IB/mlx5: Change logic for dispatching IB events for port state
authorMoni Shoua <monis@mellanox.com>
Tue, 30 May 2017 06:56:05 +0000 (09:56 +0300)
committerDoug Ledford <dledford@redhat.com>
Mon, 24 Jul 2017 14:29:18 +0000 (10:29 -0400)
The old logic ignored link state. This led to missing IB events like
when link goes down on the switch while admin state is up or to redundant
events like when admin state goes up while link is down.
To fix that, probe the port state on NETDEV events and compare to last
known state to decide if IB events needs to be dispatched.

FIxes: 5ec8c83e3ad3 ("IB/mlx5: Port events in RoCE now rely on netdev events")
Signed-off-by: Moni Shoua <monis@mellanox.com>
Reviewed-by: Noa Osherovich <noaos@mellanox.com>
Signed-off-by: Leon Romanovsky <leon@kernel.org>
Signed-off-by: Doug Ledford <dledford@redhat.com>
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mlx5_ib.h

index 85bf17a616d5a3464c62f39bf2f24528ccb233f7..bfd9117da10cc3e4b88f32cf5ab4d73922c60b95 100644 (file)
@@ -98,6 +98,20 @@ mlx5_ib_port_link_layer(struct ib_device *device, u8 port_num)
        return mlx5_port_type_cap_to_rdma_ll(port_type_cap);
 }
 
+static int get_port_state(struct ib_device *ibdev,
+                         u8 port_num,
+                         enum ib_port_state *state)
+{
+       struct ib_port_attr attr;
+       int ret;
+
+       memset(&attr, 0, sizeof(attr));
+       ret = mlx5_ib_query_port(ibdev, port_num, &attr);
+       if (!ret)
+               *state = attr.state;
+       return ret;
+}
+
 static int mlx5_netdev_event(struct notifier_block *this,
                             unsigned long event, void *ptr)
 {
@@ -115,6 +129,7 @@ static int mlx5_netdev_event(struct notifier_block *this,
                write_unlock(&ibdev->roce.netdev_lock);
                break;
 
+       case NETDEV_CHANGE:
        case NETDEV_UP:
        case NETDEV_DOWN: {
                struct net_device *lag_ndev = mlx5_lag_get_roce_netdev(ibdev->mdev);
@@ -128,10 +143,23 @@ static int mlx5_netdev_event(struct notifier_block *this,
                if ((upper == ndev || (!upper && ndev == ibdev->roce.netdev))
                    && ibdev->ib_active) {
                        struct ib_event ibev = { };
+                       enum ib_port_state port_state;
 
+                       if (get_port_state(&ibdev->ib_dev, 1, &port_state))
+                               return NOTIFY_DONE;
+
+                       if (ibdev->roce.last_port_state == port_state)
+                               return NOTIFY_DONE;
+
+                       ibdev->roce.last_port_state = port_state;
                        ibev.device = &ibdev->ib_dev;
-                       ibev.event = (event == NETDEV_UP) ?
-                                    IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR;
+                       if (port_state == IB_PORT_DOWN)
+                               ibev.event = IB_EVENT_PORT_ERR;
+                       else if (port_state == IB_PORT_ACTIVE)
+                               ibev.event = IB_EVENT_PORT_ACTIVE;
+                       else
+                               return NOTIFY_DONE;
+
                        ibev.element.port_num = 1;
                        ib_dispatch_event(&ibev);
                }
@@ -3793,6 +3821,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
                err = mlx5_enable_eth(dev);
                if (err)
                        goto err_free_port;
+               dev->roce.last_port_state = IB_PORT_DOWN;
        }
 
        err = create_dev_resources(&dev->devr);
index d2b27647f93998eb936d050f0f8f289b8cafb8d2..5fb4547bbcb8862c95e3a9fe09d9f42171d8db8d 100644 (file)
@@ -616,6 +616,7 @@ struct mlx5_roce {
        struct net_device       *netdev;
        struct notifier_block   nb;
        atomic_t                next_port;
+       enum ib_port_state last_port_state;
 };
 
 struct mlx5_ib_dev {