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