xref: /dpdk/drivers/common/mlx5/mlx5_common.c (revision 89813a522e68076e6f50ec18b075fa57cc5ae937)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2019 Mellanox Technologies, Ltd
3  */
4 
5 #include <unistd.h>
6 #include <string.h>
7 #include <stdio.h>
8 
9 #include <rte_errno.h>
10 #include <rte_mempool.h>
11 
12 #include "mlx5_common.h"
13 #include "mlx5_common_os.h"
14 #include "mlx5_common_utils.h"
15 #include "mlx5_common_pci.h"
16 
17 uint8_t haswell_broadwell_cpu;
18 
19 /* In case this is an x86_64 intel processor to check if
20  * we should use relaxed ordering.
21  */
22 #ifdef RTE_ARCH_X86_64
23 /**
24  * This function returns processor identification and feature information
25  * into the registers.
26  *
27  * @param eax, ebx, ecx, edx
28  *		Pointers to the registers that will hold cpu information.
29  * @param level
30  *		The main category of information returned.
31  */
32 static inline void mlx5_cpu_id(unsigned int level,
33 				unsigned int *eax, unsigned int *ebx,
34 				unsigned int *ecx, unsigned int *edx)
35 {
36 	__asm__("cpuid\n\t"
37 		: "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
38 		: "0" (level));
39 }
40 #endif
41 
42 RTE_LOG_REGISTER(mlx5_common_logtype, pmd.common.mlx5, NOTICE)
43 
44 static bool mlx5_common_initialized;
45 
46 /**
47  * One time innitialization routine for run-time dependency on glue library
48  * for multiple PMDs. Each mlx5 PMD that depends on mlx5_common module,
49  * must invoke in its constructor.
50  */
51 void
52 mlx5_common_init(void)
53 {
54 	if (mlx5_common_initialized)
55 		return;
56 
57 	mlx5_glue_constructor();
58 	mlx5_common_pci_init();
59 	mlx5_common_initialized = true;
60 }
61 
62 /**
63  * This function is responsible of initializing the variable
64  *  haswell_broadwell_cpu by checking if the cpu is intel
65  *  and reading the data returned from mlx5_cpu_id().
66  *  since haswell and broadwell cpus don't have improved performance
67  *  when using relaxed ordering we want to check the cpu type before
68  *  before deciding whether to enable RO or not.
69  *  if the cpu is haswell or broadwell the variable will be set to 1
70  *  otherwise it will be 0.
71  */
72 RTE_INIT_PRIO(mlx5_is_haswell_broadwell_cpu, LOG)
73 {
74 #ifdef RTE_ARCH_X86_64
75 	unsigned int broadwell_models[4] = {0x3d, 0x47, 0x4F, 0x56};
76 	unsigned int haswell_models[4] = {0x3c, 0x3f, 0x45, 0x46};
77 	unsigned int i, model, family, brand_id, vendor;
78 	unsigned int signature_intel_ebx = 0x756e6547;
79 	unsigned int extended_model;
80 	unsigned int eax = 0;
81 	unsigned int ebx = 0;
82 	unsigned int ecx = 0;
83 	unsigned int edx = 0;
84 	int max_level;
85 
86 	mlx5_cpu_id(0, &eax, &ebx, &ecx, &edx);
87 	vendor = ebx;
88 	max_level = eax;
89 	if (max_level < 1) {
90 		haswell_broadwell_cpu = 0;
91 		return;
92 	}
93 	mlx5_cpu_id(1, &eax, &ebx, &ecx, &edx);
94 	model = (eax >> 4) & 0x0f;
95 	family = (eax >> 8) & 0x0f;
96 	brand_id = ebx & 0xff;
97 	extended_model = (eax >> 12) & 0xf0;
98 	/* Check if the processor is Haswell or Broadwell */
99 	if (vendor == signature_intel_ebx) {
100 		if (family == 0x06)
101 			model += extended_model;
102 		if (brand_id == 0 && family == 0x6) {
103 			for (i = 0; i < RTE_DIM(broadwell_models); i++)
104 				if (model == broadwell_models[i]) {
105 					haswell_broadwell_cpu = 1;
106 					return;
107 				}
108 			for (i = 0; i < RTE_DIM(haswell_models); i++)
109 				if (model == haswell_models[i]) {
110 					haswell_broadwell_cpu = 1;
111 					return;
112 				}
113 		}
114 	}
115 #endif
116 	haswell_broadwell_cpu = 0;
117 }
118 
119 /**
120  * Allocate the User Access Region with DevX on specified device.
121  *
122  * @param [in] ctx
123  *   Infiniband device context to perform allocation on.
124  * @param [in] mapping
125  *   MLX5DV_UAR_ALLOC_TYPE_BF - allocate as cached memory with write-combining
126  *				attributes (if supported by the host), the
127  *				writes to the UAR registers must be followed
128  *				by write memory barrier.
129  *   MLX5DV_UAR_ALLOC_TYPE_NC - allocate as non-cached nenory, all writes are
130  *				promoted to the registers immediately, no
131  *				memory barriers needed.
132  *   mapping < 0 - the first attempt is performed with MLX5DV_UAR_ALLOC_TYPE_BF,
133  *		   if this fails the next attempt with MLX5DV_UAR_ALLOC_TYPE_NC
134  *		   is performed. The drivers specifying negative values should
135  *		   always provide the write memory barrier operation after UAR
136  *		   register writings.
137  * If there is no definitions for the MLX5DV_UAR_ALLOC_TYPE_xx (older rdma
138  * library headers), the caller can specify 0.
139  *
140  * @return
141  *   UAR object pointer on success, NULL otherwise and rte_errno is set.
142  */
143 void *
144 mlx5_devx_alloc_uar(void *ctx, int mapping)
145 {
146 	void *uar;
147 	uint32_t retry, uar_mapping;
148 	void *base_addr;
149 
150 	for (retry = 0; retry < MLX5_ALLOC_UAR_RETRY; ++retry) {
151 #ifdef MLX5DV_UAR_ALLOC_TYPE_NC
152 		/* Control the mapping type according to the settings. */
153 		uar_mapping = (mapping < 0) ?
154 			      MLX5DV_UAR_ALLOC_TYPE_NC : mapping;
155 #else
156 		/*
157 		 * It seems we have no way to control the memory mapping type
158 		 * for the UAR, the default "Write-Combining" type is supposed.
159 		 */
160 		uar_mapping = 0;
161 		RTE_SET_USED(mapping);
162 #endif
163 		uar = mlx5_glue->devx_alloc_uar(ctx, uar_mapping);
164 #ifdef MLX5DV_UAR_ALLOC_TYPE_NC
165 		if (!uar &&
166 		    mapping < 0 &&
167 		    uar_mapping == MLX5DV_UAR_ALLOC_TYPE_BF) {
168 			/*
169 			 * In some environments like virtual machine the
170 			 * Write Combining mapped might be not supported and
171 			 * UAR allocation fails. We tried "Non-Cached" mapping
172 			 * for the case.
173 			 */
174 			DRV_LOG(WARNING, "Failed to allocate DevX UAR (BF)");
175 			uar_mapping = MLX5DV_UAR_ALLOC_TYPE_NC;
176 			uar = mlx5_glue->devx_alloc_uar(ctx, uar_mapping);
177 		} else if (!uar &&
178 			   mapping < 0 &&
179 			   uar_mapping == MLX5DV_UAR_ALLOC_TYPE_NC) {
180 			/*
181 			 * If Verbs/kernel does not support "Non-Cached"
182 			 * try the "Write-Combining".
183 			 */
184 			DRV_LOG(WARNING, "Failed to allocate DevX UAR (NC)");
185 			uar_mapping = MLX5DV_UAR_ALLOC_TYPE_BF;
186 			uar = mlx5_glue->devx_alloc_uar(ctx, uar_mapping);
187 		}
188 #endif
189 		if (!uar) {
190 			DRV_LOG(ERR, "Failed to allocate DevX UAR (BF/NC)");
191 			rte_errno = ENOMEM;
192 			goto exit;
193 		}
194 		base_addr = mlx5_os_get_devx_uar_base_addr(uar);
195 		if (base_addr)
196 			break;
197 		/*
198 		 * The UARs are allocated by rdma_core within the
199 		 * IB device context, on context closure all UARs
200 		 * will be freed, should be no memory/object leakage.
201 		 */
202 		DRV_LOG(WARNING, "Retrying to allocate DevX UAR");
203 		uar = NULL;
204 	}
205 	/* Check whether we finally succeeded with valid UAR allocation. */
206 	if (!uar) {
207 		DRV_LOG(ERR, "Failed to allocate DevX UAR (NULL base)");
208 		rte_errno = ENOMEM;
209 	}
210 	/*
211 	 * Return void * instead of struct mlx5dv_devx_uar *
212 	 * is for compatibility with older rdma-core library headers.
213 	 */
214 exit:
215 	return uar;
216 }
217