mlx5: introduce new driver for Mellanox ConnectX-4 adapters
[dpdk.git] / drivers / net / mlx5 / mlx5_ethdev.c
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 }