Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright 2019 Mellanox Technologies, Ltd
3 : : */
4 : : #include <stdlib.h>
5 : :
6 : : #include <rte_malloc.h>
7 : : #include <rte_errno.h>
8 : : #include <rte_common.h>
9 : : #include <rte_sched_common.h>
10 : :
11 : : #include <mlx5_prm.h>
12 : : #include <mlx5_common.h>
13 : :
14 : : #include "mlx5_vdpa_utils.h"
15 : : #include "mlx5_vdpa.h"
16 : :
17 : : void
18 : 0 : mlx5_vdpa_mem_dereg(struct mlx5_vdpa_priv *priv)
19 : : {
20 : 0 : struct mlx5_vdpa_query_mr *mrs =
21 : : (struct mlx5_vdpa_query_mr *)priv->mrs;
22 : : struct mlx5_vdpa_query_mr *entry;
23 : : int i;
24 : :
25 [ # # ]: 0 : if (priv->mrs) {
26 [ # # ]: 0 : for (i = priv->num_mrs - 1; i >= 0; i--) {
27 : 0 : entry = &mrs[i];
28 [ # # ]: 0 : if (entry->is_indirect) {
29 [ # # ]: 0 : if (entry->mkey)
30 : 0 : claim_zero(
31 : : mlx5_devx_cmd_destroy(entry->mkey));
32 : : } else {
33 [ # # ]: 0 : if (entry->mr)
34 : 0 : claim_zero(
35 : : mlx5_glue->dereg_mr(entry->mr));
36 : : }
37 : : }
38 : 0 : rte_free(priv->mrs);
39 : 0 : priv->mrs = NULL;
40 : 0 : priv->num_mrs = 0;
41 : : }
42 [ # # ]: 0 : if (priv->vmem_info.vmem) {
43 : 0 : free(priv->vmem_info.vmem);
44 : 0 : priv->vmem_info.vmem = NULL;
45 : : }
46 : 0 : priv->gpa_mkey_index = 0;
47 : 0 : }
48 : :
49 : : static int
50 : 0 : mlx5_vdpa_regions_addr_cmp(const void *a, const void *b)
51 : : {
52 : : const struct rte_vhost_mem_region *region_a = a;
53 : : const struct rte_vhost_mem_region *region_b = b;
54 : :
55 [ # # ]: 0 : if (region_a->guest_phys_addr < region_b->guest_phys_addr)
56 : : return -1;
57 [ # # ]: 0 : if (region_a->guest_phys_addr > region_b->guest_phys_addr)
58 : 0 : return 1;
59 : : return 0;
60 : : }
61 : :
62 : : #define KLM_NUM_MAX_ALIGN(sz) (RTE_ALIGN_CEIL(sz, MLX5_MAX_KLM_BYTE_COUNT) / \
63 : : MLX5_MAX_KLM_BYTE_COUNT)
64 : :
65 : : /*
66 : : * Allocate and sort the region list and choose indirect mkey mode:
67 : : * 1. Calculate GCD, guest memory size and indirect mkey entries num per mode.
68 : : * 2. Align GCD to the maximum allowed size(2G) and to be power of 2.
69 : : * 2. Decide the indirect mkey mode according to the next rules:
70 : : * a. If both KLM_FBS entries number and KLM entries number are bigger
71 : : * than the maximum allowed(MLX5_DEVX_MAX_KLM_ENTRIES) - error.
72 : : * b. KLM mode if KLM_FBS entries number is bigger than the maximum
73 : : * allowed(MLX5_DEVX_MAX_KLM_ENTRIES).
74 : : * c. KLM mode if GCD is smaller than the minimum allowed(4K).
75 : : * d. KLM mode if the total size of KLM entries is in one cache line
76 : : * and the total size of KLM_FBS entries is not in one cache line.
77 : : * e. Otherwise, KLM_FBS mode.
78 : : */
79 : : static struct rte_vhost_memory *
80 : 0 : mlx5_vdpa_vhost_mem_regions_prepare(int vid, uint8_t *mode, uint64_t *mem_size,
81 : : uint64_t *gcd, uint32_t *entries_num)
82 : : {
83 : : struct rte_vhost_memory *mem;
84 : : uint64_t size;
85 : : uint64_t klm_entries_num = 0;
86 : : uint64_t klm_fbs_entries_num;
87 : : uint32_t i;
88 : 0 : int ret = rte_vhost_get_mem_table(vid, &mem);
89 : :
90 [ # # ]: 0 : if (ret < 0) {
91 : 0 : DRV_LOG(ERR, "Failed to get VM memory layout vid =%d.", vid);
92 : 0 : rte_errno = EINVAL;
93 : 0 : return NULL;
94 : : }
95 : 0 : qsort(mem->regions, mem->nregions, sizeof(mem->regions[0]),
96 : : mlx5_vdpa_regions_addr_cmp);
97 : 0 : *mem_size = (mem->regions[(mem->nregions - 1)].guest_phys_addr) +
98 : 0 : (mem->regions[(mem->nregions - 1)].size) -
99 : 0 : (mem->regions[0].guest_phys_addr);
100 : 0 : *gcd = 0;
101 [ # # ]: 0 : for (i = 0; i < mem->nregions; ++i) {
102 : 0 : DRV_LOG(INFO, "Region %u: HVA 0x%" PRIx64 ", GPA 0x%" PRIx64
103 : : ", size 0x%" PRIx64 ".", i,
104 : : mem->regions[i].host_user_addr,
105 : : mem->regions[i].guest_phys_addr, mem->regions[i].size);
106 [ # # ]: 0 : if (i > 0) {
107 : : /* Hole handle. */
108 : 0 : size = mem->regions[i].guest_phys_addr -
109 : 0 : (mem->regions[i - 1].guest_phys_addr +
110 : 0 : mem->regions[i - 1].size);
111 [ # # ]: 0 : *gcd = rte_get_gcd64(*gcd, size);
112 : 0 : klm_entries_num += KLM_NUM_MAX_ALIGN(size);
113 : : }
114 : 0 : size = mem->regions[i].size;
115 [ # # ]: 0 : *gcd = rte_get_gcd64(*gcd, size);
116 : 0 : klm_entries_num += KLM_NUM_MAX_ALIGN(size);
117 : : }
118 [ # # ]: 0 : if (*gcd > MLX5_MAX_KLM_BYTE_COUNT)
119 : 0 : *gcd = rte_get_gcd64(*gcd, MLX5_MAX_KLM_BYTE_COUNT);
120 [ # # # # ]: 0 : if (!RTE_IS_POWER_OF_2(*gcd)) {
121 : : uint64_t candidate_gcd = rte_align64prevpow2(*gcd);
122 : :
123 [ # # # # ]: 0 : while (candidate_gcd > 1 && (*gcd % candidate_gcd))
124 : 0 : candidate_gcd /= 2;
125 : 0 : DRV_LOG(DEBUG, "GCD 0x%" PRIx64 " is not power of 2. Adjusted "
126 : : "GCD is 0x%" PRIx64 ".", *gcd, candidate_gcd);
127 : 0 : *gcd = candidate_gcd;
128 : : }
129 : 0 : klm_fbs_entries_num = *mem_size / *gcd;
130 [ # # # # ]: 0 : if (*gcd < MLX5_MIN_KLM_FIXED_BUFFER_SIZE || klm_fbs_entries_num >
131 : 0 : MLX5_DEVX_MAX_KLM_ENTRIES ||
132 [ # # ]: 0 : ((klm_entries_num * sizeof(struct mlx5_klm)) <=
133 [ # # ]: 0 : RTE_CACHE_LINE_SIZE && (klm_fbs_entries_num *
134 : : sizeof(struct mlx5_klm)) >
135 : : RTE_CACHE_LINE_SIZE)) {
136 : 0 : *mode = MLX5_MKC_ACCESS_MODE_KLM;
137 : 0 : *entries_num = klm_entries_num;
138 : 0 : DRV_LOG(INFO, "Indirect mkey mode is KLM.");
139 : : } else {
140 : 0 : *mode = MLX5_MKC_ACCESS_MODE_KLM_FBS;
141 : 0 : *entries_num = klm_fbs_entries_num;
142 : 0 : DRV_LOG(INFO, "Indirect mkey mode is KLM Fixed Buffer Size.");
143 : : }
144 : 0 : DRV_LOG(DEBUG, "Memory registration information: nregions = %u, "
145 : : "mem_size = 0x%" PRIx64 ", GCD = 0x%" PRIx64
146 : : ", klm_fbs_entries_num = 0x%" PRIx64 ", klm_entries_num = 0x%"
147 : : PRIx64 ".", mem->nregions, *mem_size, *gcd, klm_fbs_entries_num,
148 : : klm_entries_num);
149 [ # # ]: 0 : if (*entries_num > MLX5_DEVX_MAX_KLM_ENTRIES) {
150 : 0 : DRV_LOG(ERR, "Failed to prepare memory of vid %d - memory is "
151 : : "too fragmented.", vid);
152 : 0 : free(mem);
153 : 0 : return NULL;
154 : : }
155 : 0 : return mem;
156 : : }
157 : :
158 : : static int
159 : : mlx5_vdpa_mem_cmp(struct rte_vhost_memory *mem1, struct rte_vhost_memory *mem2)
160 : : {
161 : : uint32_t i;
162 : :
163 [ # # ]: 0 : if (mem1->nregions != mem2->nregions)
164 : : return -1;
165 [ # # ]: 0 : for (i = 0; i < mem1->nregions; i++) {
166 : 0 : if (mem1->regions[i].guest_phys_addr !=
167 [ # # ]: 0 : mem2->regions[i].guest_phys_addr)
168 : : return -1;
169 [ # # ]: 0 : if (mem1->regions[i].size != mem2->regions[i].size)
170 : : return -1;
171 : : }
172 : : return 0;
173 : : }
174 : :
175 : : #define KLM_SIZE_MAX_ALIGN(sz) ((sz) > MLX5_MAX_KLM_BYTE_COUNT ? \
176 : : MLX5_MAX_KLM_BYTE_COUNT : (sz))
177 : :
178 : : static int
179 : 0 : mlx5_vdpa_create_indirect_mkey(struct mlx5_vdpa_priv *priv)
180 : 0 : {
181 : : struct mlx5_devx_mkey_attr mkey_attr;
182 : 0 : struct mlx5_hca_attr *hca_attr = &priv->cdev->config.hca_attr;
183 : 0 : struct mlx5_vdpa_query_mr *mrs =
184 : : (struct mlx5_vdpa_query_mr *)priv->mrs;
185 : : struct mlx5_vdpa_query_mr *entry;
186 : : struct rte_vhost_mem_region *reg;
187 : 0 : uint8_t mode = priv->vmem_info.mode;
188 : 0 : uint32_t entries_num = priv->vmem_info.entries_num;
189 : 0 : struct rte_vhost_memory *mem = priv->vmem_info.vmem;
190 : 0 : struct mlx5_klm klm_array[entries_num];
191 : 0 : uint64_t gcd = priv->vmem_info.gcd;
192 : 0 : int ret = -rte_errno;
193 : : uint64_t klm_size;
194 : : int klm_index = 0;
195 : : uint64_t k;
196 : : uint32_t i;
197 : :
198 : : /* If it is the last entry, create indirect mkey. */
199 [ # # ]: 0 : for (i = 0; i < mem->nregions; i++) {
200 : 0 : entry = &mrs[i];
201 : : reg = &mem->regions[i];
202 [ # # ]: 0 : if (i > 0) {
203 : : uint64_t sadd;
204 : 0 : uint64_t empty_region_sz = reg->guest_phys_addr -
205 : 0 : (mem->regions[i - 1].guest_phys_addr +
206 : 0 : mem->regions[i - 1].size);
207 : :
208 [ # # ]: 0 : if (empty_region_sz > 0) {
209 : : sadd = mem->regions[i - 1].guest_phys_addr +
210 : : mem->regions[i - 1].size;
211 : : klm_size = mode == MLX5_MKC_ACCESS_MODE_KLM ?
212 [ # # ]: 0 : KLM_SIZE_MAX_ALIGN(empty_region_sz) : gcd;
213 [ # # ]: 0 : for (k = 0; k < empty_region_sz;
214 : : k += klm_size) {
215 : 0 : klm_array[klm_index].byte_count =
216 [ # # ]: 0 : k + klm_size > empty_region_sz ?
217 : 0 : empty_region_sz - k : klm_size;
218 : 0 : klm_array[klm_index].mkey =
219 : 0 : priv->null_mr->lkey;
220 : 0 : klm_array[klm_index].address = sadd + k;
221 : 0 : klm_index++;
222 : : }
223 : : }
224 : : }
225 : : klm_size = mode == MLX5_MKC_ACCESS_MODE_KLM ?
226 [ # # ]: 0 : KLM_SIZE_MAX_ALIGN(reg->size) : gcd;
227 [ # # ]: 0 : for (k = 0; k < reg->size; k += klm_size) {
228 [ # # ]: 0 : klm_array[klm_index].byte_count = k + klm_size >
229 : 0 : reg->size ? reg->size - k : klm_size;
230 : 0 : klm_array[klm_index].mkey = entry->mr->lkey;
231 : 0 : klm_array[klm_index].address = reg->guest_phys_addr + k;
232 : 0 : klm_index++;
233 : : }
234 : : }
235 : : memset(&mkey_attr, 0, sizeof(mkey_attr));
236 : 0 : mkey_attr.addr = (uintptr_t)(mem->regions[0].guest_phys_addr);
237 : 0 : mkey_attr.size = priv->vmem_info.size;
238 : 0 : mkey_attr.pd = priv->cdev->pdn;
239 : : mkey_attr.umem_id = 0;
240 : : /* Must be zero for KLM mode. */
241 : 0 : mkey_attr.log_entity_size = mode == MLX5_MKC_ACCESS_MODE_KLM_FBS ?
242 [ # # ]: 0 : rte_log2_u64(gcd) : 0;
243 : : mkey_attr.pg_access = 0;
244 : 0 : mkey_attr.klm_array = klm_array;
245 : 0 : mkey_attr.klm_num = klm_index;
246 : : /* If only relaxed order is allowed. */
247 [ # # ]: 0 : if (hca_attr->mkc_order_write_after_write_ro_only)
248 : 0 : mlx5_devx_mkey_attr_set_ordering(&mkey_attr, hca_attr);
249 : 0 : entry = &mrs[mem->nregions];
250 : 0 : entry->mkey = mlx5_devx_cmd_mkey_create(priv->cdev->ctx, &mkey_attr);
251 [ # # ]: 0 : if (!entry->mkey) {
252 : 0 : DRV_LOG(ERR, "Failed to create indirect Mkey.");
253 : 0 : rte_errno = -ret;
254 : 0 : return ret;
255 : : }
256 : 0 : entry->is_indirect = 1;
257 : 0 : priv->gpa_mkey_index = entry->mkey->id;
258 : 0 : return 0;
259 : : }
260 : :
261 : : /*
262 : : * The target here is to group all the physical memory regions of the
263 : : * virtio device in one indirect mkey.
264 : : * For KLM Fixed Buffer Size mode (HW find the translation entry in one
265 : : * read according to the guest phisical address):
266 : : * All the sub-direct mkeys of it must be in the same size, hence, each
267 : : * one of them should be in the GCD size of all the virtio memory
268 : : * regions and the holes between them.
269 : : * For KLM mode (each entry may be in different size so HW must iterate
270 : : * the entries):
271 : : * Each virtio memory region and each hole between them have one entry,
272 : : * just need to cover the maximum allowed size(2G) by splitting entries
273 : : * which their associated memory regions are bigger than 2G.
274 : : * It means that each virtio memory region may be mapped to more than
275 : : * one direct mkey in the 2 modes.
276 : : * All the holes of invalid memory between the virtio memory regions
277 : : * will be mapped to the null memory region for security.
278 : : */
279 : : int
280 : 0 : mlx5_vdpa_mem_register(struct mlx5_vdpa_priv *priv)
281 : : {
282 : : void *mrs;
283 : 0 : uint8_t mode = 0;
284 : 0 : int ret = -rte_errno;
285 : : uint32_t i, thrd_idx, data[1];
286 : 0 : RTE_ATOMIC(uint32_t) remaining_cnt = 0;
287 : 0 : RTE_ATOMIC(uint32_t) err_cnt = 0;
288 : : uint32_t task_num = 0;
289 : 0 : struct rte_vhost_memory *mem = mlx5_vdpa_vhost_mem_regions_prepare
290 : : (priv->vid, &mode, &priv->vmem_info.size,
291 : : &priv->vmem_info.gcd, &priv->vmem_info.entries_num);
292 : :
293 [ # # ]: 0 : if (!mem)
294 : 0 : return -rte_errno;
295 [ # # ]: 0 : if (priv->vmem_info.vmem != NULL) {
296 [ # # ]: 0 : if (mlx5_vdpa_mem_cmp(mem, priv->vmem_info.vmem) == 0) {
297 : : /* VM memory not changed, reuse resources. */
298 : 0 : free(mem);
299 : 0 : return 0;
300 : : }
301 : 0 : mlx5_vdpa_mem_dereg(priv);
302 : : }
303 : 0 : priv->vmem_info.vmem = mem;
304 : 0 : priv->vmem_info.mode = mode;
305 : 0 : priv->num_mrs = mem->nregions;
306 [ # # ]: 0 : if (!priv->num_mrs || priv->num_mrs >= MLX5_VDPA_MAX_MRS) {
307 : 0 : DRV_LOG(ERR,
308 : : "Invalid number of memory regions.");
309 : 0 : goto error;
310 : : }
311 : : /* The last one is indirect mkey entry. */
312 : 0 : priv->num_mrs++;
313 : 0 : mrs = rte_zmalloc("mlx5 vDPA memory regions",
314 : 0 : sizeof(struct mlx5_vdpa_query_mr) * priv->num_mrs, 0);
315 : 0 : priv->mrs = mrs;
316 [ # # ]: 0 : if (!priv->mrs) {
317 : 0 : DRV_LOG(ERR, "Failed to allocate private memory regions.");
318 : 0 : goto error;
319 : : }
320 [ # # ]: 0 : if (priv->use_c_thread) {
321 : 0 : uint32_t main_task_idx[mem->nregions];
322 : :
323 [ # # ]: 0 : for (i = 0; i < mem->nregions; i++) {
324 : 0 : thrd_idx = i % (conf_thread_mng.max_thrds + 1);
325 [ # # ]: 0 : if (!thrd_idx) {
326 : 0 : main_task_idx[task_num] = i;
327 : 0 : task_num++;
328 : 0 : continue;
329 : : }
330 : 0 : thrd_idx = priv->last_c_thrd_idx + 1;
331 [ # # ]: 0 : if (thrd_idx >= conf_thread_mng.max_thrds)
332 : : thrd_idx = 0;
333 : 0 : priv->last_c_thrd_idx = thrd_idx;
334 : 0 : data[0] = i;
335 [ # # ]: 0 : if (mlx5_vdpa_task_add(priv, thrd_idx,
336 : : MLX5_VDPA_TASK_REG_MR,
337 : : &remaining_cnt, &err_cnt,
338 : : (void **)&data, 1)) {
339 : 0 : DRV_LOG(ERR,
340 : : "Fail to add task mem region (%d)", i);
341 : 0 : main_task_idx[task_num] = i;
342 : 0 : task_num++;
343 : : }
344 : : }
345 [ # # ]: 0 : for (i = 0; i < task_num; i++) {
346 : 0 : ret = mlx5_vdpa_register_mr(priv,
347 : : main_task_idx[i]);
348 [ # # ]: 0 : if (ret) {
349 : 0 : DRV_LOG(ERR,
350 : : "Failed to register mem region %d.", i);
351 : 0 : goto error;
352 : : }
353 : : }
354 [ # # ]: 0 : if (mlx5_vdpa_c_thread_wait_bulk_tasks_done(&remaining_cnt,
355 : : &err_cnt, 100)) {
356 : 0 : DRV_LOG(ERR,
357 : : "Failed to wait register mem region tasks ready.");
358 : 0 : goto error;
359 : : }
360 : : } else {
361 [ # # ]: 0 : for (i = 0; i < mem->nregions; i++) {
362 : 0 : ret = mlx5_vdpa_register_mr(priv, i);
363 [ # # ]: 0 : if (ret) {
364 : 0 : DRV_LOG(ERR,
365 : : "Failed to register mem region %d.", i);
366 : 0 : goto error;
367 : : }
368 : : }
369 : : }
370 : 0 : ret = mlx5_vdpa_create_indirect_mkey(priv);
371 [ # # ]: 0 : if (ret) {
372 : 0 : DRV_LOG(ERR, "Failed to create indirect mkey .");
373 : 0 : goto error;
374 : : }
375 : : return 0;
376 : 0 : error:
377 : 0 : mlx5_vdpa_mem_dereg(priv);
378 : 0 : rte_errno = -ret;
379 : 0 : return ret;
380 : : }
381 : :
382 : : int
383 : 0 : mlx5_vdpa_register_mr(struct mlx5_vdpa_priv *priv, uint32_t idx)
384 : : {
385 : 0 : struct rte_vhost_memory *mem = priv->vmem_info.vmem;
386 : 0 : struct mlx5_vdpa_query_mr *mrs =
387 : : (struct mlx5_vdpa_query_mr *)priv->mrs;
388 : : struct mlx5_vdpa_query_mr *entry;
389 : : struct rte_vhost_mem_region *reg;
390 : : int ret;
391 : :
392 : : reg = &mem->regions[idx];
393 : 0 : entry = &mrs[idx];
394 : 0 : entry->mr = mlx5_glue->reg_mr_iova
395 : 0 : (priv->cdev->pd,
396 : 0 : (void *)(uintptr_t)(reg->host_user_addr),
397 : : reg->size, reg->guest_phys_addr,
398 : : IBV_ACCESS_LOCAL_WRITE);
399 [ # # ]: 0 : if (!entry->mr) {
400 : 0 : DRV_LOG(ERR, "Failed to create direct Mkey.");
401 : 0 : ret = -rte_errno;
402 : 0 : return ret;
403 : : }
404 : 0 : entry->is_indirect = 0;
405 : 0 : return 0;
406 : : }
|