xref: /dpdk/drivers/net/mlx5/mlx5_ethdev.c (revision 771fa900b73aae1ac8ec0ae6ac086e9e164da7b2)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright 2015 6WIND S.A.
5  *   Copyright 2015 Mellanox.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of 6WIND S.A. nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <stddef.h>
35 #include <unistd.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <dirent.h>
42 #include <net/if.h>
43 #include <sys/ioctl.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <linux/if.h>
47 
48 /* DPDK headers don't like -pedantic. */
49 #ifdef PEDANTIC
50 #pragma GCC diagnostic ignored "-pedantic"
51 #endif
52 #include <rte_atomic.h>
53 #include <rte_ethdev.h>
54 #include <rte_mbuf.h>
55 #include <rte_common.h>
56 #ifdef PEDANTIC
57 #pragma GCC diagnostic error "-pedantic"
58 #endif
59 
60 #include "mlx5.h"
61 #include "mlx5_utils.h"
62 
63 /**
64  * Get interface name from private structure.
65  *
66  * @param[in] priv
67  *   Pointer to private structure.
68  * @param[out] ifname
69  *   Interface name output buffer.
70  *
71  * @return
72  *   0 on success, -1 on failure and errno is set.
73  */
74 int
75 priv_get_ifname(const struct priv *priv, char (*ifname)[IF_NAMESIZE])
76 {
77 	DIR *dir;
78 	struct dirent *dent;
79 	unsigned int dev_type = 0;
80 	unsigned int dev_port_prev = ~0u;
81 	char match[IF_NAMESIZE] = "";
82 
83 	{
84 		MKSTR(path, "%s/device/net", priv->ctx->device->ibdev_path);
85 
86 		dir = opendir(path);
87 		if (dir == NULL)
88 			return -1;
89 	}
90 	while ((dent = readdir(dir)) != NULL) {
91 		char *name = dent->d_name;
92 		FILE *file;
93 		unsigned int dev_port;
94 		int r;
95 
96 		if ((name[0] == '.') &&
97 		    ((name[1] == '\0') ||
98 		     ((name[1] == '.') && (name[2] == '\0'))))
99 			continue;
100 
101 		MKSTR(path, "%s/device/net/%s/%s",
102 		      priv->ctx->device->ibdev_path, name,
103 		      (dev_type ? "dev_id" : "dev_port"));
104 
105 		file = fopen(path, "rb");
106 		if (file == NULL) {
107 			if (errno != ENOENT)
108 				continue;
109 			/*
110 			 * Switch to dev_id when dev_port does not exist as
111 			 * is the case with Linux kernel versions < 3.15.
112 			 */
113 try_dev_id:
114 			match[0] = '\0';
115 			if (dev_type)
116 				break;
117 			dev_type = 1;
118 			dev_port_prev = ~0u;
119 			rewinddir(dir);
120 			continue;
121 		}
122 		r = fscanf(file, (dev_type ? "%x" : "%u"), &dev_port);
123 		fclose(file);
124 		if (r != 1)
125 			continue;
126 		/*
127 		 * Switch to dev_id when dev_port returns the same value for
128 		 * all ports. May happen when using a MOFED release older than
129 		 * 3.0 with a Linux kernel >= 3.15.
130 		 */
131 		if (dev_port == dev_port_prev)
132 			goto try_dev_id;
133 		dev_port_prev = dev_port;
134 		if (dev_port == (priv->port - 1u))
135 			snprintf(match, sizeof(match), "%s", name);
136 	}
137 	closedir(dir);
138 	if (match[0] == '\0')
139 		return -1;
140 	strncpy(*ifname, match, sizeof(*ifname));
141 	return 0;
142 }
143 
144 /**
145  * Read from sysfs entry.
146  *
147  * @param[in] priv
148  *   Pointer to private structure.
149  * @param[in] entry
150  *   Entry name relative to sysfs path.
151  * @param[out] buf
152  *   Data output buffer.
153  * @param size
154  *   Buffer size.
155  *
156  * @return
157  *   0 on success, -1 on failure and errno is set.
158  */
159 static int
160 priv_sysfs_read(const struct priv *priv, const char *entry,
161 		char *buf, size_t size)
162 {
163 	char ifname[IF_NAMESIZE];
164 	FILE *file;
165 	int ret;
166 	int err;
167 
168 	if (priv_get_ifname(priv, &ifname))
169 		return -1;
170 
171 	MKSTR(path, "%s/device/net/%s/%s", priv->ctx->device->ibdev_path,
172 	      ifname, entry);
173 
174 	file = fopen(path, "rb");
175 	if (file == NULL)
176 		return -1;
177 	ret = fread(buf, 1, size, file);
178 	err = errno;
179 	if (((size_t)ret < size) && (ferror(file)))
180 		ret = -1;
181 	else
182 		ret = size;
183 	fclose(file);
184 	errno = err;
185 	return ret;
186 }
187 
188 /**
189  * Write to sysfs entry.
190  *
191  * @param[in] priv
192  *   Pointer to private structure.
193  * @param[in] entry
194  *   Entry name relative to sysfs path.
195  * @param[in] buf
196  *   Data buffer.
197  * @param size
198  *   Buffer size.
199  *
200  * @return
201  *   0 on success, -1 on failure and errno is set.
202  */
203 static int
204 priv_sysfs_write(const struct priv *priv, const char *entry,
205 		 char *buf, size_t size)
206 {
207 	char ifname[IF_NAMESIZE];
208 	FILE *file;
209 	int ret;
210 	int err;
211 
212 	if (priv_get_ifname(priv, &ifname))
213 		return -1;
214 
215 	MKSTR(path, "%s/device/net/%s/%s", priv->ctx->device->ibdev_path,
216 	      ifname, entry);
217 
218 	file = fopen(path, "wb");
219 	if (file == NULL)
220 		return -1;
221 	ret = fwrite(buf, 1, size, file);
222 	err = errno;
223 	if (((size_t)ret < size) || (ferror(file)))
224 		ret = -1;
225 	else
226 		ret = size;
227 	fclose(file);
228 	errno = err;
229 	return ret;
230 }
231 
232 /**
233  * Get unsigned long sysfs property.
234  *
235  * @param priv
236  *   Pointer to private structure.
237  * @param[in] name
238  *   Entry name relative to sysfs path.
239  * @param[out] value
240  *   Value output buffer.
241  *
242  * @return
243  *   0 on success, -1 on failure and errno is set.
244  */
245 static int
246 priv_get_sysfs_ulong(struct priv *priv, const char *name, unsigned long *value)
247 {
248 	int ret;
249 	unsigned long value_ret;
250 	char value_str[32];
251 
252 	ret = priv_sysfs_read(priv, name, value_str, (sizeof(value_str) - 1));
253 	if (ret == -1) {
254 		DEBUG("cannot read %s value from sysfs: %s",
255 		      name, strerror(errno));
256 		return -1;
257 	}
258 	value_str[ret] = '\0';
259 	errno = 0;
260 	value_ret = strtoul(value_str, NULL, 0);
261 	if (errno) {
262 		DEBUG("invalid %s value `%s': %s", name, value_str,
263 		      strerror(errno));
264 		return -1;
265 	}
266 	*value = value_ret;
267 	return 0;
268 }
269 
270 /**
271  * Set unsigned long sysfs property.
272  *
273  * @param priv
274  *   Pointer to private structure.
275  * @param[in] name
276  *   Entry name relative to sysfs path.
277  * @param value
278  *   Value to set.
279  *
280  * @return
281  *   0 on success, -1 on failure and errno is set.
282  */
283 static int
284 priv_set_sysfs_ulong(struct priv *priv, const char *name, unsigned long value)
285 {
286 	int ret;
287 	MKSTR(value_str, "%lu", value);
288 
289 	ret = priv_sysfs_write(priv, name, value_str, (sizeof(value_str) - 1));
290 	if (ret == -1) {
291 		DEBUG("cannot write %s `%s' (%lu) to sysfs: %s",
292 		      name, value_str, value, strerror(errno));
293 		return -1;
294 	}
295 	return 0;
296 }
297 
298 /**
299  * Perform ifreq ioctl() on associated Ethernet device.
300  *
301  * @param[in] priv
302  *   Pointer to private structure.
303  * @param req
304  *   Request number to pass to ioctl().
305  * @param[out] ifr
306  *   Interface request structure output buffer.
307  *
308  * @return
309  *   0 on success, -1 on failure and errno is set.
310  */
311 int
312 priv_ifreq(const struct priv *priv, int req, struct ifreq *ifr)
313 {
314 	int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
315 	int ret = -1;
316 
317 	if (sock == -1)
318 		return ret;
319 	if (priv_get_ifname(priv, &ifr->ifr_name) == 0)
320 		ret = ioctl(sock, req, ifr);
321 	close(sock);
322 	return ret;
323 }
324 
325 /**
326  * Get device MTU.
327  *
328  * @param priv
329  *   Pointer to private structure.
330  * @param[out] mtu
331  *   MTU value output buffer.
332  *
333  * @return
334  *   0 on success, -1 on failure and errno is set.
335  */
336 int
337 priv_get_mtu(struct priv *priv, uint16_t *mtu)
338 {
339 	unsigned long ulong_mtu;
340 
341 	if (priv_get_sysfs_ulong(priv, "mtu", &ulong_mtu) == -1)
342 		return -1;
343 	*mtu = ulong_mtu;
344 	return 0;
345 }
346 
347 /**
348  * Set device flags.
349  *
350  * @param priv
351  *   Pointer to private structure.
352  * @param keep
353  *   Bitmask for flags that must remain untouched.
354  * @param flags
355  *   Bitmask for flags to modify.
356  *
357  * @return
358  *   0 on success, -1 on failure and errno is set.
359  */
360 int
361 priv_set_flags(struct priv *priv, unsigned int keep, unsigned int flags)
362 {
363 	unsigned long tmp;
364 
365 	if (priv_get_sysfs_ulong(priv, "flags", &tmp) == -1)
366 		return -1;
367 	tmp &= keep;
368 	tmp |= flags;
369 	return priv_set_sysfs_ulong(priv, "flags", tmp);
370 }
371 
372 /**
373  * Get PCI information from struct ibv_device.
374  *
375  * @param device
376  *   Pointer to Ethernet device structure.
377  * @param[out] pci_addr
378  *   PCI bus address output buffer.
379  *
380  * @return
381  *   0 on success, -1 on failure and errno is set.
382  */
383 int
384 mlx5_ibv_device_to_pci_addr(const struct ibv_device *device,
385 			    struct rte_pci_addr *pci_addr)
386 {
387 	FILE *file;
388 	char line[32];
389 	MKSTR(path, "%s/device/uevent", device->ibdev_path);
390 
391 	file = fopen(path, "rb");
392 	if (file == NULL)
393 		return -1;
394 	while (fgets(line, sizeof(line), file) == line) {
395 		size_t len = strlen(line);
396 		int ret;
397 
398 		/* Truncate long lines. */
399 		if (len == (sizeof(line) - 1))
400 			while (line[(len - 1)] != '\n') {
401 				ret = fgetc(file);
402 				if (ret == EOF)
403 					break;
404 				line[(len - 1)] = ret;
405 			}
406 		/* Extract information. */
407 		if (sscanf(line,
408 			   "PCI_SLOT_NAME="
409 			   "%" SCNx16 ":%" SCNx8 ":%" SCNx8 ".%" SCNx8 "\n",
410 			   &pci_addr->domain,
411 			   &pci_addr->bus,
412 			   &pci_addr->devid,
413 			   &pci_addr->function) == 4) {
414 			ret = 0;
415 			break;
416 		}
417 	}
418 	fclose(file);
419 	return 0;
420 }
421