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
9 struct 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
16 struct mlx5_hv_vhca_work {
17 struct work_struct invalidate_work;
18 struct mlx5_hv_vhca *hv_vhca;
19 u64 block_mask;
20 };
21
22 struct mlx5_hv_vhca_data_block {
23 u16 sequence;
24 u16 offset;
25 u8 reserved[4];
26 u64 data[15];
27 };
28
29 struct 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
mlx5_hv_vhca_create(struct mlx5_core_dev * dev)41 struct 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_obj(*hv_vhca);
46 if (!hv_vhca)
47 return NULL;
48
49 hv_vhca->work_queue = create_singlethread_workqueue("mlx5_hv_vhca");
50 if (!hv_vhca->work_queue) {
51 kfree(hv_vhca);
52 return NULL;
53 }
54
55 hv_vhca->dev = dev;
56 mutex_init(&hv_vhca->agents_lock);
57
58 return hv_vhca;
59 }
60
mlx5_hv_vhca_destroy(struct mlx5_hv_vhca * hv_vhca)61 void mlx5_hv_vhca_destroy(struct mlx5_hv_vhca *hv_vhca)
62 {
63 if (!hv_vhca)
64 return;
65
66 destroy_workqueue(hv_vhca->work_queue);
67 kfree(hv_vhca);
68 }
69
mlx5_hv_vhca_invalidate_work(struct work_struct * work)70 static 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
mlx5_hv_vhca_invalidate(void * context,u64 block_mask)96 void 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_obj(*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
mlx5_hv_vhca_agents_control(struct mlx5_hv_vhca * hv_vhca,struct mlx5_hv_vhca_control_block * block)114 static 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
mlx5_hv_vhca_capabilities(struct mlx5_hv_vhca * hv_vhca,u32 * capabilities)132 static 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
145 static void
mlx5_hv_vhca_control_agent_invalidate(struct mlx5_hv_vhca_agent * agent,u64 block_mask)146 mlx5_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_obj(*block);
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
180 write:
181 mlx5_hv_write_config(dev, block, sizeof(*block), 0);
182
183 free_block:
184 kfree(block);
185 }
186
187 static struct mlx5_hv_vhca_agent *
mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca * hv_vhca)188 mlx5_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, NULL);
194 }
195
mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent * agent)196 static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent)
197 {
198 mlx5_hv_vhca_agent_destroy(agent);
199 }
200
mlx5_hv_vhca_init(struct mlx5_hv_vhca * hv_vhca)201 void mlx5_hv_vhca_init(struct mlx5_hv_vhca *hv_vhca)
202 {
203 struct mlx5_hv_vhca_agent *agent;
204 int err;
205
206 if (!hv_vhca)
207 return;
208
209 err = mlx5_hv_register_invalidate(hv_vhca->dev, hv_vhca,
210 mlx5_hv_vhca_invalidate);
211 if (err)
212 return;
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;
218 }
219
220 hv_vhca->agents[MLX5_HV_VHCA_AGENT_CONTROL] = agent;
221 }
222
mlx5_hv_vhca_cleanup(struct mlx5_hv_vhca * hv_vhca)223 void mlx5_hv_vhca_cleanup(struct mlx5_hv_vhca *hv_vhca)
224 {
225 struct mlx5_hv_vhca_agent *agent;
226 int i;
227
228 if (!hv_vhca)
229 return;
230
231 agent = hv_vhca->agents[MLX5_HV_VHCA_AGENT_CONTROL];
232 if (agent)
233 mlx5_hv_vhca_control_agent_destroy(agent);
234
235 mutex_lock(&hv_vhca->agents_lock);
236 for (i = 0; i < MLX5_HV_VHCA_AGENT_MAX; i++)
237 WARN_ON(hv_vhca->agents[i]);
238
239 mutex_unlock(&hv_vhca->agents_lock);
240
241 mlx5_hv_unregister_invalidate(hv_vhca->dev);
242 }
243
mlx5_hv_vhca_agents_update(struct mlx5_hv_vhca * hv_vhca)244 static void mlx5_hv_vhca_agents_update(struct mlx5_hv_vhca *hv_vhca)
245 {
246 mlx5_hv_vhca_invalidate(hv_vhca, BIT(MLX5_HV_VHCA_AGENT_CONTROL));
247 }
248
249 struct mlx5_hv_vhca_agent *
mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca * hv_vhca,enum mlx5_hv_vhca_agent_type type,void (* control)(struct mlx5_hv_vhca_agent *,struct mlx5_hv_vhca_control_block * block),void (* invalidate)(struct mlx5_hv_vhca_agent *,u64 block_mask),void (* cleaup)(struct mlx5_hv_vhca_agent * agent),void * priv,struct mlx5_hv_vhca_agent ** ctx_update)250 mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
251 enum mlx5_hv_vhca_agent_type type,
252 void (*control)(struct mlx5_hv_vhca_agent*,
253 struct mlx5_hv_vhca_control_block *block),
254 void (*invalidate)(struct mlx5_hv_vhca_agent*,
255 u64 block_mask),
256 void (*cleaup)(struct mlx5_hv_vhca_agent *agent),
257 void *priv,
258 struct mlx5_hv_vhca_agent **ctx_update)
259 {
260 struct mlx5_hv_vhca_agent *agent;
261
262 if (!hv_vhca)
263 return ERR_PTR(-ENOMEM);
264
265 if (type >= MLX5_HV_VHCA_AGENT_MAX)
266 return ERR_PTR(-EINVAL);
267
268 mutex_lock(&hv_vhca->agents_lock);
269 if (hv_vhca->agents[type]) {
270 mutex_unlock(&hv_vhca->agents_lock);
271 return ERR_PTR(-EINVAL);
272 }
273 mutex_unlock(&hv_vhca->agents_lock);
274
275 agent = kzalloc_obj(*agent);
276 if (!agent)
277 return ERR_PTR(-ENOMEM);
278
279 agent->type = type;
280 agent->hv_vhca = hv_vhca;
281 agent->priv = priv;
282 agent->control = control;
283 agent->invalidate = invalidate;
284 agent->cleanup = cleaup;
285
286 if (ctx_update)
287 WRITE_ONCE(*ctx_update, agent);
288
289 mutex_lock(&hv_vhca->agents_lock);
290 hv_vhca->agents[type] = agent;
291 mutex_unlock(&hv_vhca->agents_lock);
292
293 mlx5_hv_vhca_agents_update(hv_vhca);
294
295 return agent;
296 }
297
mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent * agent)298 void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent)
299 {
300 struct mlx5_hv_vhca *hv_vhca = agent->hv_vhca;
301
302 mutex_lock(&hv_vhca->agents_lock);
303
304 if (WARN_ON(agent != hv_vhca->agents[agent->type])) {
305 mutex_unlock(&hv_vhca->agents_lock);
306 return;
307 }
308
309 hv_vhca->agents[agent->type] = NULL;
310 mutex_unlock(&hv_vhca->agents_lock);
311
312 if (agent->cleanup)
313 agent->cleanup(agent);
314
315 kfree(agent);
316
317 mlx5_hv_vhca_agents_update(hv_vhca);
318 }
319
mlx5_hv_vhca_data_block_prepare(struct mlx5_hv_vhca_agent * agent,struct mlx5_hv_vhca_data_block * data_block,void * src,int len,int * offset)320 static int mlx5_hv_vhca_data_block_prepare(struct mlx5_hv_vhca_agent *agent,
321 struct mlx5_hv_vhca_data_block *data_block,
322 void *src, int len, int *offset)
323 {
324 int bytes = min_t(int, (int)sizeof(data_block->data), len);
325
326 data_block->sequence = agent->seq;
327 data_block->offset = (*offset)++;
328 memcpy(data_block->data, src, bytes);
329
330 return bytes;
331 }
332
mlx5_hv_vhca_agent_seq_update(struct mlx5_hv_vhca_agent * agent)333 static void mlx5_hv_vhca_agent_seq_update(struct mlx5_hv_vhca_agent *agent)
334 {
335 agent->seq++;
336 }
337
mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent * agent,void * buf,int len)338 int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent,
339 void *buf, int len)
340 {
341 int offset = agent->type * HV_CONFIG_BLOCK_SIZE_MAX;
342 int block_offset = 0;
343 int total = 0;
344 int err;
345
346 while (len) {
347 struct mlx5_hv_vhca_data_block data_block = {0};
348 int bytes;
349
350 bytes = mlx5_hv_vhca_data_block_prepare(agent, &data_block,
351 buf + total,
352 len, &block_offset);
353 if (!bytes)
354 return -ENOMEM;
355
356 err = mlx5_hv_write_config(agent->hv_vhca->dev, &data_block,
357 sizeof(data_block), offset);
358 if (err)
359 return err;
360
361 total += bytes;
362 len -= bytes;
363 }
364
365 mlx5_hv_vhca_agent_seq_update(agent);
366
367 return 0;
368 }
369
mlx5_hv_vhca_agent_priv(struct mlx5_hv_vhca_agent * agent)370 void *mlx5_hv_vhca_agent_priv(struct mlx5_hv_vhca_agent *agent)
371 {
372 return agent->priv;
373 }
374