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