xref: /linux/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
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