numam-dpdk/drivers/net/mlx5/mlx5_ethdev.c
Adrien Mazarguil 771fa900b7 mlx5: introduce new driver for Mellanox ConnectX-4 adapters
In its current state, this driver implements the bare minimum to initialize
itself and Mellanox ConnectX-4 adapters without doing anything else
(no RX/TX for instance). It is disabled by default since it is based on the
mlx4 driver and also depends on libibverbs.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
Signed-off-by: Or Ami <ora@mellanox.com>
2015-10-30 22:03:42 +01:00

421 lines
9.5 KiB
C

/*-
* BSD LICENSE
*
* Copyright 2015 6WIND S.A.
* Copyright 2015 Mellanox.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of 6WIND S.A. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stddef.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if.h>
/* DPDK headers don't like -pedantic. */
#ifdef PEDANTIC
#pragma GCC diagnostic ignored "-pedantic"
#endif
#include <rte_atomic.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#include <rte_common.h>
#ifdef PEDANTIC
#pragma GCC diagnostic error "-pedantic"
#endif
#include "mlx5.h"
#include "mlx5_utils.h"
/**
* Get interface name from private structure.
*
* @param[in] priv
* Pointer to private structure.
* @param[out] ifname
* Interface name output buffer.
*
* @return
* 0 on success, -1 on failure and errno is set.
*/
int
priv_get_ifname(const struct priv *priv, char (*ifname)[IF_NAMESIZE])
{
DIR *dir;
struct dirent *dent;
unsigned int dev_type = 0;
unsigned int dev_port_prev = ~0u;
char match[IF_NAMESIZE] = "";
{
MKSTR(path, "%s/device/net", priv->ctx->device->ibdev_path);
dir = opendir(path);
if (dir == NULL)
return -1;
}
while ((dent = readdir(dir)) != NULL) {
char *name = dent->d_name;
FILE *file;
unsigned int dev_port;
int r;
if ((name[0] == '.') &&
((name[1] == '\0') ||
((name[1] == '.') && (name[2] == '\0'))))
continue;
MKSTR(path, "%s/device/net/%s/%s",
priv->ctx->device->ibdev_path, name,
(dev_type ? "dev_id" : "dev_port"));
file = fopen(path, "rb");
if (file == NULL) {
if (errno != ENOENT)
continue;
/*
* Switch to dev_id when dev_port does not exist as
* is the case with Linux kernel versions < 3.15.
*/
try_dev_id:
match[0] = '\0';
if (dev_type)
break;
dev_type = 1;
dev_port_prev = ~0u;
rewinddir(dir);
continue;
}
r = fscanf(file, (dev_type ? "%x" : "%u"), &dev_port);
fclose(file);
if (r != 1)
continue;
/*
* Switch to dev_id when dev_port returns the same value for
* all ports. May happen when using a MOFED release older than
* 3.0 with a Linux kernel >= 3.15.
*/
if (dev_port == dev_port_prev)
goto try_dev_id;
dev_port_prev = dev_port;
if (dev_port == (priv->port - 1u))
snprintf(match, sizeof(match), "%s", name);
}
closedir(dir);
if (match[0] == '\0')
return -1;
strncpy(*ifname, match, sizeof(*ifname));
return 0;
}
/**
* Read from sysfs entry.
*
* @param[in] priv
* Pointer to private structure.
* @param[in] entry
* Entry name relative to sysfs path.
* @param[out] buf
* Data output buffer.
* @param size
* Buffer size.
*
* @return
* 0 on success, -1 on failure and errno is set.
*/
static int
priv_sysfs_read(const struct priv *priv, const char *entry,
char *buf, size_t size)
{
char ifname[IF_NAMESIZE];
FILE *file;
int ret;
int err;
if (priv_get_ifname(priv, &ifname))
return -1;
MKSTR(path, "%s/device/net/%s/%s", priv->ctx->device->ibdev_path,
ifname, entry);
file = fopen(path, "rb");
if (file == NULL)
return -1;
ret = fread(buf, 1, size, file);
err = errno;
if (((size_t)ret < size) && (ferror(file)))
ret = -1;
else
ret = size;
fclose(file);
errno = err;
return ret;
}
/**
* Write to sysfs entry.
*
* @param[in] priv
* Pointer to private structure.
* @param[in] entry
* Entry name relative to sysfs path.
* @param[in] buf
* Data buffer.
* @param size
* Buffer size.
*
* @return
* 0 on success, -1 on failure and errno is set.
*/
static int
priv_sysfs_write(const struct priv *priv, const char *entry,
char *buf, size_t size)
{
char ifname[IF_NAMESIZE];
FILE *file;
int ret;
int err;
if (priv_get_ifname(priv, &ifname))
return -1;
MKSTR(path, "%s/device/net/%s/%s", priv->ctx->device->ibdev_path,
ifname, entry);
file = fopen(path, "wb");
if (file == NULL)
return -1;
ret = fwrite(buf, 1, size, file);
err = errno;
if (((size_t)ret < size) || (ferror(file)))
ret = -1;
else
ret = size;
fclose(file);
errno = err;
return ret;
}
/**
* Get unsigned long sysfs property.
*
* @param priv
* Pointer to private structure.
* @param[in] name
* Entry name relative to sysfs path.
* @param[out] value
* Value output buffer.
*
* @return
* 0 on success, -1 on failure and errno is set.
*/
static int
priv_get_sysfs_ulong(struct priv *priv, const char *name, unsigned long *value)
{
int ret;
unsigned long value_ret;
char value_str[32];
ret = priv_sysfs_read(priv, name, value_str, (sizeof(value_str) - 1));
if (ret == -1) {
DEBUG("cannot read %s value from sysfs: %s",
name, strerror(errno));
return -1;
}
value_str[ret] = '\0';
errno = 0;
value_ret = strtoul(value_str, NULL, 0);
if (errno) {
DEBUG("invalid %s value `%s': %s", name, value_str,
strerror(errno));
return -1;
}
*value = value_ret;
return 0;
}
/**
* Set unsigned long sysfs property.
*
* @param priv
* Pointer to private structure.
* @param[in] name
* Entry name relative to sysfs path.
* @param value
* Value to set.
*
* @return
* 0 on success, -1 on failure and errno is set.
*/
static int
priv_set_sysfs_ulong(struct priv *priv, const char *name, unsigned long value)
{
int ret;
MKSTR(value_str, "%lu", value);
ret = priv_sysfs_write(priv, name, value_str, (sizeof(value_str) - 1));
if (ret == -1) {
DEBUG("cannot write %s `%s' (%lu) to sysfs: %s",
name, value_str, value, strerror(errno));
return -1;
}
return 0;
}
/**
* Perform ifreq ioctl() on associated Ethernet device.
*
* @param[in] priv
* Pointer to private structure.
* @param req
* Request number to pass to ioctl().
* @param[out] ifr
* Interface request structure output buffer.
*
* @return
* 0 on success, -1 on failure and errno is set.
*/
int
priv_ifreq(const struct priv *priv, int req, struct ifreq *ifr)
{
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
int ret = -1;
if (sock == -1)
return ret;
if (priv_get_ifname(priv, &ifr->ifr_name) == 0)
ret = ioctl(sock, req, ifr);
close(sock);
return ret;
}
/**
* Get device MTU.
*
* @param priv
* Pointer to private structure.
* @param[out] mtu
* MTU value output buffer.
*
* @return
* 0 on success, -1 on failure and errno is set.
*/
int
priv_get_mtu(struct priv *priv, uint16_t *mtu)
{
unsigned long ulong_mtu;
if (priv_get_sysfs_ulong(priv, "mtu", &ulong_mtu) == -1)
return -1;
*mtu = ulong_mtu;
return 0;
}
/**
* Set device flags.
*
* @param priv
* Pointer to private structure.
* @param keep
* Bitmask for flags that must remain untouched.
* @param flags
* Bitmask for flags to modify.
*
* @return
* 0 on success, -1 on failure and errno is set.
*/
int
priv_set_flags(struct priv *priv, unsigned int keep, unsigned int flags)
{
unsigned long tmp;
if (priv_get_sysfs_ulong(priv, "flags", &tmp) == -1)
return -1;
tmp &= keep;
tmp |= flags;
return priv_set_sysfs_ulong(priv, "flags", tmp);
}
/**
* Get PCI information from struct ibv_device.
*
* @param device
* Pointer to Ethernet device structure.
* @param[out] pci_addr
* PCI bus address output buffer.
*
* @return
* 0 on success, -1 on failure and errno is set.
*/
int
mlx5_ibv_device_to_pci_addr(const struct ibv_device *device,
struct rte_pci_addr *pci_addr)
{
FILE *file;
char line[32];
MKSTR(path, "%s/device/uevent", device->ibdev_path);
file = fopen(path, "rb");
if (file == NULL)
return -1;
while (fgets(line, sizeof(line), file) == line) {
size_t len = strlen(line);
int ret;
/* Truncate long lines. */
if (len == (sizeof(line) - 1))
while (line[(len - 1)] != '\n') {
ret = fgetc(file);
if (ret == EOF)
break;
line[(len - 1)] = ret;
}
/* Extract information. */
if (sscanf(line,
"PCI_SLOT_NAME="
"%" SCNx16 ":%" SCNx8 ":%" SCNx8 ".%" SCNx8 "\n",
&pci_addr->domain,
&pci_addr->bus,
&pci_addr->devid,
&pci_addr->function) == 4) {
ret = 0;
break;
}
}
fclose(file);
return 0;
}