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 10 struct mlx5_vhca_state_notifier { 11 struct mlx5_core_dev *dev; 12 struct mlx5_nb nb; 13 struct blocking_notifier_head n_head; 14 }; 15 16 struct 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 22 int 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 33 static 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 45 int 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 59 int 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 69 static void 70 mlx5_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 89 static 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 99 static int 100 mlx5_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 117 void 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 129 int 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(¬ifier->n_head); 143 MLX5_NB_INIT(¬ifier->nb, mlx5_vhca_state_change_notifier, VHCA_STATE_CHANGE); 144 return 0; 145 } 146 147 void 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 156 void 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, ¬ifier->nb); 165 } 166 167 void 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, ¬ifier->nb); 176 } 177 178 int 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 185 void 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