1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2/* Copyright (c) 2020 Mellanox Technologies Ltd */ 3#include <linux/mlx5/driver.h> 4#include "vhca_event.h" 5#include "priv.h" 6#include "sf.h" 7#include "mlx5_ifc_vhca_event.h" 8#include "ecpf.h" 9#include "mlx5_core.h" 10#include "eswitch.h" 11#include "diag/sf_tracepoint.h" 12#include "devlink.h" 13 14struct mlx5_sf_hw { 15 u32 usr_sfnum; 16 u8 allocated: 1; 17 u8 pending_delete: 1; 18}; 19 20struct mlx5_sf_hwc_table { 21 struct mlx5_sf_hw *sfs; 22 int max_fn; 23 u16 start_fn_id; 24}; 25 26enum mlx5_sf_hwc_index { 27 MLX5_SF_HWC_LOCAL, 28 MLX5_SF_HWC_EXTERNAL, 29 MLX5_SF_HWC_MAX, 30}; 31 32struct mlx5_sf_hw_table { 33 struct mlx5_core_dev *dev; 34 struct mutex table_lock; /* Serializes sf deletion and vhca state change handler. */ 35 struct notifier_block vhca_nb; 36 struct mlx5_sf_hwc_table hwc[MLX5_SF_HWC_MAX]; 37}; 38 39static struct mlx5_sf_hwc_table * 40mlx5_sf_controller_to_hwc(struct mlx5_core_dev *dev, u32 controller) 41{ 42 int idx = !!controller; 43 44 return &dev->priv.sf_hw_table->hwc[idx]; 45} 46 47u16 mlx5_sf_sw_to_hw_id(struct mlx5_core_dev *dev, u32 controller, u16 sw_id) 48{ 49 struct mlx5_sf_hwc_table *hwc; 50 51 hwc = mlx5_sf_controller_to_hwc(dev, controller); 52 return hwc->start_fn_id + sw_id; 53} 54 55static u16 mlx5_sf_hw_to_sw_id(struct mlx5_sf_hwc_table *hwc, u16 hw_id) 56{ 57 return hw_id - hwc->start_fn_id; 58} 59 60static struct mlx5_sf_hwc_table * 61mlx5_sf_table_fn_to_hwc(struct mlx5_sf_hw_table *table, u16 fn_id) 62{ 63 int i; 64 65 for (i = 0; i < ARRAY_SIZE(table->hwc); i++) { 66 if (table->hwc[i].max_fn && 67 fn_id >= table->hwc[i].start_fn_id && 68 fn_id < (table->hwc[i].start_fn_id + table->hwc[i].max_fn)) 69 return &table->hwc[i]; 70 } 71 return NULL; 72} 73 74static int mlx5_sf_hw_table_id_alloc(struct mlx5_sf_hw_table *table, u32 controller, 75 u32 usr_sfnum) 76{ 77 struct mlx5_sf_hwc_table *hwc; 78 int free_idx = -1; 79 int i; 80 81 hwc = mlx5_sf_controller_to_hwc(table->dev, controller); 82 if (!hwc->sfs) 83 return -ENOSPC; 84 85 for (i = 0; i < hwc->max_fn; i++) { 86 if (!hwc->sfs[i].allocated && free_idx == -1) { 87 free_idx = i; 88 continue; 89 } 90 91 if (hwc->sfs[i].allocated && hwc->sfs[i].usr_sfnum == usr_sfnum) 92 return -EEXIST; 93 } 94 95 if (free_idx == -1) 96 return -ENOSPC; 97 98 hwc->sfs[free_idx].usr_sfnum = usr_sfnum; 99 hwc->sfs[free_idx].allocated = true; 100 return free_idx; 101} 102 103static void mlx5_sf_hw_table_id_free(struct mlx5_sf_hw_table *table, u32 controller, int id) 104{ 105 struct mlx5_sf_hwc_table *hwc; 106 107 hwc = mlx5_sf_controller_to_hwc(table->dev, controller); 108 hwc->sfs[id].allocated = false; 109 hwc->sfs[id].pending_delete = false; 110} 111 112int mlx5_sf_hw_table_sf_alloc(struct mlx5_core_dev *dev, u32 controller, u32 usr_sfnum) 113{ 114 struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; 115 u16 hw_fn_id; 116 int sw_id; 117 int err; 118 119 if (!table) 120 return -EOPNOTSUPP; 121 122 mutex_lock(&table->table_lock); 123 sw_id = mlx5_sf_hw_table_id_alloc(table, controller, usr_sfnum); 124 if (sw_id < 0) { 125 err = sw_id; 126 goto exist_err; 127 } 128 129 hw_fn_id = mlx5_sf_sw_to_hw_id(dev, controller, sw_id); 130 err = mlx5_cmd_alloc_sf(dev, hw_fn_id); 131 if (err) 132 goto err; 133 134 err = mlx5_modify_vhca_sw_id(dev, hw_fn_id, usr_sfnum); 135 if (err) 136 goto vhca_err; 137 138 if (controller) { 139 /* If this SF is for external controller, SF manager 140 * needs to arm firmware to receive the events. 141 */ 142 err = mlx5_vhca_event_arm(dev, hw_fn_id); 143 if (err) 144 goto vhca_err; 145 } 146 147 trace_mlx5_sf_hwc_alloc(dev, controller, hw_fn_id, usr_sfnum); 148 mutex_unlock(&table->table_lock); 149 return sw_id; 150 151vhca_err: 152 mlx5_cmd_dealloc_sf(dev, hw_fn_id); 153err: 154 mlx5_sf_hw_table_id_free(table, controller, sw_id); 155exist_err: 156 mutex_unlock(&table->table_lock); 157 return err; 158} 159 160void mlx5_sf_hw_table_sf_free(struct mlx5_core_dev *dev, u32 controller, u16 id) 161{ 162 struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; 163 u16 hw_fn_id; 164 165 mutex_lock(&table->table_lock); 166 hw_fn_id = mlx5_sf_sw_to_hw_id(dev, controller, id); 167 mlx5_cmd_dealloc_sf(dev, hw_fn_id); 168 mlx5_sf_hw_table_id_free(table, controller, id); 169 mutex_unlock(&table->table_lock); 170} 171 172static void mlx5_sf_hw_table_hwc_sf_free(struct mlx5_core_dev *dev, 173 struct mlx5_sf_hwc_table *hwc, int idx) 174{ 175 mlx5_cmd_dealloc_sf(dev, hwc->start_fn_id + idx); 176 hwc->sfs[idx].allocated = false; 177 hwc->sfs[idx].pending_delete = false; 178 trace_mlx5_sf_hwc_free(dev, hwc->start_fn_id + idx); 179} 180 181void mlx5_sf_hw_table_sf_deferred_free(struct mlx5_core_dev *dev, u32 controller, u16 id) 182{ 183 struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; 184 u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {}; 185 struct mlx5_sf_hwc_table *hwc; 186 u16 hw_fn_id; 187 u8 state; 188 int err; 189 190 hw_fn_id = mlx5_sf_sw_to_hw_id(dev, controller, id); 191 hwc = mlx5_sf_controller_to_hwc(dev, controller); 192 mutex_lock(&table->table_lock); 193 err = mlx5_cmd_query_vhca_state(dev, hw_fn_id, out, sizeof(out)); 194 if (err) 195 goto err; 196 state = MLX5_GET(query_vhca_state_out, out, vhca_state_context.vhca_state); 197 if (state == MLX5_VHCA_STATE_ALLOCATED) { 198 mlx5_cmd_dealloc_sf(dev, hw_fn_id); 199 hwc->sfs[id].allocated = false; 200 } else { 201 hwc->sfs[id].pending_delete = true; 202 trace_mlx5_sf_hwc_deferred_free(dev, hw_fn_id); 203 } 204err: 205 mutex_unlock(&table->table_lock); 206} 207 208static void mlx5_sf_hw_table_hwc_dealloc_all(struct mlx5_core_dev *dev, 209 struct mlx5_sf_hwc_table *hwc) 210{ 211 int i; 212 213 for (i = 0; i < hwc->max_fn; i++) { 214 if (hwc->sfs[i].allocated) 215 mlx5_sf_hw_table_hwc_sf_free(dev, hwc, i); 216 } 217} 218 219static void mlx5_sf_hw_table_dealloc_all(struct mlx5_sf_hw_table *table) 220{ 221 mlx5_sf_hw_table_hwc_dealloc_all(table->dev, &table->hwc[MLX5_SF_HWC_EXTERNAL]); 222 mlx5_sf_hw_table_hwc_dealloc_all(table->dev, &table->hwc[MLX5_SF_HWC_LOCAL]); 223} 224 225static int mlx5_sf_hw_table_hwc_init(struct mlx5_sf_hwc_table *hwc, u16 max_fn, u16 base_id) 226{ 227 struct mlx5_sf_hw *sfs; 228 229 if (!max_fn) 230 return 0; 231 232 sfs = kcalloc(max_fn, sizeof(*sfs), GFP_KERNEL); 233 if (!sfs) 234 return -ENOMEM; 235 236 hwc->sfs = sfs; 237 hwc->max_fn = max_fn; 238 hwc->start_fn_id = base_id; 239 return 0; 240} 241 242static void mlx5_sf_hw_table_hwc_cleanup(struct mlx5_sf_hwc_table *hwc) 243{ 244 kfree(hwc->sfs); 245} 246 247static void mlx5_sf_hw_table_res_unregister(struct mlx5_core_dev *dev) 248{ 249 devl_resources_unregister(priv_to_devlink(dev)); 250} 251 252static int mlx5_sf_hw_table_res_register(struct mlx5_core_dev *dev, u16 max_fn, 253 u16 max_ext_fn) 254{ 255 struct devlink_resource_size_params size_params; 256 struct devlink *devlink = priv_to_devlink(dev); 257 int err; 258 259 devlink_resource_size_params_init(&size_params, max_fn, max_fn, 1, 260 DEVLINK_RESOURCE_UNIT_ENTRY); 261 err = devl_resource_register(devlink, "max_local_SFs", max_fn, MLX5_DL_RES_MAX_LOCAL_SFS, 262 DEVLINK_RESOURCE_ID_PARENT_TOP, &size_params); 263 if (err) 264 return err; 265 266 devlink_resource_size_params_init(&size_params, max_ext_fn, max_ext_fn, 1, 267 DEVLINK_RESOURCE_UNIT_ENTRY); 268 return devl_resource_register(devlink, "max_external_SFs", max_ext_fn, 269 MLX5_DL_RES_MAX_EXTERNAL_SFS, DEVLINK_RESOURCE_ID_PARENT_TOP, 270 &size_params); 271} 272 273int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev) 274{ 275 struct mlx5_sf_hw_table *table; 276 u16 max_ext_fn = 0; 277 u16 ext_base_id = 0; 278 u16 base_id; 279 u16 max_fn; 280 int err; 281 282 if (!mlx5_vhca_event_supported(dev)) 283 return 0; 284 285 max_fn = mlx5_sf_max_functions(dev); 286 287 err = mlx5_esw_sf_max_hpf_functions(dev, &max_ext_fn, &ext_base_id); 288 if (err) 289 return err; 290 291 if (mlx5_sf_hw_table_res_register(dev, max_fn, max_ext_fn)) 292 mlx5_core_dbg(dev, "failed to register max SFs resources"); 293 294 if (!max_fn && !max_ext_fn) 295 return 0; 296 297 table = kzalloc(sizeof(*table), GFP_KERNEL); 298 if (!table) { 299 err = -ENOMEM; 300 goto alloc_err; 301 } 302 303 mutex_init(&table->table_lock); 304 table->dev = dev; 305 dev->priv.sf_hw_table = table; 306 307 base_id = mlx5_sf_start_function_id(dev); 308 err = mlx5_sf_hw_table_hwc_init(&table->hwc[MLX5_SF_HWC_LOCAL], max_fn, base_id); 309 if (err) 310 goto table_err; 311 312 err = mlx5_sf_hw_table_hwc_init(&table->hwc[MLX5_SF_HWC_EXTERNAL], 313 max_ext_fn, ext_base_id); 314 if (err) 315 goto ext_err; 316 317 mlx5_core_dbg(dev, "SF HW table: max sfs = %d, ext sfs = %d\n", max_fn, max_ext_fn); 318 return 0; 319 320ext_err: 321 mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_LOCAL]); 322table_err: 323 mutex_destroy(&table->table_lock); 324 kfree(table); 325alloc_err: 326 mlx5_sf_hw_table_res_unregister(dev); 327 return err; 328} 329 330void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev) 331{ 332 struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; 333 334 if (!table) 335 goto res_unregister; 336 337 mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_EXTERNAL]); 338 mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_LOCAL]); 339 mutex_destroy(&table->table_lock); 340 kfree(table); 341res_unregister: 342 mlx5_sf_hw_table_res_unregister(dev); 343} 344 345static int mlx5_sf_hw_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data) 346{ 347 struct mlx5_sf_hw_table *table = container_of(nb, struct mlx5_sf_hw_table, vhca_nb); 348 const struct mlx5_vhca_state_event *event = data; 349 struct mlx5_sf_hwc_table *hwc; 350 struct mlx5_sf_hw *sf_hw; 351 u16 sw_id; 352 353 if (event->new_vhca_state != MLX5_VHCA_STATE_ALLOCATED) 354 return 0; 355 356 hwc = mlx5_sf_table_fn_to_hwc(table, event->function_id); 357 if (!hwc) 358 return 0; 359 360 sw_id = mlx5_sf_hw_to_sw_id(hwc, event->function_id); 361 sf_hw = &hwc->sfs[sw_id]; 362 363 mutex_lock(&table->table_lock); 364 /* SF driver notified through firmware that SF is finally detached. 365 * Hence recycle the sf hardware id for reuse. 366 */ 367 if (sf_hw->allocated && sf_hw->pending_delete) 368 mlx5_sf_hw_table_hwc_sf_free(table->dev, hwc, sw_id); 369 mutex_unlock(&table->table_lock); 370 return 0; 371} 372 373int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev) 374{ 375 struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; 376 377 if (!table) 378 return 0; 379 380 table->vhca_nb.notifier_call = mlx5_sf_hw_vhca_event; 381 return mlx5_vhca_event_notifier_register(dev, &table->vhca_nb); 382} 383 384void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev) 385{ 386 struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; 387 388 if (!table) 389 return; 390 391 mlx5_vhca_event_notifier_unregister(dev, &table->vhca_nb); 392 /* Dealloc SFs whose firmware event has been missed. */ 393 mlx5_sf_hw_table_dealloc_all(table); 394} 395 396bool mlx5_sf_hw_table_supported(const struct mlx5_core_dev *dev) 397{ 398 return !!dev->priv.sf_hw_table; 399} 400