linux/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
   2/* Copyright (c) 2020 Mellanox Technologies Ltd */
   3
   4#include <linux/mlx5/driver.h>
   5#include "mlx5_ifc_vhca_event.h"
   6#include "mlx5_core.h"
   7#include "vhca_event.h"
   8#include "ecpf.h"
   9
  10struct mlx5_vhca_state_notifier {
  11        struct mlx5_core_dev *dev;
  12        struct mlx5_nb nb;
  13        struct blocking_notifier_head n_head;
  14};
  15
  16struct mlx5_vhca_event_work {
  17        struct work_struct work;
  18        struct mlx5_vhca_state_notifier *notifier;
  19        struct mlx5_vhca_state_event event;
  20};
  21
  22int mlx5_cmd_query_vhca_state(struct mlx5_core_dev *dev, u16 function_id, u32 *out, u32 outlen)
  23{
  24        u32 in[MLX5_ST_SZ_DW(query_vhca_state_in)] = {};
  25
  26        MLX5_SET(query_vhca_state_in, in, opcode, MLX5_CMD_OP_QUERY_VHCA_STATE);
  27        MLX5_SET(query_vhca_state_in, in, function_id, function_id);
  28        MLX5_SET(query_vhca_state_in, in, embedded_cpu_function, 0);
  29
  30        return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
  31}
  32
  33static int mlx5_cmd_modify_vhca_state(struct mlx5_core_dev *dev, u16 function_id,
  34                                      u32 *in, u32 inlen)
  35{
  36        u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {};
  37
  38        MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE);
  39        MLX5_SET(modify_vhca_state_in, in, function_id, function_id);
  40        MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, 0);
  41
  42        return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
  43}
  44
  45int mlx5_modify_vhca_sw_id(struct mlx5_core_dev *dev, u16 function_id, u32 sw_fn_id)
  46{
  47        u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {};
  48        u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {};
  49
  50        MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE);
  51        MLX5_SET(modify_vhca_state_in, in, function_id, function_id);
  52        MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, 0);
  53        MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.sw_function_id, 1);
  54        MLX5_SET(modify_vhca_state_in, in, vhca_state_context.sw_function_id, sw_fn_id);
  55
  56        return mlx5_cmd_exec_inout(dev, modify_vhca_state, in, out);
  57}
  58
  59int mlx5_vhca_event_arm(struct mlx5_core_dev *dev, u16 function_id)
  60{
  61        u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {};
  62
  63        MLX5_SET(modify_vhca_state_in, in, vhca_state_context.arm_change_event, 1);
  64        MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.arm_change_event, 1);
  65
  66        return mlx5_cmd_modify_vhca_state(dev, function_id, in, sizeof(in));
  67}
  68
  69static void
  70mlx5_vhca_event_notify(struct mlx5_core_dev *dev, struct mlx5_vhca_state_event *event)
  71{
  72        u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {};
  73        int err;
  74
  75        err = mlx5_cmd_query_vhca_state(dev, event->function_id, out, sizeof(out));
  76        if (err)
  77                return;
  78
  79        event->sw_function_id = MLX5_GET(query_vhca_state_out, out,
  80                                         vhca_state_context.sw_function_id);
  81        event->new_vhca_state = MLX5_GET(query_vhca_state_out, out,
  82                                         vhca_state_context.vhca_state);
  83
  84        mlx5_vhca_event_arm(dev, event->function_id);
  85
  86        blocking_notifier_call_chain(&dev->priv.vhca_state_notifier->n_head, 0, event);
  87}
  88
  89static void mlx5_vhca_state_work_handler(struct work_struct *_work)
  90{
  91        struct mlx5_vhca_event_work *work = container_of(_work, struct mlx5_vhca_event_work, work);
  92        struct mlx5_vhca_state_notifier *notifier = work->notifier;
  93        struct mlx5_core_dev *dev = notifier->dev;
  94
  95        mlx5_vhca_event_notify(dev, &work->event);
  96        kfree(work);
  97}
  98
  99static int
 100mlx5_vhca_state_change_notifier(struct notifier_block *nb, unsigned long type, void *data)
 101{
 102        struct mlx5_vhca_state_notifier *notifier =
 103                                mlx5_nb_cof(nb, struct mlx5_vhca_state_notifier, nb);
 104        struct mlx5_vhca_event_work *work;
 105        struct mlx5_eqe *eqe = data;
 106
 107        work = kzalloc(sizeof(*work), GFP_ATOMIC);
 108        if (!work)
 109                return NOTIFY_DONE;
 110        INIT_WORK(&work->work, &mlx5_vhca_state_work_handler);
 111        work->notifier = notifier;
 112        work->event.function_id = be16_to_cpu(eqe->data.vhca_state.function_id);
 113        mlx5_events_work_enqueue(notifier->dev, &work->work);
 114        return NOTIFY_OK;
 115}
 116
 117void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap)
 118{
 119        if (!mlx5_vhca_event_supported(dev))
 120                return;
 121
 122        MLX5_SET(cmd_hca_cap, set_hca_cap, vhca_state, 1);
 123        MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_allocated, 1);
 124        MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_active, 1);
 125        MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_in_use, 1);
 126        MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_teardown_request, 1);
 127}
 128
 129int mlx5_vhca_event_init(struct mlx5_core_dev *dev)
 130{
 131        struct mlx5_vhca_state_notifier *notifier;
 132
 133        if (!mlx5_vhca_event_supported(dev))
 134                return 0;
 135
 136        notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
 137        if (!notifier)
 138                return -ENOMEM;
 139
 140        dev->priv.vhca_state_notifier = notifier;
 141        notifier->dev = dev;
 142        BLOCKING_INIT_NOTIFIER_HEAD(&notifier->n_head);
 143        MLX5_NB_INIT(&notifier->nb, mlx5_vhca_state_change_notifier, VHCA_STATE_CHANGE);
 144        return 0;
 145}
 146
 147void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev)
 148{
 149        if (!mlx5_vhca_event_supported(dev))
 150                return;
 151
 152        kfree(dev->priv.vhca_state_notifier);
 153        dev->priv.vhca_state_notifier = NULL;
 154}
 155
 156void mlx5_vhca_event_start(struct mlx5_core_dev *dev)
 157{
 158        struct mlx5_vhca_state_notifier *notifier;
 159
 160        if (!dev->priv.vhca_state_notifier)
 161                return;
 162
 163        notifier = dev->priv.vhca_state_notifier;
 164        mlx5_eq_notifier_register(dev, &notifier->nb);
 165}
 166
 167void mlx5_vhca_event_stop(struct mlx5_core_dev *dev)
 168{
 169        struct mlx5_vhca_state_notifier *notifier;
 170
 171        if (!dev->priv.vhca_state_notifier)
 172                return;
 173
 174        notifier = dev->priv.vhca_state_notifier;
 175        mlx5_eq_notifier_unregister(dev, &notifier->nb);
 176}
 177
 178int mlx5_vhca_event_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb)
 179{
 180        if (!dev->priv.vhca_state_notifier)
 181                return -EOPNOTSUPP;
 182        return blocking_notifier_chain_register(&dev->priv.vhca_state_notifier->n_head, nb);
 183}
 184
 185void mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb)
 186{
 187        blocking_notifier_chain_unregister(&dev->priv.vhca_state_notifier->n_head, nb);
 188}
 189