1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2// Copyright (c) 2018 Mellanox Technologies 3 4#include <linux/hyperv.h> 5#include "mlx5_core.h" 6#include "lib/hv.h" 7#include "lib/hv_vhca.h" 8 9struct mlx5_hv_vhca { 10 struct mlx5_core_dev *dev; 11 struct workqueue_struct *work_queue; 12 struct mlx5_hv_vhca_agent *agents[MLX5_HV_VHCA_AGENT_MAX]; 13 struct mutex agents_lock; /* Protect agents array */ 14}; 15 16struct mlx5_hv_vhca_work { 17 struct work_struct invalidate_work; 18 struct mlx5_hv_vhca *hv_vhca; 19 u64 block_mask; 20}; 21 22struct mlx5_hv_vhca_data_block { 23 u16 sequence; 24 u16 offset; 25 u8 reserved[4]; 26 u64 data[15]; 27}; 28 29struct mlx5_hv_vhca_agent { 30 enum mlx5_hv_vhca_agent_type type; 31 struct mlx5_hv_vhca *hv_vhca; 32 void *priv; 33 u16 seq; 34 void (*control)(struct mlx5_hv_vhca_agent *agent, 35 struct mlx5_hv_vhca_control_block *block); 36 void (*invalidate)(struct mlx5_hv_vhca_agent *agent, 37 u64 block_mask); 38 void (*cleanup)(struct mlx5_hv_vhca_agent *agent); 39}; 40 41struct mlx5_hv_vhca *mlx5_hv_vhca_create(struct mlx5_core_dev *dev) 42{ 43 struct mlx5_hv_vhca *hv_vhca; 44 45 hv_vhca = kzalloc(sizeof(*hv_vhca), GFP_KERNEL); 46 if (!hv_vhca) 47 return ERR_PTR(-ENOMEM); 48 49 hv_vhca->work_queue = create_singlethread_workqueue("mlx5_hv_vhca"); 50 if (!hv_vhca->work_queue) { 51 kfree(hv_vhca); 52 return ERR_PTR(-ENOMEM); 53 } 54 55 hv_vhca->dev = dev; 56 mutex_init(&hv_vhca->agents_lock); 57 58 return hv_vhca; 59} 60 61void mlx5_hv_vhca_destroy(struct mlx5_hv_vhca *hv_vhca) 62{ 63 if (IS_ERR_OR_NULL(hv_vhca)) 64 return; 65 66 destroy_workqueue(hv_vhca->work_queue); 67 kfree(hv_vhca); 68} 69 70static void mlx5_hv_vhca_invalidate_work(struct work_struct *work) 71{ 72 struct mlx5_hv_vhca_work *hwork; 73 struct mlx5_hv_vhca *hv_vhca; 74 int i; 75 76 hwork = container_of(work, struct mlx5_hv_vhca_work, invalidate_work); 77 hv_vhca = hwork->hv_vhca; 78 79 mutex_lock(&hv_vhca->agents_lock); 80 for (i = 0; i < MLX5_HV_VHCA_AGENT_MAX; i++) { 81 struct mlx5_hv_vhca_agent *agent = hv_vhca->agents[i]; 82 83 if (!agent || !agent->invalidate) 84 continue; 85 86 if (!(BIT(agent->type) & hwork->block_mask)) 87 continue; 88 89 agent->invalidate(agent, hwork->block_mask); 90 } 91 mutex_unlock(&hv_vhca->agents_lock); 92 93 kfree(hwork); 94} 95 96void mlx5_hv_vhca_invalidate(void *context, u64 block_mask) 97{ 98 struct mlx5_hv_vhca *hv_vhca = (struct mlx5_hv_vhca *)context; 99 struct mlx5_hv_vhca_work *work; 100 101 work = kzalloc(sizeof(*work), GFP_ATOMIC); 102 if (!work) 103 return; 104 105 INIT_WORK(&work->invalidate_work, mlx5_hv_vhca_invalidate_work); 106 work->hv_vhca = hv_vhca; 107 work->block_mask = block_mask; 108 109 queue_work(hv_vhca->work_queue, &work->invalidate_work); 110} 111 112#define AGENT_MASK(type) (type ? BIT(type - 1) : 0 /* control */) 113 114static void mlx5_hv_vhca_agents_control(struct mlx5_hv_vhca *hv_vhca, 115 struct mlx5_hv_vhca_control_block *block) 116{ 117 int i; 118 119 for (i = 0; i < MLX5_HV_VHCA_AGENT_MAX; i++) { 120 struct mlx5_hv_vhca_agent *agent = hv_vhca->agents[i]; 121 122 if (!agent || !agent->control) 123 continue; 124 125 if (!(AGENT_MASK(agent->type) & block->control)) 126 continue; 127 128 agent->control(agent, block); 129 } 130} 131 132static void mlx5_hv_vhca_capabilities(struct mlx5_hv_vhca *hv_vhca, 133 u32 *capabilities) 134{ 135 int i; 136 137 for (i = 0; i < MLX5_HV_VHCA_AGENT_MAX; i++) { 138 struct mlx5_hv_vhca_agent *agent = hv_vhca->agents[i]; 139 140 if (agent) 141 *capabilities |= AGENT_MASK(agent->type); 142 } 143} 144 145static void 146mlx5_hv_vhca_control_agent_invalidate(struct mlx5_hv_vhca_agent *agent, 147 u64 block_mask) 148{ 149 struct mlx5_hv_vhca *hv_vhca = agent->hv_vhca; 150 struct mlx5_core_dev *dev = hv_vhca->dev; 151 struct mlx5_hv_vhca_control_block *block; 152 u32 capabilities = 0; 153 int err; 154 155 block = kzalloc(sizeof(*block), GFP_KERNEL); 156 if (!block) 157 return; 158 159 err = mlx5_hv_read_config(dev, block, sizeof(*block), 0); 160 if (err) 161 goto free_block; 162 163 mlx5_hv_vhca_capabilities(hv_vhca, &capabilities); 164 165 /* In case no capabilities, send empty block in return */ 166 if (!capabilities) { 167 memset(block, 0, sizeof(*block)); 168 goto write; 169 } 170 171 if (block->capabilities != capabilities) 172 block->capabilities = capabilities; 173 174 if (block->control & ~capabilities) 175 goto free_block; 176 177 mlx5_hv_vhca_agents_control(hv_vhca, block); 178 block->command_ack = block->command; 179 180write: 181 mlx5_hv_write_config(dev, block, sizeof(*block), 0); 182 183free_block: 184 kfree(block); 185} 186 187static struct mlx5_hv_vhca_agent * 188mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca) 189{ 190 return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL, 191 NULL, 192 mlx5_hv_vhca_control_agent_invalidate, 193 NULL, NULL); 194} 195 196static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent) 197{ 198 mlx5_hv_vhca_agent_destroy(agent); 199} 200 201int mlx5_hv_vhca_init(struct mlx5_hv_vhca *hv_vhca) 202{ 203 struct mlx5_hv_vhca_agent *agent; 204 int err; 205 206 if (IS_ERR_OR_NULL(hv_vhca)) 207 return IS_ERR_OR_NULL(hv_vhca); 208 209 err = mlx5_hv_register_invalidate(hv_vhca->dev, hv_vhca, 210 mlx5_hv_vhca_invalidate); 211 if (err) 212 return err; 213 214 agent = mlx5_hv_vhca_control_agent_create(hv_vhca); 215 if (IS_ERR_OR_NULL(agent)) { 216 mlx5_hv_unregister_invalidate(hv_vhca->dev); 217 return IS_ERR_OR_NULL(agent); 218 } 219 220 hv_vhca->agents[MLX5_HV_VHCA_AGENT_CONTROL] = agent; 221 222 return 0; 223} 224 225void mlx5_hv_vhca_cleanup(struct mlx5_hv_vhca *hv_vhca) 226{ 227 struct mlx5_hv_vhca_agent *agent; 228 int i; 229 230 if (IS_ERR_OR_NULL(hv_vhca)) 231 return; 232 233 agent = hv_vhca->agents[MLX5_HV_VHCA_AGENT_CONTROL]; 234 if (agent) 235 mlx5_hv_vhca_control_agent_destroy(agent); 236 237 mutex_lock(&hv_vhca->agents_lock); 238 for (i = 0; i < MLX5_HV_VHCA_AGENT_MAX; i++) 239 WARN_ON(hv_vhca->agents[i]); 240 241 mutex_unlock(&hv_vhca->agents_lock); 242 243 mlx5_hv_unregister_invalidate(hv_vhca->dev); 244} 245 246static void mlx5_hv_vhca_agents_update(struct mlx5_hv_vhca *hv_vhca) 247{ 248 mlx5_hv_vhca_invalidate(hv_vhca, BIT(MLX5_HV_VHCA_AGENT_CONTROL)); 249} 250 251struct mlx5_hv_vhca_agent * 252mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, 253 enum mlx5_hv_vhca_agent_type type, 254 void (*control)(struct mlx5_hv_vhca_agent*, 255 struct mlx5_hv_vhca_control_block *block), 256 void (*invalidate)(struct mlx5_hv_vhca_agent*, 257 u64 block_mask), 258 void (*cleaup)(struct mlx5_hv_vhca_agent *agent), 259 void *priv) 260{ 261 struct mlx5_hv_vhca_agent *agent; 262 263 if (IS_ERR_OR_NULL(hv_vhca)) 264 return ERR_PTR(-ENOMEM); 265 266 if (type >= MLX5_HV_VHCA_AGENT_MAX) 267 return ERR_PTR(-EINVAL); 268 269 mutex_lock(&hv_vhca->agents_lock); 270 if (hv_vhca->agents[type]) { 271 mutex_unlock(&hv_vhca->agents_lock); 272 return ERR_PTR(-EINVAL); 273 } 274 mutex_unlock(&hv_vhca->agents_lock); 275 276 agent = kzalloc(sizeof(*agent), GFP_KERNEL); 277 if (!agent) 278 return ERR_PTR(-ENOMEM); 279 280 agent->type = type; 281 agent->hv_vhca = hv_vhca; 282 agent->priv = priv; 283 agent->control = control; 284 agent->invalidate = invalidate; 285 agent->cleanup = cleaup; 286 287 mutex_lock(&hv_vhca->agents_lock); 288 hv_vhca->agents[type] = agent; 289 mutex_unlock(&hv_vhca->agents_lock); 290 291 mlx5_hv_vhca_agents_update(hv_vhca); 292 293 return agent; 294} 295 296void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent) 297{ 298 struct mlx5_hv_vhca *hv_vhca = agent->hv_vhca; 299 300 mutex_lock(&hv_vhca->agents_lock); 301 302 if (WARN_ON(agent != hv_vhca->agents[agent->type])) { 303 mutex_unlock(&hv_vhca->agents_lock); 304 return; 305 } 306 307 hv_vhca->agents[agent->type] = NULL; 308 mutex_unlock(&hv_vhca->agents_lock); 309 310 if (agent->cleanup) 311 agent->cleanup(agent); 312 313 kfree(agent); 314 315 mlx5_hv_vhca_agents_update(hv_vhca); 316} 317 318static int mlx5_hv_vhca_data_block_prepare(struct mlx5_hv_vhca_agent *agent, 319 struct mlx5_hv_vhca_data_block *data_block, 320 void *src, int len, int *offset) 321{ 322 int bytes = min_t(int, (int)sizeof(data_block->data), len); 323 324 data_block->sequence = agent->seq; 325 data_block->offset = (*offset)++; 326 memcpy(data_block->data, src, bytes); 327 328 return bytes; 329} 330 331static void mlx5_hv_vhca_agent_seq_update(struct mlx5_hv_vhca_agent *agent) 332{ 333 agent->seq++; 334} 335 336int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent, 337 void *buf, int len) 338{ 339 int offset = agent->type * HV_CONFIG_BLOCK_SIZE_MAX; 340 int block_offset = 0; 341 int total = 0; 342 int err; 343 344 while (len) { 345 struct mlx5_hv_vhca_data_block data_block = {0}; 346 int bytes; 347 348 bytes = mlx5_hv_vhca_data_block_prepare(agent, &data_block, 349 buf + total, 350 len, &block_offset); 351 if (!bytes) 352 return -ENOMEM; 353 354 err = mlx5_hv_write_config(agent->hv_vhca->dev, &data_block, 355 sizeof(data_block), offset); 356 if (err) 357 return err; 358 359 total += bytes; 360 len -= bytes; 361 } 362 363 mlx5_hv_vhca_agent_seq_update(agent); 364 365 return 0; 366} 367 368void *mlx5_hv_vhca_agent_priv(struct mlx5_hv_vhca_agent *agent) 369{ 370 return agent->priv; 371} 372