Linux-libre 5.4.39-gnu
[librecmc/linux-libre.git] / drivers / net / ethernet / mellanox / mlx5 / core / lib / hv_vhca.c
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
41 struct mlx5_hv_vhca *mlx5_hv_vhca_create(struct mlx5_core_dev *dev)
42 {
43         struct mlx5_hv_vhca *hv_vhca = NULL;
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
61 void 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
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
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(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
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
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
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(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
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 *
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);
194 }
195
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
201 int 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
225 void 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
246 static 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
251 struct mlx5_hv_vhca_agent *
252 mlx5_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
296 void 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
318 static 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
331 static void mlx5_hv_vhca_agent_seq_update(struct mlx5_hv_vhca_agent *agent)
332 {
333         agent->seq++;
334 }
335
336 int 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
368 void *mlx5_hv_vhca_agent_priv(struct mlx5_hv_vhca_agent *agent)
369 {
370         return agent->priv;
371 }