Commit 8e98387b authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'mlx5-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mellanox/linux

Saeed Mahameed says:

====================
mlx5-next auxbus support

This pull request is targeting net-next and rdma-next branches.

This series provides mlx5 support for auxiliary bus devices.

It starts with a merge commit of tag 'auxbus-5.11-rc1' from
gregkh/driver-core into mlx5-next, then the mlx5 patches that will convert
mlx5 ulp devices (netdev, rdma, vdpa) to use the proper auxbus
infrastructure instead of the internal mlx5 device and interface management
implementation, which Leon is deleting at the end of this patchset.

Link: https://lore.kernel.org/alsa-devel/20201026111849.1035786-1-leon@kernel.org/

Thanks to everyone for the joint effort !

* 'mlx5-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mellanox/linux:
  RDMA/mlx5: Remove IB representors dead code
  net/mlx5: Simplify eswitch mode check
  net/mlx5: Delete custom device management logic
  RDMA/mlx5: Convert mlx5_ib to use auxiliary bus
  net/mlx5e: Connect ethernet part to auxiliary bus
  vdpa/mlx5: Connect mlx5_vdpa to auxiliary bus
  net/mlx5: Register mlx5 devices to auxiliary virtual bus
  vdpa/mlx5: Make hardware definitions visible to all mlx5 devices
  net/mlx5_core: Clean driver version and name
  net/mlx5: Properly convey driver version to firmware
  driver core: auxiliary bus: minor coding style tweaks
  driver core: auxiliary bus: make remove function return void
  driver core: auxiliary bus: move slab.h from include file
  Add auxiliary bus support
====================

Link: https://lore.kernel.org/r/20201207053349.402772-1-saeed@kernel.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 374a96b9 04b222f9
.. SPDX-License-Identifier: GPL-2.0-only
=============
Auxiliary Bus
=============
In some subsystems, the functionality of the core device (PCI/ACPI/other) is
too complex for a single device to be managed by a monolithic driver
(e.g. Sound Open Firmware), multiple devices might implement a common
intersection of functionality (e.g. NICs + RDMA), or a driver may want to
export an interface for another subsystem to drive (e.g. SIOV Physical Function
export Virtual Function management). A split of the functinoality into child-
devices representing sub-domains of functionality makes it possible to
compartmentalize, layer, and distribute domain-specific concerns via a Linux
device-driver model.
An example for this kind of requirement is the audio subsystem where a single
IP is handling multiple entities such as HDMI, Soundwire, local devices such as
mics/speakers etc. The split for the core's functionality can be arbitrary or
be defined by the DSP firmware topology and include hooks for test/debug. This
allows for the audio core device to be minimal and focused on hardware-specific
control and communication.
Each auxiliary_device represents a part of its parent functionality. The
generic behavior can be extended and specialized as needed by encapsulating an
auxiliary_device within other domain-specific structures and the use of .ops
callbacks. Devices on the auxiliary bus do not share any structures and the use
of a communication channel with the parent is domain-specific.
Note that ops are intended as a way to augment instance behavior within a class
of auxiliary devices, it is not the mechanism for exporting common
infrastructure from the parent. Consider EXPORT_SYMBOL_NS() to convey
infrastructure from the parent module to the auxiliary module(s).
When Should the Auxiliary Bus Be Used
=====================================
The auxiliary bus is to be used when a driver and one or more kernel modules,
who share a common header file with the driver, need a mechanism to connect and
provide access to a shared object allocated by the auxiliary_device's
registering driver. The registering driver for the auxiliary_device(s) and the
kernel module(s) registering auxiliary_drivers can be from the same subsystem,
or from multiple subsystems.
The emphasis here is on a common generic interface that keeps subsystem
customization out of the bus infrastructure.
One example is a PCI network device that is RDMA-capable and exports a child
device to be driven by an auxiliary_driver in the RDMA subsystem. The PCI
driver allocates and registers an auxiliary_device for each physical
function on the NIC. The RDMA driver registers an auxiliary_driver that claims
each of these auxiliary_devices. This conveys data/ops published by the parent
PCI device/driver to the RDMA auxiliary_driver.
Another use case is for the PCI device to be split out into multiple sub
functions. For each sub function an auxiliary_device is created. A PCI sub
function driver binds to such devices that creates its own one or more class
devices. A PCI sub function auxiliary device is likely to be contained in a
struct with additional attributes such as user defined sub function number and
optional attributes such as resources and a link to the parent device. These
attributes could be used by systemd/udev; and hence should be initialized
before a driver binds to an auxiliary_device.
A key requirement for utilizing the auxiliary bus is that there is no
dependency on a physical bus, device, register accesses or regmap support.
These individual devices split from the core cannot live on the platform bus as
they are not physical devices that are controlled by DT/ACPI. The same
argument applies for not using MFD in this scenario as MFD relies on individual
function devices being physical devices.
Auxiliary Device
================
An auxiliary_device represents a part of its parent device's functionality. It
is given a name that, combined with the registering drivers KBUILD_MODNAME,
creates a match_name that is used for driver binding, and an id that combined
with the match_name provide a unique name to register with the bus subsystem.
Registering an auxiliary_device is a two-step process. First call
auxiliary_device_init(), which checks several aspects of the auxiliary_device
struct and performs a device_initialize(). After this step completes, any
error state must have a call to auxiliary_device_uninit() in its resolution path.
The second step in registering an auxiliary_device is to perform a call to
auxiliary_device_add(), which sets the name of the device and add the device to
the bus.
Unregistering an auxiliary_device is also a two-step process to mirror the
register process. First call auxiliary_device_delete(), then call
auxiliary_device_uninit().
.. code-block:: c
struct auxiliary_device {
struct device dev;
const char *name;
u32 id;
};
If two auxiliary_devices both with a match_name "mod.foo" are registered onto
the bus, they must have unique id values (e.g. "x" and "y") so that the
registered devices names are "mod.foo.x" and "mod.foo.y". If match_name + id
are not unique, then the device_add fails and generates an error message.
The auxiliary_device.dev.type.release or auxiliary_device.dev.release must be
populated with a non-NULL pointer to successfully register the auxiliary_device.
The auxiliary_device.dev.parent must also be populated.
Auxiliary Device Memory Model and Lifespan
------------------------------------------
The registering driver is the entity that allocates memory for the
auxiliary_device and register it on the auxiliary bus. It is important to note
that, as opposed to the platform bus, the registering driver is wholly
responsible for the management for the memory used for the driver object.
A parent object, defined in the shared header file, contains the
auxiliary_device. It also contains a pointer to the shared object(s), which
also is defined in the shared header. Both the parent object and the shared
object(s) are allocated by the registering driver. This layout allows the
auxiliary_driver's registering module to perform a container_of() call to go
from the pointer to the auxiliary_device, that is passed during the call to the
auxiliary_driver's probe function, up to the parent object, and then have
access to the shared object(s).
The memory for the auxiliary_device is freed only in its release() callback
flow as defined by its registering driver.
The memory for the shared object(s) must have a lifespan equal to, or greater
than, the lifespan of the memory for the auxiliary_device. The auxiliary_driver
should only consider that this shared object is valid as long as the
auxiliary_device is still registered on the auxiliary bus. It is up to the
registering driver to manage (e.g. free or keep available) the memory for the
shared object beyond the life of the auxiliary_device.
The registering driver must unregister all auxiliary devices before its own
driver.remove() is completed.
Auxiliary Drivers
=================
Auxiliary drivers follow the standard driver model convention, where
discovery/enumeration is handled by the core, and drivers
provide probe() and remove() methods. They support power management
and shutdown notifications using the standard conventions.
.. code-block:: c
struct auxiliary_driver {
int (*probe)(struct auxiliary_device *,
const struct auxiliary_device_id *id);
void (*remove)(struct auxiliary_device *);
void (*shutdown)(struct auxiliary_device *);
int (*suspend)(struct auxiliary_device *, pm_message_t);
int (*resume)(struct auxiliary_device *);
struct device_driver driver;
const struct auxiliary_device_id *id_table;
};
Auxiliary drivers register themselves with the bus by calling
auxiliary_driver_register(). The id_table contains the match_names of auxiliary
devices that a driver can bind with.
Example Usage
=============
Auxiliary devices are created and registered by a subsystem-level core device
that needs to break up its functionality into smaller fragments. One way to
extend the scope of an auxiliary_device is to encapsulate it within a domain-
pecific structure defined by the parent device. This structure contains the
auxiliary_device and any associated shared data/callbacks needed to establish
the connection with the parent.
An example is:
.. code-block:: c
struct foo {
struct auxiliary_device auxdev;
void (*connect)(struct auxiliary_device *auxdev);
void (*disconnect)(struct auxiliary_device *auxdev);
void *data;
};
The parent device then registers the auxiliary_device by calling
auxiliary_device_init(), and then auxiliary_device_add(), with the pointer to
the auxdev member of the above structure. The parent provides a name for the
auxiliary_device that, combined with the parent's KBUILD_MODNAME, creates a
match_name that is be used for matching and binding with a driver.
Whenever an auxiliary_driver is registered, based on the match_name, the
auxiliary_driver's probe() is invoked for the matching devices. The
auxiliary_driver can also be encapsulated inside custom drivers that make the
core device's functionality extensible by adding additional domain-specific ops
as follows:
.. code-block:: c
struct my_ops {
void (*send)(struct auxiliary_device *auxdev);
void (*receive)(struct auxiliary_device *auxdev);
};
struct my_driver {
struct auxiliary_driver auxiliary_drv;
const struct my_ops ops;
};
An example of this type of usage is:
.. code-block:: c
const struct auxiliary_device_id my_auxiliary_id_table[] = {
{ .name = "foo_mod.foo_dev" },
{ },
};
const struct my_ops my_custom_ops = {
.send = my_tx,
.receive = my_rx,
};
const struct my_driver my_drv = {
.auxiliary_drv = {
.name = "myauxiliarydrv",
.id_table = my_auxiliary_id_table,
.probe = my_probe,
.remove = my_remove,
.shutdown = my_shutdown,
},
.ops = my_custom_ops,
};
......@@ -72,6 +72,7 @@ available subsections can be seen below.
thermal/index
fpga/index
acpi/index
auxiliary_bus
backlight/lp855x-driver.rst
connector
console
......
# SPDX-License-Identifier: GPL-2.0
menu "Generic Driver Options"
config AUXILIARY_BUS
bool
config UEVENT_HELPER
bool "Support for uevent helper"
help
......
......@@ -7,6 +7,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
attribute_container.o transport_class.o \
topology.o container.o property.o cacheinfo.o \
swnode.o
obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
obj-y += power/
obj-$(CONFIG_ISA_BUS_API) += isa.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2020 Intel Corporation
*
* Please see Documentation/driver-api/auxiliary_bus.rst for more information.
*/
#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
#include <linux/device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/string.h>
#include <linux/auxiliary_bus.h>
static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id,
const struct auxiliary_device *auxdev)
{
for (; id->name[0]; id++) {
const char *p = strrchr(dev_name(&auxdev->dev), '.');
int match_size;
if (!p)
continue;
match_size = p - dev_name(&auxdev->dev);
/* use dev_name(&auxdev->dev) prefix before last '.' char to match to */
if (strlen(id->name) == match_size &&
!strncmp(dev_name(&auxdev->dev), id->name, match_size))
return id;
}
return NULL;
}
static int auxiliary_match(struct device *dev, struct device_driver *drv)
{
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
struct auxiliary_driver *auxdrv = to_auxiliary_drv(drv);
return !!auxiliary_match_id(auxdrv->id_table, auxdev);
}
static int auxiliary_uevent(struct device *dev, struct kobj_uevent_env *env)
{
const char *name, *p;
name = dev_name(dev);
p = strrchr(name, '.');
return add_uevent_var(env, "MODALIAS=%s%.*s", AUXILIARY_MODULE_PREFIX,
(int)(p - name), name);
}
static const struct dev_pm_ops auxiliary_dev_pm_ops = {
SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, pm_generic_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume)
};
static int auxiliary_bus_probe(struct device *dev)
{
struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
int ret;
ret = dev_pm_domain_attach(dev, true);
if (ret) {
dev_warn(dev, "Failed to attach to PM Domain : %d\n", ret);
return ret;
}
ret = auxdrv->probe(auxdev, auxiliary_match_id(auxdrv->id_table, auxdev));
if (ret)
dev_pm_domain_detach(dev, true);
return ret;
}
static int auxiliary_bus_remove(struct device *dev)
{
struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
if (auxdrv->remove)
auxdrv->remove(auxdev);
dev_pm_domain_detach(dev, true);
return 0;
}
static void auxiliary_bus_shutdown(struct device *dev)
{
struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
if (auxdrv->shutdown)
auxdrv->shutdown(auxdev);
}
static struct bus_type auxiliary_bus_type = {
.name = "auxiliary",
.probe = auxiliary_bus_probe,
.remove = auxiliary_bus_remove,
.shutdown = auxiliary_bus_shutdown,
.match = auxiliary_match,
.uevent = auxiliary_uevent,
.pm = &auxiliary_dev_pm_ops,
};
/**
* auxiliary_device_init - check auxiliary_device and initialize
* @auxdev: auxiliary device struct
*
* This is the first step in the two-step process to register an
* auxiliary_device.
*
* When this function returns an error code, then the device_initialize will
* *not* have been performed, and the caller will be responsible to free any
* memory allocated for the auxiliary_device in the error path directly.
*
* It returns 0 on success. On success, the device_initialize has been
* performed. After this point any error unwinding will need to include a call
* to auxiliary_device_uninit(). In this post-initialize error scenario, a call
* to the device's .release callback will be triggered, and all memory clean-up
* is expected to be handled there.
*/
int auxiliary_device_init(struct auxiliary_device *auxdev)
{
struct device *dev = &auxdev->dev;
if (!dev->parent) {
pr_err("auxiliary_device has a NULL dev->parent\n");
return -EINVAL;
}
if (!auxdev->name) {
pr_err("auxiliary_device has a NULL name\n");
return -EINVAL;
}
dev->bus = &auxiliary_bus_type;
device_initialize(&auxdev->dev);
return 0;
}
EXPORT_SYMBOL_GPL(auxiliary_device_init);
/**
* __auxiliary_device_add - add an auxiliary bus device
* @auxdev: auxiliary bus device to add to the bus
* @modname: name of the parent device's driver module
*
* This is the second step in the two-step process to register an
* auxiliary_device.
*
* This function must be called after a successful call to
* auxiliary_device_init(), which will perform the device_initialize. This
* means that if this returns an error code, then a call to
* auxiliary_device_uninit() must be performed so that the .release callback
* will be triggered to free the memory associated with the auxiliary_device.
*
* The expectation is that users will call the "auxiliary_device_add" macro so
* that the caller's KBUILD_MODNAME is automatically inserted for the modname
* parameter. Only if a user requires a custom name would this version be
* called directly.
*/
int __auxiliary_device_add(struct auxiliary_device *auxdev, const char *modname)
{
struct device *dev = &auxdev->dev;
int ret;
if (!modname) {
dev_err(dev, "auxiliary device modname is NULL\n");
return -EINVAL;
}
ret = dev_set_name(dev, "%s.%s.%d", modname, auxdev->name, auxdev->id);
if (ret) {
dev_err(dev, "auxiliary device dev_set_name failed: %d\n", ret);
return ret;
}
ret = device_add(dev);
if (ret)
dev_err(dev, "adding auxiliary device failed!: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(__auxiliary_device_add);
/**
* auxiliary_find_device - auxiliary device iterator for locating a particular device.
* @start: Device to begin with
* @data: Data to pass to match function
* @match: Callback function to check device
*
* This function returns a reference to a device that is 'found'
* for later use, as determined by the @match callback.
*
* The callback should return 0 if the device doesn't match and non-zero
* if it does. If the callback returns non-zero, this function will
* return to the caller and not iterate over any more devices.
*/
struct auxiliary_device *auxiliary_find_device(struct device *start,
const void *data,
int (*match)(struct device *dev, const void *data))
{
struct device *dev;
dev = bus_find_device(&auxiliary_bus_type, start, data, match);
if (!dev)
return NULL;
return to_auxiliary_dev(dev);
}
EXPORT_SYMBOL_GPL(auxiliary_find_device);
/**
* __auxiliary_driver_register - register a driver for auxiliary bus devices
* @auxdrv: auxiliary_driver structure
* @owner: owning module/driver
* @modname: KBUILD_MODNAME for parent driver
*/
int __auxiliary_driver_register(struct auxiliary_driver *auxdrv,
struct module *owner, const char *modname)
{
if (WARN_ON(!auxdrv->probe) || WARN_ON(!auxdrv->id_table))
return -EINVAL;
if (auxdrv->name)
auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s.%s", modname,
auxdrv->name);
else
auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s", modname);
if (!auxdrv->driver.name)
return -ENOMEM;
auxdrv->driver.owner = owner;
auxdrv->driver.bus = &auxiliary_bus_type;
auxdrv->driver.mod_name = modname;
return driver_register(&auxdrv->driver);
}
EXPORT_SYMBOL_GPL(__auxiliary_driver_register);
/**
* auxiliary_driver_unregister - unregister a driver
* @auxdrv: auxiliary_driver structure
*/
void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv)
{
driver_unregister(&auxdrv->driver);
kfree(auxdrv->driver.name);
}
EXPORT_SYMBOL_GPL(auxiliary_driver_unregister);
static int __init auxiliary_bus_init(void)
{
return bus_register(&auxiliary_bus_type);
}
static void __exit auxiliary_bus_exit(void)
{
bus_unregister(&auxiliary_bus_type);
}
module_init(auxiliary_bus_init);
module_exit(auxiliary_bus_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Auxiliary Bus");
MODULE_AUTHOR("David Ertman <david.m.ertman@intel.com>");
MODULE_AUTHOR("Kiran Patil <kiran.patil@intel.com>");
......@@ -138,13 +138,6 @@ static int mlx5_ib_create_counters(struct ib_counters *counters,
}
static bool is_mdev_switchdev_mode(const struct mlx5_core_dev *mdev)
{
return MLX5_ESWITCH_MANAGER(mdev) &&
mlx5_ib_eswitch_mode(mdev->priv.eswitch) ==
MLX5_ESWITCH_OFFLOADS;
}
static const struct mlx5_ib_counters *get_counters(struct mlx5_ib_dev *dev,
u8 port_num)
{
......
......@@ -13,7 +13,7 @@ mlx5_ib_set_vport_rep(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
struct mlx5_ib_dev *ibdev;
int vport_index;
ibdev = mlx5_ib_get_uplink_ibdev(dev->priv.eswitch);
ibdev = mlx5_eswitch_uplink_get_proto_dev(dev->priv.eswitch, REP_IB);
vport_index = rep->vport_index;
ibdev->port[vport_index].rep = rep;
......@@ -33,6 +33,7 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
const struct mlx5_ib_profile *profile;
struct mlx5_ib_dev *ibdev;
int vport_index;
int ret;
if (rep->vport == MLX5_VPORT_UPLINK)
profile = &raw_eth_profile;
......@@ -46,8 +47,8 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
ibdev->port = kcalloc(num_ports, sizeof(*ibdev->port),
GFP_KERNEL);
if (!ibdev->port) {
ib_dealloc_device(&ibdev->ib_dev);
return -ENOMEM;
ret = -ENOMEM;
goto fail_port;
}
ibdev->is_rep = true;
......@@ -58,12 +59,24 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
ibdev->mdev = dev;
ibdev->num_ports = num_ports;
if (!__mlx5_ib_add(ibdev, profile))
return -EINVAL;
ret = __mlx5_ib_add(ibdev, profile);
if (ret)
goto fail_add;
rep->rep_data[REP_IB].priv = ibdev;
return 0;
fail_add:
kfree(ibdev->port);
fail_port:
ib_dealloc_device(&ibdev->ib_dev);
return ret;
}
static void *mlx5_ib_rep_to_dev(struct mlx5_eswitch_rep *rep)
{
return rep->rep_data[REP_IB].priv;
}
static void
......@@ -83,59 +96,18 @@ mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep)
__mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
}
static void *mlx5_ib_vport_get_proto_dev(struct mlx5_eswitch_rep *rep)
{
return mlx5_ib_rep_to_dev(rep);
}
static const struct mlx5_eswitch_rep_ops rep_ops = {
.load = mlx5_ib_vport_rep_load,
.unload = mlx5_ib_vport_rep_unload,
.get_proto_dev = mlx5_ib_vport_get_proto_dev,
.get_proto_dev = mlx5_ib_rep_to_dev,
};
void mlx5_ib_register_vport_reps(struct mlx5_core_dev *mdev)
{
struct mlx5_eswitch *esw = mdev->priv.eswitch;
mlx5_eswitch_register_vport_reps(esw, &rep_ops, REP_IB);
}
void mlx5_ib_unregister_vport_reps(struct mlx5_core_dev *mdev)
{
struct mlx5_eswitch *esw = mdev->priv.eswitch;
mlx5_eswitch_unregister_vport_reps(esw, REP_IB);
}
u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw)
{
return mlx5_eswitch_mode(esw);
}
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
u16 vport_num)
{
return mlx5_eswitch_get_proto_dev(esw, vport_num, REP_IB);
}
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
u16 vport_num)
{
return mlx5_eswitch_get_proto_dev(esw, vport_num, REP_ETH);
}
struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw)
{
return mlx5_eswitch_uplink_get_proto_dev(esw, REP_IB);
}
struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
u16 vport_num)
{
return mlx5_eswitch_vport_rep(esw, vport_num);
}
struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq,
u16 port)
......@@ -154,3 +126,49 @@ struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
return mlx5_eswitch_add_send_to_vport_rule(esw, rep->vport,
sq->base.mqp.qpn);
}
static int mlx5r_rep_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
struct mlx5_eswitch *esw;
esw = mdev->priv.eswitch;
mlx5_eswitch_register_vport_reps(esw, &rep_ops, REP_IB);
return 0;
}
static void mlx5r_rep_remove(struct auxiliary_device *adev)
{
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
struct mlx5_eswitch *esw;
esw = mdev->priv.eswitch;
mlx5_eswitch_unregister_vport_reps(esw, REP_IB);
}
static const struct auxiliary_device_id mlx5r_rep_id_table[] = {
{ .name = MLX5_ADEV_NAME ".rdma-rep", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, mlx5r_rep_id_table);
static struct auxiliary_driver mlx5r_rep_driver = {
.name = "rep",
.probe = mlx5r_rep_probe,
.remove = mlx5r_rep_remove,
.id_table = mlx5r_rep_id_table,
};
int mlx5r_rep_init(void)
{
return auxiliary_driver_register(&mlx5r_rep_driver);
}
void mlx5r_rep_cleanup(void)
{
auxiliary_driver_unregister(&mlx5r_rep_driver);
}
......@@ -12,47 +12,16 @@
extern const struct mlx5_ib_profile raw_eth_profile;
#ifdef CONFIG_MLX5_ESWITCH
u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw);
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
u16 vport_num);
struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw);
struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
u16 vport_num);
void mlx5_ib_register_vport_reps(struct mlx5_core_dev *mdev);
void mlx5_ib_unregister_vport_reps(struct mlx5_core_dev *mdev);
int mlx5r_rep_init(void);
void mlx5r_rep_cleanup(void);
struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq,
u16 port);
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
u16 vport_num);
#else /* CONFIG_MLX5_ESWITCH */
static inline u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw)
{
return MLX5_ESWITCH_NONE;
}
static inline
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
u16 vport_num)
{
return NULL;
}
static inline
struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw)
{
return NULL;
}
static inline
struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
u16 vport_num)
{
return NULL;
}
static inline void mlx5_ib_register_vport_reps(struct mlx5_core_dev *mdev) {}
static inline void mlx5_ib_unregister_vport_reps(struct mlx5_core_dev *mdev) {}
static inline int mlx5r_rep_init(void) { return 0; }
static inline void mlx5r_rep_cleanup(void) {}
static inline
struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq,
......@@ -68,10 +37,4 @@ struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
return NULL;
}
#endif
static inline
struct mlx5_ib_dev *mlx5_ib_rep_to_dev(struct mlx5_eswitch_rep *rep)
{
return rep->rep_data[REP_IB].priv;
}
#endif /* __MLX5_IB_REP_H__ */
......@@ -4593,8 +4593,8 @@ void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
ib_dealloc_device(&dev->ib_dev);
}
void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
const struct mlx5_ib_profile *profile)
int __mlx5_ib_add(struct mlx5_ib_dev *dev,
const struct mlx5_ib_profile *profile)
{
int err;
int i;
......@@ -4610,13 +4610,16 @@ void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
}
dev->ib_active = true;
return dev;
return 0;
err_out:
__mlx5_ib_remove(dev, profile, i);
return NULL;
/* Clean up stages which were initialized */
while (i) {
i--;
if (profile->stage[i].cleanup)
profile->stage[i].cleanup(dev);
}
return -ENOMEM;
}
static const struct mlx5_ib_profile pf_profile = {
......@@ -4739,8 +4742,11 @@ const struct mlx5_ib_profile raw_eth_profile = {
NULL),
};
static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev)
static int mlx5r_mp_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
struct mlx5_ib_multiport_info *mpi;
struct mlx5_ib_dev *dev;
bool bound = false;
......@@ -4748,15 +4754,14 @@ static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev)
mpi = kzalloc(sizeof(*mpi), GFP_KERNEL);
if (!mpi)
return NULL;
return -ENOMEM;
mpi->mdev = mdev;
err = mlx5_query_nic_vport_system_image_guid(mdev,
&mpi->sys_image_guid);
if (err) {
kfree(mpi);
return NULL;
return err;
}
mutex_lock(&mlx5_ib_multiport_mutex);
......@@ -4777,40 +4782,46 @@ static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev)
}
mutex_unlock(&mlx5_ib_multiport_mutex);
return mpi;
dev_set_drvdata(&adev->dev, mpi);
return 0;
}
static void mlx5r_mp_remove(struct auxiliary_device *adev)
{
struct mlx5_ib_multiport_info *mpi;
mpi = dev_get_drvdata(&adev->dev);
mutex_lock(&mlx5_ib_multiport_mutex);
if (mpi->ibdev)
mlx5_ib_unbind_slave_port(mpi->ibdev, mpi);
list_del(&mpi->list);
mutex_unlock(&mlx5_ib_multiport_mutex);
kfree(mpi);
}
static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
static int mlx5r_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
const struct mlx5_ib_profile *profile;
int port_type_cap, num_ports, ret;
enum rdma_link_layer ll;
struct mlx5_ib_dev *dev;
int port_type_cap;
int num_ports;
if (MLX5_ESWITCH_MANAGER(mdev) &&
mlx5_ib_eswitch_mode(mdev->priv.eswitch) == MLX5_ESWITCH_OFFLOADS) {
if (!mlx5_core_mp_enabled(mdev))
mlx5_ib_register_vport_reps(mdev);
return mdev;
}
port_type_cap = MLX5_CAP_GEN(mdev, port_type);
ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap);
if (mlx5_core_is_mp_slave(mdev) && ll == IB_LINK_LAYER_ETHERNET)
return mlx5_ib_add_slave_port(mdev);
num_ports = max(MLX5_CAP_GEN(mdev, num_ports),
MLX5_CAP_GEN(mdev, num_vhca_ports));
dev = ib_alloc_device(mlx5_ib_dev, ib_dev);
if (!dev)
return NULL;
return -ENOMEM;
dev->port = kcalloc(num_ports, sizeof(*dev->port),
GFP_KERNEL);
if (!dev->port) {
ib_dealloc_device(&dev->ib_dev);
return NULL;
return -ENOMEM;
}
dev->mdev = mdev;
......@@ -4821,38 +4832,50 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
else
profile = &pf_profile;
return __mlx5_ib_add(dev, profile);
ret = __mlx5_ib_add(dev, profile);
if (ret) {
kfree(dev->port);
ib_dealloc_device(&dev->ib_dev);
return ret;
}
dev_set_drvdata(&adev->dev, dev);
return 0;
}
static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context)
static void mlx5r_remove(struct auxiliary_device *adev)
{
struct mlx5_ib_multiport_info *mpi;
struct mlx5_ib_dev *dev;
if (MLX5_ESWITCH_MANAGER(mdev) && context == mdev) {
mlx5_ib_unregister_vport_reps(mdev);
return;
}
if (mlx5_core_is_mp_slave(mdev)) {
mpi = context;
mutex_lock(&mlx5_ib_multiport_mutex);
if (mpi->ibdev)
mlx5_ib_unbind_slave_port(mpi->ibdev, mpi);
list_del(&mpi->list);
mutex_unlock(&mlx5_ib_multiport_mutex);
kfree(mpi);
return;
}
dev = context;
dev = dev_get_drvdata(&adev->dev);
__mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
}
static struct mlx5_interface mlx5_ib_interface = {
.add = mlx5_ib_add,
.remove = mlx5_ib_remove,
.protocol = MLX5_INTERFACE_PROTOCOL_IB,
static const struct auxiliary_device_id mlx5r_mp_id_table[] = {
{ .name = MLX5_ADEV_NAME ".multiport", },
{},
};
static const struct auxiliary_device_id mlx5r_id_table[] = {
{ .name = MLX5_ADEV_NAME ".rdma", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, mlx5r_mp_id_table);
MODULE_DEVICE_TABLE(auxiliary, mlx5r_id_table);
static struct auxiliary_driver mlx5r_mp_driver = {
.name = "multiport",
.probe = mlx5r_mp_probe,
.remove = mlx5r_mp_remove,
.id_table = mlx5r_mp_id_table,
};
static struct auxiliary_driver mlx5r_driver = {
.name = "rdma",
.probe = mlx5r_probe,
.remove = mlx5r_remove,
.id_table = mlx5r_id_table,
};
unsigned long mlx5_ib_get_xlt_emergency_page(void)
......@@ -4868,7 +4891,7 @@ void mlx5_ib_put_xlt_emergency_page(void)
static int __init mlx5_ib_init(void)
{
int err;
int ret;
xlt_emergency_page = __get_free_page(GFP_KERNEL);
if (!xlt_emergency_page)
......@@ -4883,15 +4906,33 @@ static int __init mlx5_ib_init(void)
}
mlx5_ib_odp_init();
ret = mlx5r_rep_init();
if (ret)
goto rep_err;
ret = auxiliary_driver_register(&mlx5r_mp_driver);
if (ret)
goto mp_err;
ret = auxiliary_driver_register(&mlx5r_driver);
if (ret)
goto drv_err;
return 0;
err = mlx5_register_interface(&mlx5_ib_interface);
return err;
drv_err:
auxiliary_driver_unregister(&mlx5r_mp_driver);
mp_err:
mlx5r_rep_cleanup();
rep_err:
destroy_workqueue(mlx5_ib_event_wq);
free_page((unsigned long)xlt_emergency_page);
return ret;
}
static void __exit mlx5_ib_cleanup(void)
{
mlx5_unregister_interface(&mlx5_ib_interface);
auxiliary_driver_unregister(&mlx5r_driver);
auxiliary_driver_unregister(&mlx5r_mp_driver);
mlx5r_rep_cleanup();
destroy_workqueue(mlx5_ib_event_wq);
mutex_destroy(&xlt_emergency_page_mutex);
free_page(xlt_emergency_page);
......
......@@ -1317,8 +1317,8 @@ extern const struct mmu_interval_notifier_ops mlx5_mn_ops;
void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
const struct mlx5_ib_profile *profile,
int stage);
void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
const struct mlx5_ib_profile *profile);
int __mlx5_ib_add(struct mlx5_ib_dev *dev,
const struct mlx5_ib_profile *profile);
int mlx5_ib_get_vf_config(struct ib_device *device, int vf,
u8 port, struct ifla_vf_info *info);
......
......@@ -6,6 +6,7 @@
config MLX5_CORE
tristate "Mellanox 5th generation network adapters (ConnectX series) core driver"
depends on PCI
select AUXILIARY_BUS
select NET_DEVLINK
depends on VXLAN || !VXLAN
depends on MLXFW || !MLXFW
......
......@@ -31,313 +31,484 @@
*/
#include <linux/mlx5/driver.h>
#include <linux/mlx5/eswitch.h>
#include <linux/mlx5/mlx5_ifc_vdpa.h>
#include "mlx5_core.h"
static LIST_HEAD(intf_list);
static LIST_HEAD(mlx5_dev_list);
/* intf dev list mutex */
static DEFINE_MUTEX(mlx5_intf_mutex);
static DEFINE_IDA(mlx5_adev_ida);
struct mlx5_device_context {
struct list_head list;
struct mlx5_interface *intf;
void *context;
unsigned long state;
};
static bool is_eth_rep_supported(struct mlx5_core_dev *dev)
{
if (!IS_ENABLED(CONFIG_MLX5_ESWITCH))
return false;
enum {
MLX5_INTERFACE_ADDED,
MLX5_INTERFACE_ATTACHED,
};
if (!MLX5_ESWITCH_MANAGER(dev))
return false;
if (!is_mdev_switchdev_mode(dev))
return false;
void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
return true;
}
static bool is_eth_supported(struct mlx5_core_dev *dev)
{
struct mlx5_device_context *dev_ctx;
struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv);
if (!IS_ENABLED(CONFIG_MLX5_CORE_EN))
return false;
if (!mlx5_lag_intf_add(intf, priv))
return;
if (is_eth_rep_supported(dev))
return false;
dev_ctx = kzalloc(sizeof(*dev_ctx), GFP_KERNEL);
if (!dev_ctx)
return;
if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
return false;
dev_ctx->intf = intf;
if (!MLX5_CAP_GEN(dev, eth_net_offloads)) {
mlx5_core_warn(dev, "Missing eth_net_offloads capability\n");
return false;
}
dev_ctx->context = intf->add(dev);
if (dev_ctx->context) {
set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state);
if (intf->attach)
set_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state);
if (!MLX5_CAP_GEN(dev, nic_flow_table)) {
mlx5_core_warn(dev, "Missing nic_flow_table capability\n");
return false;
}
spin_lock_irq(&priv->ctx_lock);
list_add_tail(&dev_ctx->list, &priv->ctx_list);
spin_unlock_irq(&priv->ctx_lock);
if (!MLX5_CAP_ETH(dev, csum_cap)) {
mlx5_core_warn(dev, "Missing csum_cap capability\n");
return false;
}
if (!dev_ctx->context)
kfree(dev_ctx);
if (!MLX5_CAP_ETH(dev, max_lso_cap)) {
mlx5_core_warn(dev, "Missing max_lso_cap capability\n");
return false;
}
if (!MLX5_CAP_ETH(dev, vlan_cap)) {
mlx5_core_warn(dev, "Missing vlan_cap capability\n");
return false;
}
if (!MLX5_CAP_ETH(dev, rss_ind_tbl_cap)) {
mlx5_core_warn(dev, "Missing rss_ind_tbl_cap capability\n");
return false;
}
if (MLX5_CAP_FLOWTABLE(dev,
flow_table_properties_nic_receive.max_ft_level) < 3) {
mlx5_core_warn(dev, "max_ft_level < 3\n");
return false;
}
if (!MLX5_CAP_ETH(dev, self_lb_en_modifiable))
mlx5_core_warn(dev, "Self loop back prevention is not supported\n");
if (!MLX5_CAP_GEN(dev, cq_moderation))
mlx5_core_warn(dev, "CQ moderation is not supported\n");
return true;
}
static struct mlx5_device_context *mlx5_get_device(struct mlx5_interface *intf,
struct mlx5_priv *priv)
static bool is_vnet_supported(struct mlx5_core_dev *dev)
{
struct mlx5_device_context *dev_ctx;
if (!IS_ENABLED(CONFIG_MLX5_VDPA_NET))
return false;
list_for_each_entry(dev_ctx, &priv->ctx_list, list)
if (dev_ctx->intf == intf)
return dev_ctx;
return NULL;
if (mlx5_core_is_pf(dev))
return false;
if (!(MLX5_CAP_GEN_64(dev, general_obj_types) &
MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q))
return false;
if (!(MLX5_CAP_DEV_VDPA_EMULATION(dev, event_mode) &
MLX5_VIRTIO_Q_EVENT_MODE_QP_MODE))
return false;
if (!MLX5_CAP_DEV_VDPA_EMULATION(dev, eth_frame_offload_type))
return false;
return true;
}
void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
static bool is_ib_rep_supported(struct mlx5_core_dev *dev)
{
struct mlx5_device_context *dev_ctx;
struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv);
if (!IS_ENABLED(CONFIG_MLX5_INFINIBAND))
return false;
dev_ctx = mlx5_get_device(intf, priv);
if (!dev_ctx)
return;
if (dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_IB_ADEV)
return false;
spin_lock_irq(&priv->ctx_lock);
list_del(&dev_ctx->list);
spin_unlock_irq(&priv->ctx_lock);
if (!is_eth_rep_supported(dev))
return false;
if (test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state))
intf->remove(dev, dev_ctx->context);
if (!MLX5_ESWITCH_MANAGER(dev))
return false;
kfree(dev_ctx);
if (!is_mdev_switchdev_mode(dev))
return false;
if (mlx5_core_mp_enabled(dev))
return false;
return true;
}
static void mlx5_attach_interface(struct mlx5_interface *intf, struct mlx5_priv *priv)
static bool is_mp_supported(struct mlx5_core_dev *dev)
{
struct mlx5_device_context *dev_ctx;
struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv);
dev_ctx = mlx5_get_device(intf, priv);
if (!dev_ctx)
return;
if (intf->attach) {
if (test_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state))
return;
if (intf->attach(dev, dev_ctx->context))
return;
set_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state);
} else {
if (test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state))
return;
dev_ctx->context = intf->add(dev);
if (!dev_ctx->context)
return;
set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state);
}
if (!IS_ENABLED(CONFIG_MLX5_INFINIBAND))
return false;
if (dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_IB_ADEV)
return false;
if (is_ib_rep_supported(dev))
return false;
if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
return false;
if (!mlx5_core_is_mp_slave(dev))
return false;
return true;
}
void mlx5_attach_device(struct mlx5_core_dev *dev)
static bool is_ib_supported(struct mlx5_core_dev *dev)
{
struct mlx5_priv *priv = &dev->priv;
struct mlx5_interface *intf;
if (!IS_ENABLED(CONFIG_MLX5_INFINIBAND))
return false;
mutex_lock(&mlx5_intf_mutex);
list_for_each_entry(intf, &intf_list, list)
mlx5_attach_interface(intf, priv);
mutex_unlock(&mlx5_intf_mutex);
if (dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_IB_ADEV)
return false;
if (is_ib_rep_supported(dev))
return false;
if (is_mp_supported(dev))
return false;
return true;
}
enum {
MLX5_INTERFACE_PROTOCOL_ETH_REP,
MLX5_INTERFACE_PROTOCOL_ETH,
MLX5_INTERFACE_PROTOCOL_IB_REP,
MLX5_INTERFACE_PROTOCOL_MPIB,
MLX5_INTERFACE_PROTOCOL_IB,
MLX5_INTERFACE_PROTOCOL_VNET,
};
static const struct mlx5_adev_device {
const char *suffix;
bool (*is_supported)(struct mlx5_core_dev *dev);
} mlx5_adev_devices[] = {
[MLX5_INTERFACE_PROTOCOL_VNET] = { .suffix = "vnet",
.is_supported = &is_vnet_supported },
[MLX5_INTERFACE_PROTOCOL_IB] = { .suffix = "rdma",
.is_supported = &is_ib_supported },
[MLX5_INTERFACE_PROTOCOL_ETH] = { .suffix = "eth",
.is_supported = &is_eth_supported },
[MLX5_INTERFACE_PROTOCOL_ETH_REP] = { .suffix = "eth-rep",
.is_supported = &is_eth_rep_supported },
[MLX5_INTERFACE_PROTOCOL_IB_REP] = { .suffix = "rdma-rep",
.is_supported = &is_ib_rep_supported },
[MLX5_INTERFACE_PROTOCOL_MPIB] = { .suffix = "multiport",
.is_supported = &is_mp_supported },
};
int mlx5_adev_idx_alloc(void)
{
return ida_alloc(&mlx5_adev_ida, GFP_KERNEL);
}
static void mlx5_detach_interface(struct mlx5_interface *intf, struct mlx5_priv *priv)
void mlx5_adev_idx_free(int idx)
{
struct mlx5_device_context *dev_ctx;
struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv);
dev_ctx = mlx5_get_device(intf, priv);
if (!dev_ctx)
return;
if (intf->detach) {
if (!test_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state))
return;
intf->detach(dev, dev_ctx->context);
clear_bit(MLX5_INTERFACE_ATTACHED, &dev_ctx->state);
} else {
if (!test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state))
return;
intf->remove(dev, dev_ctx->context);
clear_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state);
}
ida_free(&mlx5_adev_ida, idx);
}
void mlx5_detach_device(struct mlx5_core_dev *dev)
int mlx5_adev_init(struct mlx5_core_dev *dev)
{
struct mlx5_priv *priv = &dev->priv;
struct mlx5_interface *intf;
mutex_lock(&mlx5_intf_mutex);
list_for_each_entry(intf, &intf_list, list)
mlx5_detach_interface(intf, priv);
mutex_unlock(&mlx5_intf_mutex);
priv->adev = kcalloc(ARRAY_SIZE(mlx5_adev_devices),
sizeof(struct mlx5_adev *), GFP_KERNEL);
if (!priv->adev)
return -ENOMEM;
return 0;
}
bool mlx5_device_registered(struct mlx5_core_dev *dev)
void mlx5_adev_cleanup(struct mlx5_core_dev *dev)
{
struct mlx5_priv *priv;
bool found = false;
struct mlx5_priv *priv = &dev->priv;
mutex_lock(&mlx5_intf_mutex);
list_for_each_entry(priv, &mlx5_dev_list, dev_list)
if (priv == &dev->priv)
found = true;
mutex_unlock(&mlx5_intf_mutex);
kfree(priv->adev);
}
static void adev_release(struct device *dev)
{
struct mlx5_adev *mlx5_adev =
container_of(dev, struct mlx5_adev, adev.dev);
struct mlx5_priv *priv = &mlx5_adev->mdev->priv;
int idx = mlx5_adev->idx;
return found;
kfree(mlx5_adev);
priv->adev[idx] = NULL;
}
void mlx5_register_device(struct mlx5_core_dev *dev)
static struct mlx5_adev *add_adev(struct mlx5_core_dev *dev, int idx)
{
const char *suffix = mlx5_adev_devices[idx].suffix;
struct auxiliary_device *adev;
struct mlx5_adev *madev;
int ret;
madev = kzalloc(sizeof(*madev), GFP_KERNEL);
if (!madev)
return ERR_PTR(-ENOMEM);
adev = &madev->adev;
adev->id = dev->priv.adev_idx;
adev->name = suffix;
adev->dev.parent = dev->device;
adev->dev.release = adev_release;
madev->mdev = dev;
madev->idx = idx;
ret = auxiliary_device_init(adev);
if (ret) {
kfree(madev);
return ERR_PTR(ret);
}
ret = auxiliary_device_add(adev);
if (ret) {
auxiliary_device_uninit(adev);
return ERR_PTR(ret);
}
return madev;
}
static void del_adev(struct auxiliary_device *adev)
{
auxiliary_device_delete(adev);
auxiliary_device_uninit(adev);
}
int mlx5_attach_device(struct mlx5_core_dev *dev)
{
struct mlx5_priv *priv = &dev->priv;
struct mlx5_interface *intf;
struct auxiliary_device *adev;
struct auxiliary_driver *adrv;
int ret = 0, i;
mutex_lock(&mlx5_intf_mutex);
list_add_tail(&priv->dev_list, &mlx5_dev_list);
list_for_each_entry(intf, &intf_list, list)
mlx5_add_device(intf, priv);
for (i = 0; i < ARRAY_SIZE(mlx5_adev_devices); i++) {
if (!priv->adev[i]) {
bool is_supported = false;
if (mlx5_adev_devices[i].is_supported)
is_supported = mlx5_adev_devices[i].is_supported(dev);
if (!is_supported)
continue;
priv->adev[i] = add_adev(dev, i);
if (IS_ERR(priv->adev[i])) {
ret = PTR_ERR(priv->adev[i]);
priv->adev[i] = NULL;
}
} else {
adev = &priv->adev[i]->adev;
adrv = to_auxiliary_drv(adev->dev.driver);
if (adrv->resume)
ret = adrv->resume(adev);
}
if (ret) {
mlx5_core_warn(dev, "Device[%d] (%s) failed to load\n",
i, mlx5_adev_devices[i].suffix);
break;
}
}
mutex_unlock(&mlx5_intf_mutex);
return ret;
}
void mlx5_unregister_device(struct mlx5_core_dev *dev)
void mlx5_detach_device(struct mlx5_core_dev *dev)
{
struct mlx5_priv *priv = &dev->priv;
struct mlx5_interface *intf;
struct auxiliary_device *adev;
struct auxiliary_driver *adrv;
pm_message_t pm = {};
int i;
mutex_lock(&mlx5_intf_mutex);
list_for_each_entry_reverse(intf, &intf_list, list)
mlx5_remove_device(intf, priv);
list_del(&priv->dev_list);
for (i = ARRAY_SIZE(mlx5_adev_devices) - 1; i >= 0; i--) {
if (!priv->adev[i])
continue;
adev = &priv->adev[i]->adev;
adrv = to_auxiliary_drv(adev->dev.driver);
if (adrv->suspend) {
adrv->suspend(adev, pm);
continue;
}
del_adev(&priv->adev[i]->adev);
priv->adev[i] = NULL;
}
mutex_unlock(&mlx5_intf_mutex);
}
int mlx5_register_interface(struct mlx5_interface *intf)
int mlx5_register_device(struct mlx5_core_dev *dev)
{
struct mlx5_priv *priv;
if (!intf->add || !intf->remove)
return -EINVAL;
int ret;
mutex_lock(&mlx5_intf_mutex);
list_add_tail(&intf->list, &intf_list);
list_for_each_entry(priv, &mlx5_dev_list, dev_list)
mlx5_add_device(intf, priv);
dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV;
ret = mlx5_rescan_drivers_locked(dev);
mutex_unlock(&mlx5_intf_mutex);
if (ret)
mlx5_unregister_device(dev);
return 0;
return ret;
}
EXPORT_SYMBOL(mlx5_register_interface);
void mlx5_unregister_interface(struct mlx5_interface *intf)
void mlx5_unregister_device(struct mlx5_core_dev *dev)
{
struct mlx5_priv *priv;
mutex_lock(&mlx5_intf_mutex);
list_for_each_entry(priv, &mlx5_dev_list, dev_list)
mlx5_remove_device(intf, priv);
list_del(&intf->list);
dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV;
mlx5_rescan_drivers_locked(dev);
mutex_unlock(&mlx5_intf_mutex);
}
EXPORT_SYMBOL(mlx5_unregister_interface);
/* Must be called with intf_mutex held */
static bool mlx5_has_added_dev_by_protocol(struct mlx5_core_dev *mdev, int protocol)
static int add_drivers(struct mlx5_core_dev *dev)
{
struct mlx5_device_context *dev_ctx;
struct mlx5_interface *intf;
bool found = false;
list_for_each_entry(intf, &intf_list, list) {
if (intf->protocol == protocol) {
dev_ctx = mlx5_get_device(intf, &mdev->priv);
if (dev_ctx && test_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state))
found = true;
break;
struct mlx5_priv *priv = &dev->priv;
int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(mlx5_adev_devices); i++) {
bool is_supported = false;
if (priv->adev[i])
continue;
if (mlx5_adev_devices[i].is_supported)
is_supported = mlx5_adev_devices[i].is_supported(dev);
if (!is_supported)
continue;
priv->adev[i] = add_adev(dev, i);
if (IS_ERR(priv->adev[i])) {
mlx5_core_warn(dev, "Device[%d] (%s) failed to load\n",
i, mlx5_adev_devices[i].suffix);
/* We continue to rescan drivers and leave to the caller
* to make decision if to release everything or continue.
*/
ret = PTR_ERR(priv->adev[i]);
priv->adev[i] = NULL;
}
}
return found;
return ret;
}
void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol)
static void delete_drivers(struct mlx5_core_dev *dev)
{
mutex_lock(&mlx5_intf_mutex);
if (mlx5_has_added_dev_by_protocol(mdev, protocol)) {
mlx5_remove_dev_by_protocol(mdev, protocol);
mlx5_add_dev_by_protocol(mdev, protocol);
struct mlx5_priv *priv = &dev->priv;
bool delete_all;
int i;
delete_all = priv->flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV;
for (i = ARRAY_SIZE(mlx5_adev_devices) - 1; i >= 0; i--) {
bool is_supported = false;
if (!priv->adev[i])
continue;
if (mlx5_adev_devices[i].is_supported && !delete_all)
is_supported = mlx5_adev_devices[i].is_supported(dev);
if (is_supported)
continue;
del_adev(&priv->adev[i]->adev);
priv->adev[i] = NULL;
}
mutex_unlock(&mlx5_intf_mutex);
}
/* Must be called with intf_mutex held */
void mlx5_add_dev_by_protocol(struct mlx5_core_dev *dev, int protocol)
/* This function is used after mlx5_core_dev is reconfigured.
*/
int mlx5_rescan_drivers_locked(struct mlx5_core_dev *dev)
{
struct mlx5_interface *intf;
struct mlx5_priv *priv = &dev->priv;
list_for_each_entry(intf, &intf_list, list)
if (intf->protocol == protocol) {
mlx5_add_device(intf, &dev->priv);
break;
}
}
lockdep_assert_held(&mlx5_intf_mutex);
/* Must be called with intf_mutex held */
void mlx5_remove_dev_by_protocol(struct mlx5_core_dev *dev, int protocol)
{
struct mlx5_interface *intf;
delete_drivers(dev);
if (priv->flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)
return 0;
list_for_each_entry(intf, &intf_list, list)
if (intf->protocol == protocol) {
mlx5_remove_device(intf, &dev->priv);
break;
}
return add_drivers(dev);
}
static u32 mlx5_gen_pci_id(struct mlx5_core_dev *dev)
static u32 mlx5_gen_pci_id(const struct mlx5_core_dev *dev)
{
return (u32)((pci_domain_nr(dev->pdev->bus) << 16) |
(dev->pdev->bus->number << 8) |
PCI_SLOT(dev->pdev->devfn));
}
/* Must be called with intf_mutex held */
static int next_phys_dev(struct device *dev, const void *data)
{
struct mlx5_adev *madev = container_of(dev, struct mlx5_adev, adev.dev);
struct mlx5_core_dev *mdev = madev->mdev;
const struct mlx5_core_dev *curr = data;
if (!mlx5_core_is_pf(mdev))
return 0;
if (mdev == curr)
return 0;
if (mlx5_gen_pci_id(mdev) != mlx5_gen_pci_id(curr))
return 0;
return 1;
}
/* This function is called with two flows:
* 1. During initialization of mlx5_core_dev and we don't need to lock it.
* 2. During LAG configure stage and caller holds &mlx5_intf_mutex.
*/
struct mlx5_core_dev *mlx5_get_next_phys_dev(struct mlx5_core_dev *dev)
{
struct mlx5_core_dev *res = NULL;
struct mlx5_core_dev *tmp_dev;
struct mlx5_priv *priv;
u32 pci_id;
struct auxiliary_device *adev;
struct mlx5_adev *madev;
if (!mlx5_core_is_pf(dev))
return NULL;
pci_id = mlx5_gen_pci_id(dev);
list_for_each_entry(priv, &mlx5_dev_list, dev_list) {
tmp_dev = container_of(priv, struct mlx5_core_dev, priv);
if (!mlx5_core_is_pf(tmp_dev))
continue;
if ((dev != tmp_dev) && (mlx5_gen_pci_id(tmp_dev) == pci_id)) {
res = tmp_dev;
break;
}
}
adev = auxiliary_find_device(NULL, dev, &next_phys_dev);
if (!adev)
return NULL;
return res;
madev = container_of(adev, struct mlx5_adev, adev);
put_device(&adev->dev);
return madev->mdev;
}
void mlx5_dev_list_lock(void)
{
mutex_lock(&mlx5_intf_mutex);
}
void mlx5_dev_list_unlock(void)
{
mutex_unlock(&mlx5_intf_mutex);
......
......@@ -43,7 +43,7 @@ mlx5_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
u32 running_fw, stored_fw;
int err;
err = devlink_info_driver_name_put(req, DRIVER_NAME);
err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
if (err)
return err;
......@@ -212,7 +212,7 @@ static int mlx5_devlink_fs_mode_validate(struct devlink *devlink, u32 id,
u8 eswitch_mode;
bool smfs_cap;
eswitch_mode = mlx5_eswitch_mode(dev->priv.eswitch);
eswitch_mode = mlx5_eswitch_mode(dev);
smfs_cap = mlx5_fs_dr_is_supported(dev);
if (!smfs_cap) {
......
......@@ -41,9 +41,7 @@ void mlx5e_ethtool_get_drvinfo(struct mlx5e_priv *priv,
{
struct mlx5_core_dev *mdev = priv->mdev;
strlcpy(drvinfo->driver, DRIVER_NAME, sizeof(drvinfo->driver));
strlcpy(drvinfo->version, DRIVER_VERSION,
sizeof(drvinfo->version));
strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
"%d.%d.%04d (%.16s)",
fw_rev_maj(mdev), fw_rev_min(mdev), fw_rev_sub(mdev),
......
......@@ -3112,7 +3112,7 @@ static void mlx5e_modify_admin_state(struct mlx5_core_dev *mdev,
mlx5_set_port_admin_status(mdev, state);
if (!MLX5_ESWITCH_MANAGER(mdev) || mlx5_eswitch_mode(esw) == MLX5_ESWITCH_OFFLOADS)
if (mlx5_eswitch_mode(mdev) != MLX5_ESWITCH_LEGACY)
return;
if (state == MLX5_PORT_UP)
......@@ -4577,31 +4577,6 @@ const struct net_device_ops mlx5e_netdev_ops = {
.ndo_get_devlink_port = mlx5e_get_devlink_port,
};
static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev)
{
if (MLX5_CAP_GEN(mdev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
return -EOPNOTSUPP;
if (!MLX5_CAP_GEN(mdev, eth_net_offloads) ||
!MLX5_CAP_GEN(mdev, nic_flow_table) ||
!MLX5_CAP_ETH(mdev, csum_cap) ||
!MLX5_CAP_ETH(mdev, max_lso_cap) ||
!MLX5_CAP_ETH(mdev, vlan_cap) ||
!MLX5_CAP_ETH(mdev, rss_ind_tbl_cap) ||
MLX5_CAP_FLOWTABLE(mdev,
flow_table_properties_nic_receive.max_ft_level)
< 3) {
mlx5_core_warn(mdev,
"Not creating net device, some required device capabilities are missing\n");
return -EOPNOTSUPP;
}
if (!MLX5_CAP_ETH(mdev, self_lb_en_modifiable))
mlx5_core_warn(mdev, "Self loop back prevention is not supported\n");
if (!MLX5_CAP_GEN(mdev, cq_moderation))
mlx5_core_warn(mdev, "CQ moderation is not supported\n");
return 0;
}
void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len,
int num_channels)
{
......@@ -5421,13 +5396,12 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv)
free_netdev(netdev);
}
/* mlx5e_attach and mlx5e_detach scope should be only creating/destroying
* hardware contexts and to connect it to the current netdev.
*/
static int mlx5e_attach(struct mlx5_core_dev *mdev, void *vpriv)
static int mlx5e_resume(struct auxiliary_device *adev)
{
struct mlx5e_priv *priv = vpriv;
struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
struct mlx5e_priv *priv = dev_get_drvdata(&adev->dev);
struct net_device *netdev = priv->netdev;
struct mlx5_core_dev *mdev = edev->mdev;
int err;
if (netif_device_present(netdev))
......@@ -5446,109 +5420,111 @@ static int mlx5e_attach(struct mlx5_core_dev *mdev, void *vpriv)
return 0;
}
static void mlx5e_detach(struct mlx5_core_dev *mdev, void *vpriv)
static int mlx5e_suspend(struct auxiliary_device *adev, pm_message_t state)
{
struct mlx5e_priv *priv = vpriv;
struct mlx5e_priv *priv = dev_get_drvdata(&adev->dev);
struct net_device *netdev = priv->netdev;
#ifdef CONFIG_MLX5_ESWITCH
if (MLX5_ESWITCH_MANAGER(mdev) && vpriv == mdev)
return;
#endif
struct mlx5_core_dev *mdev = priv->mdev;
if (!netif_device_present(netdev))
return;
return -ENODEV;
mlx5e_detach_netdev(priv);
mlx5e_destroy_mdev_resources(mdev);
return 0;
}
static void *mlx5e_add(struct mlx5_core_dev *mdev)
static int mlx5e_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = edev->mdev;
struct net_device *netdev;
pm_message_t state = {};
void *priv;
int err;
int nch;
err = mlx5e_check_required_hca_cap(mdev);
if (err)
return NULL;
#ifdef CONFIG_MLX5_ESWITCH
if (MLX5_ESWITCH_MANAGER(mdev) &&
mlx5_eswitch_mode(mdev->priv.eswitch) == MLX5_ESWITCH_OFFLOADS) {
mlx5e_rep_register_vport_reps(mdev);
return mdev;
}
#endif
nch = mlx5e_get_max_num_channels(mdev);
netdev = mlx5e_create_netdev(mdev, &mlx5e_nic_profile, nch, NULL);
if (!netdev) {
mlx5_core_err(mdev, "mlx5e_create_netdev failed\n");
return NULL;
return -ENOMEM;
}
dev_net_set(netdev, mlx5_core_net(mdev));
priv = netdev_priv(netdev);
dev_set_drvdata(&adev->dev, priv);
err = mlx5e_attach(mdev, priv);
err = mlx5e_resume(adev);
if (err) {
mlx5_core_err(mdev, "mlx5e_attach failed, %d\n", err);
mlx5_core_err(mdev, "mlx5e_resume failed, %d\n", err);
goto err_destroy_netdev;
}
err = register_netdev(netdev);
if (err) {
mlx5_core_err(mdev, "register_netdev failed, %d\n", err);
goto err_detach;
goto err_resume;
}
mlx5e_devlink_port_type_eth_set(priv);
mlx5e_dcbnl_init_app(priv);
return priv;
return 0;
err_detach:
mlx5e_detach(mdev, priv);
err_resume:
mlx5e_suspend(adev, state);
err_destroy_netdev:
mlx5e_destroy_netdev(priv);
return NULL;
return err;
}
static void mlx5e_remove(struct mlx5_core_dev *mdev, void *vpriv)
static void mlx5e_remove(struct auxiliary_device *adev)
{
struct mlx5e_priv *priv;
struct mlx5e_priv *priv = dev_get_drvdata(&adev->dev);
pm_message_t state = {};
#ifdef CONFIG_MLX5_ESWITCH
if (MLX5_ESWITCH_MANAGER(mdev) && vpriv == mdev) {
mlx5e_rep_unregister_vport_reps(mdev);
return;
}
#endif
priv = vpriv;
mlx5e_dcbnl_delete_app(priv);
unregister_netdev(priv->netdev);
mlx5e_detach(mdev, vpriv);
mlx5e_suspend(adev, state);
mlx5e_destroy_netdev(priv);
}
static struct mlx5_interface mlx5e_interface = {
.add = mlx5e_add,
.remove = mlx5e_remove,
.attach = mlx5e_attach,
.detach = mlx5e_detach,
.protocol = MLX5_INTERFACE_PROTOCOL_ETH,
static const struct auxiliary_device_id mlx5e_id_table[] = {
{ .name = MLX5_ADEV_NAME ".eth", },
{},
};
void mlx5e_init(void)
MODULE_DEVICE_TABLE(auxiliary, mlx5e_id_table);
static struct auxiliary_driver mlx5e_driver = {
.name = "eth",
.probe = mlx5e_probe,
.remove = mlx5e_remove,
.suspend = mlx5e_suspend,
.resume = mlx5e_resume,
.id_table = mlx5e_id_table,
};
int mlx5e_init(void)
{
int ret;
mlx5e_ipsec_build_inverse_table();
mlx5e_build_ptys2ethtool_map();
mlx5_register_interface(&mlx5e_interface);
ret = mlx5e_rep_init();
if (ret)
return ret;
ret = auxiliary_driver_register(&mlx5e_driver);
if (ret)
mlx5e_rep_cleanup();
return ret;
}
void mlx5e_cleanup(void)
{
mlx5_unregister_interface(&mlx5e_interface);
auxiliary_driver_unregister(&mlx5e_driver);
mlx5e_rep_cleanup();
}
......@@ -64,7 +64,6 @@ static void mlx5e_rep_get_drvinfo(struct net_device *dev,
strlcpy(drvinfo->driver, mlx5e_rep_driver_name,
sizeof(drvinfo->driver));
strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
"%d.%d.%04d (%.16s)",
fw_rev_maj(mdev), fw_rev_min(mdev),
......@@ -1316,16 +1315,48 @@ static const struct mlx5_eswitch_rep_ops rep_ops = {
.get_proto_dev = mlx5e_vport_rep_get_proto_dev
};
void mlx5e_rep_register_vport_reps(struct mlx5_core_dev *mdev)
static int mlx5e_rep_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx5_eswitch *esw = mdev->priv.eswitch;
struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = edev->mdev;
struct mlx5_eswitch *esw;
esw = mdev->priv.eswitch;
mlx5_eswitch_register_vport_reps(esw, &rep_ops, REP_ETH);
return 0;
}
void mlx5e_rep_unregister_vport_reps(struct mlx5_core_dev *mdev)
static void mlx5e_rep_remove(struct auxiliary_device *adev)
{
struct mlx5_eswitch *esw = mdev->priv.eswitch;
struct mlx5_adev *vdev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = vdev->mdev;
struct mlx5_eswitch *esw;
esw = mdev->priv.eswitch;
mlx5_eswitch_unregister_vport_reps(esw, REP_ETH);
}
static const struct auxiliary_device_id mlx5e_rep_id_table[] = {
{ .name = MLX5_ADEV_NAME ".eth-rep", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, mlx5e_rep_id_table);
static struct auxiliary_driver mlx5e_rep_driver = {
.name = "eth-rep",
.probe = mlx5e_rep_probe,
.remove = mlx5e_rep_remove,
.id_table = mlx5e_rep_id_table,
};
int mlx5e_rep_init(void)
{
return auxiliary_driver_register(&mlx5e_rep_driver);
}
void mlx5e_rep_cleanup(void)
{
auxiliary_driver_unregister(&mlx5e_rep_driver);
}
......@@ -203,8 +203,8 @@ struct mlx5e_rep_sq {
struct list_head list;
};
void mlx5e_rep_register_vport_reps(struct mlx5_core_dev *mdev);
void mlx5e_rep_unregister_vport_reps(struct mlx5_core_dev *mdev);
int mlx5e_rep_init(void);
void mlx5e_rep_cleanup(void);
int mlx5e_rep_bond_init(struct mlx5e_rep_priv *rpriv);
void mlx5e_rep_bond_cleanup(struct mlx5e_rep_priv *rpriv);
int mlx5e_rep_bond_enslave(struct mlx5_eswitch *esw, struct net_device *netdev,
......@@ -232,6 +232,8 @@ static inline bool mlx5e_eswitch_rep(struct net_device *netdev)
static inline bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) { return false; }
static inline int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) { return 0; }
static inline void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) {}
static inline int mlx5e_rep_init(void) { return 0; };
static inline void mlx5e_rep_cleanup(void) {};
#endif
static inline bool mlx5e_is_vport_rep(struct mlx5e_priv *priv)
......
......@@ -271,8 +271,6 @@ mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev,
return 0;
}
#define esw_offloads_mode(esw) (mlx5_eswitch_mode(esw) == MLX5_ESWITCH_OFFLOADS)
static struct mlx5_tc_ct_priv *
get_ct_priv(struct mlx5e_priv *priv)
{
......@@ -280,7 +278,7 @@ get_ct_priv(struct mlx5e_priv *priv)
struct mlx5_rep_uplink_priv *uplink_priv;
struct mlx5e_rep_priv *uplink_rpriv;
if (esw_offloads_mode(esw)) {
if (is_mdev_switchdev_mode(priv->mdev)) {
uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
uplink_priv = &uplink_rpriv->uplink_priv;
......@@ -297,7 +295,7 @@ mlx5_tc_rule_insert(struct mlx5e_priv *priv,
{
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
if (esw_offloads_mode(esw))
if (is_mdev_switchdev_mode(priv->mdev))
return mlx5_eswitch_add_offloaded_rule(esw, spec, attr);
return mlx5e_add_offloaded_nic_rule(priv, spec, attr);
......@@ -310,7 +308,7 @@ mlx5_tc_rule_delete(struct mlx5e_priv *priv,
{
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
if (esw_offloads_mode(esw)) {
if (is_mdev_switchdev_mode(priv->mdev)) {
mlx5_eswitch_del_offloaded_rule(esw, rule, attr);
return;
......
......@@ -1641,8 +1641,7 @@ int mlx5_eswitch_enable_locked(struct mlx5_eswitch *esw, int mode, int num_vfs)
if (mode == MLX5_ESWITCH_LEGACY) {
err = esw_legacy_enable(esw);
} else {
mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_ETH);
mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
mlx5_rescan_drivers(esw->dev);
err = esw_offloads_enable(esw);
}
......@@ -1660,10 +1659,9 @@ int mlx5_eswitch_enable_locked(struct mlx5_eswitch *esw, int mode, int num_vfs)
abort:
esw->mode = MLX5_ESWITCH_NONE;
if (mode == MLX5_ESWITCH_OFFLOADS) {
mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_ETH);
}
if (mode == MLX5_ESWITCH_OFFLOADS)
mlx5_rescan_drivers(esw->dev);
esw_destroy_tsar(esw);
return err;
}
......@@ -1724,10 +1722,9 @@ void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw, bool clear_vf)
mlx5_lag_update(esw->dev);
if (old_mode == MLX5_ESWITCH_OFFLOADS) {
mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_ETH);
}
if (old_mode == MLX5_ESWITCH_OFFLOADS)
mlx5_rescan_drivers(esw->dev);
esw_destroy_tsar(esw);
if (clear_vf)
......@@ -2466,8 +2463,10 @@ int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
return err;
}
u8 mlx5_eswitch_mode(struct mlx5_eswitch *esw)
u8 mlx5_eswitch_mode(struct mlx5_core_dev *dev)
{
struct mlx5_eswitch *esw = dev->priv.eswitch;
return ESW_ALLOWED(esw) ? esw->mode : MLX5_ESWITCH_NONE;
}
EXPORT_SYMBOL_GPL(mlx5_eswitch_mode);
......
......@@ -39,7 +39,7 @@ static void mlx5i_get_drvinfo(struct net_device *dev,
struct mlx5e_priv *priv = mlx5i_epriv(dev);
mlx5e_ethtool_get_drvinfo(priv, drvinfo);
strlcpy(drvinfo->driver, DRIVER_NAME "[ib_ipoib]",
strlcpy(drvinfo->driver, KBUILD_MODNAME "[ib_ipoib]",
sizeof(drvinfo->driver));
}
......
......@@ -243,24 +243,30 @@ static bool mlx5_lag_check_prereq(struct mlx5_lag *ldev)
#endif
}
static void mlx5_lag_add_ib_devices(struct mlx5_lag *ldev)
static void mlx5_lag_add_devices(struct mlx5_lag *ldev)
{
int i;
for (i = 0; i < MLX5_MAX_PORTS; i++)
if (ldev->pf[i].dev)
mlx5_add_dev_by_protocol(ldev->pf[i].dev,
MLX5_INTERFACE_PROTOCOL_IB);
for (i = 0; i < MLX5_MAX_PORTS; i++) {
if (!ldev->pf[i].dev)
continue;
ldev->pf[i].dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
mlx5_rescan_drivers_locked(ldev->pf[i].dev);
}
}
static void mlx5_lag_remove_ib_devices(struct mlx5_lag *ldev)
static void mlx5_lag_remove_devices(struct mlx5_lag *ldev)
{
int i;
for (i = 0; i < MLX5_MAX_PORTS; i++)
if (ldev->pf[i].dev)
mlx5_remove_dev_by_protocol(ldev->pf[i].dev,
MLX5_INTERFACE_PROTOCOL_IB);
for (i = 0; i < MLX5_MAX_PORTS; i++) {
if (!ldev->pf[i].dev)
continue;
ldev->pf[i].dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
mlx5_rescan_drivers_locked(ldev->pf[i].dev);
}
}
static void mlx5_do_bond(struct mlx5_lag *ldev)
......@@ -290,20 +296,21 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)
#endif
if (roce_lag)
mlx5_lag_remove_ib_devices(ldev);
mlx5_lag_remove_devices(ldev);
err = mlx5_activate_lag(ldev, &tracker,
roce_lag ? MLX5_LAG_FLAG_ROCE :
MLX5_LAG_FLAG_SRIOV);
if (err) {
if (roce_lag)
mlx5_lag_add_ib_devices(ldev);
mlx5_lag_add_devices(ldev);
return;
}
if (roce_lag) {
mlx5_add_dev_by_protocol(dev0, MLX5_INTERFACE_PROTOCOL_IB);
dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
mlx5_rescan_drivers_locked(dev0);
mlx5_nic_vport_enable_roce(dev1);
}
} else if (do_bond && __mlx5_lag_is_active(ldev)) {
......@@ -312,7 +319,8 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)
roce_lag = __mlx5_lag_is_roce(ldev);
if (roce_lag) {
mlx5_remove_dev_by_protocol(dev0, MLX5_INTERFACE_PROTOCOL_IB);
dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
mlx5_rescan_drivers_locked(dev0);
mlx5_nic_vport_disable_roce(dev1);
}
......@@ -321,7 +329,7 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)
return;
if (roce_lag)
mlx5_lag_add_ib_devices(ldev);
mlx5_lag_add_devices(ldev);
}
}
......@@ -596,6 +604,8 @@ void mlx5_lag_add(struct mlx5_core_dev *dev, struct net_device *netdev)
if (err)
mlx5_core_err(dev, "Failed to init multipath lag err=%d\n",
err);
return;
}
/* Must be called with intf_mutex held */
......@@ -739,24 +749,6 @@ u8 mlx5_lag_get_slave_port(struct mlx5_core_dev *dev,
}
EXPORT_SYMBOL(mlx5_lag_get_slave_port);
bool mlx5_lag_intf_add(struct mlx5_interface *intf, struct mlx5_priv *priv)
{
struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev,
priv);
struct mlx5_lag *ldev;
if (intf->protocol != MLX5_INTERFACE_PROTOCOL_IB)
return true;
ldev = mlx5_lag_dev_get(dev);
if (!ldev || !__mlx5_lag_is_roce(ldev) ||
ldev->pf[MLX5_LAG_P1].dev == dev)
return true;
/* If bonded, we do not add an IB device for PF1. */
return false;
}
int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,
u64 *values,
int num_counters,
......
......@@ -50,6 +50,7 @@
#ifdef CONFIG_RFS_ACCEL
#include <linux/cpu_rmap.h>
#endif
#include <linux/version.h>
#include <net/devlink.h>
#include "mlx5_core.h"
#include "lib/eq.h"
......@@ -76,7 +77,6 @@
MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) core driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(DRIVER_VERSION);
unsigned int mlx5_core_debug_mask;
module_param_named(debug_mask, mlx5_core_debug_mask, uint, 0644);
......@@ -227,13 +227,16 @@ static void mlx5_set_driver_version(struct mlx5_core_dev *dev)
strncat(string, ",", remaining_size);
remaining_size = max_t(int, 0, driver_ver_sz - strlen(string));
strncat(string, DRIVER_NAME, remaining_size);
strncat(string, KBUILD_MODNAME, remaining_size);
remaining_size = max_t(int, 0, driver_ver_sz - strlen(string));
strncat(string, ",", remaining_size);
remaining_size = max_t(int, 0, driver_ver_sz - strlen(string));
strncat(string, DRIVER_VERSION, remaining_size);
snprintf(string + strlen(string), remaining_size, "%u.%u.%u",
(u8)((LINUX_VERSION_CODE >> 16) & 0xff), (u8)((LINUX_VERSION_CODE >> 8) & 0xff),
(u16)(LINUX_VERSION_CODE & 0xffff));
/*Send the command*/
MLX5_SET(set_driver_version_in, in, opcode,
......@@ -309,7 +312,7 @@ static int request_bar(struct pci_dev *pdev)
return -ENODEV;
}
err = pci_request_regions(pdev, DRIVER_NAME);
err = pci_request_regions(pdev, KBUILD_MODNAME);
if (err)
dev_err(&pdev->dev, "Couldn't get PCI resources, aborting\n");
......@@ -1219,14 +1222,21 @@ int mlx5_load_one(struct mlx5_core_dev *dev, bool boot)
err = mlx5_devlink_register(priv_to_devlink(dev), dev->device);
if (err)
goto err_devlink_reg;
mlx5_register_device(dev);
err = mlx5_register_device(dev);
} else {
mlx5_attach_device(dev);
err = mlx5_attach_device(dev);
}
if (err)
goto err_register;
mutex_unlock(&dev->intf_state_mutex);
return 0;
err_register:
if (boot)
mlx5_devlink_unregister(priv_to_devlink(dev));
err_devlink_reg:
clear_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state);
mlx5_unload(dev);
......@@ -1303,8 +1313,14 @@ static int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
if (err)
goto err_pagealloc_init;
err = mlx5_adev_init(dev);
if (err)
goto err_adev_init;
return 0;
err_adev_init:
mlx5_pagealloc_cleanup(dev);
err_pagealloc_init:
mlx5_health_cleanup(dev);
err_health_init:
......@@ -1321,6 +1337,7 @@ static void mlx5_mdev_uninit(struct mlx5_core_dev *dev)
{
struct mlx5_priv *priv = &dev->priv;
mlx5_adev_cleanup(dev);
mlx5_pagealloc_cleanup(dev);
mlx5_health_cleanup(dev);
debugfs_remove_recursive(dev->priv.dbg_root);
......@@ -1331,7 +1348,6 @@ static void mlx5_mdev_uninit(struct mlx5_core_dev *dev)
mutex_destroy(&dev->intf_state_mutex);
}
#define MLX5_IB_MOD "mlx5_ib"
static int init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct mlx5_core_dev *dev;
......@@ -1351,6 +1367,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *id)
dev->coredev_type = id->driver_data & MLX5_PCI_DEV_IS_VF ?
MLX5_COREDEV_VF : MLX5_COREDEV_PF;
dev->priv.adev_idx = mlx5_adev_idx_alloc();
if (dev->priv.adev_idx < 0)
return dev->priv.adev_idx;
err = mlx5_mdev_init(dev, prof_sel);
if (err)
goto mdev_init_err;
......@@ -1369,8 +1389,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_load_one;
}
request_module_nowait(MLX5_IB_MOD);
err = mlx5_crdump_enable(dev);
if (err)
dev_err(&pdev->dev, "mlx5_crdump_enable failed with error code %d\n", err);
......@@ -1384,6 +1402,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *id)
pci_init_err:
mlx5_mdev_uninit(dev);
mdev_init_err:
mlx5_adev_idx_free(dev->priv.adev_idx);
mlx5_devlink_free(devlink);
return err;
......@@ -1400,6 +1419,7 @@ static void remove_one(struct pci_dev *pdev)
mlx5_unload_one(dev, true);
mlx5_pci_close(dev);
mlx5_mdev_uninit(dev);
mlx5_adev_idx_free(dev->priv.adev_idx);
mlx5_devlink_free(devlink);
}
......@@ -1614,7 +1634,7 @@ void mlx5_recover_device(struct mlx5_core_dev *dev)
}
static struct pci_driver mlx5_core_driver = {
.name = DRIVER_NAME,
.name = KBUILD_MODNAME,
.id_table = mlx5_core_pci_table,
.probe = init_one,
.remove = remove_one,
......@@ -1640,6 +1660,9 @@ static int __init init(void)
{
int err;
WARN_ONCE(strcmp(MLX5_ADEV_NAME, KBUILD_MODNAME),
"mlx5_core name not in sync with kernel module name");
get_random_bytes(&sw_owner_id, sizeof(sw_owner_id));
mlx5_core_verify_params();
......@@ -1651,7 +1674,11 @@ static int __init init(void)
goto err_debug;
#ifdef CONFIG_MLX5_CORE_EN
mlx5e_init();
err = mlx5e_init();
if (err) {
pci_unregister_driver(&mlx5_core_driver);
goto err_debug;
}
#endif
return 0;
......
......@@ -42,9 +42,6 @@
#include <linux/mlx5/fs.h>
#include <linux/mlx5/driver.h>
#define DRIVER_NAME "mlx5_core"
#define DRIVER_VERSION "5.0-0"
extern uint mlx5_core_debug_mask;
#define mlx5_core_dbg(__dev, format, ...) \
......@@ -185,22 +182,20 @@ void mlx5_events_cleanup(struct mlx5_core_dev *dev);
void mlx5_events_start(struct mlx5_core_dev *dev);
void mlx5_events_stop(struct mlx5_core_dev *dev);
void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv);
void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv);
void mlx5_attach_device(struct mlx5_core_dev *dev);
int mlx5_adev_idx_alloc(void);
void mlx5_adev_idx_free(int idx);
void mlx5_adev_cleanup(struct mlx5_core_dev *dev);
int mlx5_adev_init(struct mlx5_core_dev *dev);
int mlx5_attach_device(struct mlx5_core_dev *dev);
void mlx5_detach_device(struct mlx5_core_dev *dev);
bool mlx5_device_registered(struct mlx5_core_dev *dev);
void mlx5_register_device(struct mlx5_core_dev *dev);
int mlx5_register_device(struct mlx5_core_dev *dev);
void mlx5_unregister_device(struct mlx5_core_dev *dev);
void mlx5_add_dev_by_protocol(struct mlx5_core_dev *dev, int protocol);
void mlx5_remove_dev_by_protocol(struct mlx5_core_dev *dev, int protocol);
struct mlx5_core_dev *mlx5_get_next_phys_dev(struct mlx5_core_dev *dev);
void mlx5_dev_list_lock(void);
void mlx5_dev_list_unlock(void);
int mlx5_dev_list_trylock(void);
bool mlx5_lag_intf_add(struct mlx5_interface *intf, struct mlx5_priv *priv);
int mlx5_query_mtpps(struct mlx5_core_dev *dev, u32 *mtpps, u32 mtpps_size);
int mlx5_set_mtpps(struct mlx5_core_dev *mdev, u32 *mtpps, u32 mtpps_size);
int mlx5_query_mtppse(struct mlx5_core_dev *mdev, u8 pin, u8 *arm, u8 *mode);
......@@ -219,7 +214,7 @@ int mlx5_firmware_flash(struct mlx5_core_dev *dev, const struct firmware *fw,
int mlx5_fw_version_query(struct mlx5_core_dev *dev,
u32 *running_ver, u32 *stored_ver);
void mlx5e_init(void);
int mlx5e_init(void);
void mlx5e_cleanup(void);
static inline bool mlx5_sriov_is_enabled(struct mlx5_core_dev *dev)
......@@ -239,7 +234,17 @@ static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev)
MLX5_CAP_GEN(dev, lag_master);
}
void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol);
int mlx5_rescan_drivers_locked(struct mlx5_core_dev *dev);
static inline int mlx5_rescan_drivers(struct mlx5_core_dev *dev)
{
int ret;
mlx5_dev_list_lock();
ret = mlx5_rescan_drivers_locked(dev);
mlx5_dev_list_unlock();
return ret;
}
void mlx5_lag_update(struct mlx5_core_dev *dev);
enum {
......
subdir-ccflags-y += -I$(srctree)/drivers/vdpa/mlx5/core
obj-$(CONFIG_MLX5_VDPA_NET) += mlx5_vdpa.o
mlx5_vdpa-$(CONFIG_MLX5_VDPA_NET) += net/main.o net/mlx5_vnet.o core/resources.o core/mr.o
mlx5_vdpa-$(CONFIG_MLX5_VDPA_NET) += net/mlx5_vnet.o core/resources.o core/mr.o
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#include <linux/module.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/device.h>
#include "mlx5_vdpa_ifc.h"
#include "mlx5_vnet.h"
MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
MODULE_DESCRIPTION("Mellanox VDPA driver");
MODULE_LICENSE("Dual BSD/GPL");
static bool required_caps_supported(struct mlx5_core_dev *mdev)
{
u8 event_mode;
u64 got;
got = MLX5_CAP_GEN_64(mdev, general_obj_types);
if (!(got & MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q))
return false;
event_mode = MLX5_CAP_DEV_VDPA_EMULATION(mdev, event_mode);
if (!(event_mode & MLX5_VIRTIO_Q_EVENT_MODE_QP_MODE))
return false;
if (!MLX5_CAP_DEV_VDPA_EMULATION(mdev, eth_frame_offload_type))
return false;
return true;
}
static void *mlx5_vdpa_add(struct mlx5_core_dev *mdev)
{
struct mlx5_vdpa_dev *vdev;
if (mlx5_core_is_pf(mdev))
return NULL;
if (!required_caps_supported(mdev)) {
dev_info(mdev->device, "virtio net emulation not supported\n");
return NULL;
}
vdev = mlx5_vdpa_add_dev(mdev);
if (IS_ERR(vdev))
return NULL;
return vdev;
}
static void mlx5_vdpa_remove(struct mlx5_core_dev *mdev, void *context)
{
struct mlx5_vdpa_dev *vdev = context;
mlx5_vdpa_remove_dev(vdev);
}
static struct mlx5_interface mlx5_vdpa_interface = {
.add = mlx5_vdpa_add,
.remove = mlx5_vdpa_remove,
.protocol = MLX5_INTERFACE_PROTOCOL_VDPA,
};
static int __init mlx5_vdpa_init(void)
{
return mlx5_register_interface(&mlx5_vdpa_interface);
}
static void __exit mlx5_vdpa_exit(void)
{
mlx5_unregister_interface(&mlx5_vdpa_interface);
}
module_init(mlx5_vdpa_init);
module_exit(mlx5_vdpa_exit);
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#include <linux/module.h>
#include <linux/vdpa.h>
#include <linux/vringh.h>
#include <uapi/linux/virtio_net.h>
#include <uapi/linux/virtio_ids.h>
#include <linux/virtio_config.h>
#include <linux/auxiliary_bus.h>
#include <linux/mlx5/cq.h>
#include <linux/mlx5/qp.h>
#include <linux/mlx5/device.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/vport.h>
#include <linux/mlx5/fs.h>
#include <linux/mlx5/device.h>
#include "mlx5_vnet.h"
#include "mlx5_vdpa_ifc.h"
#include <linux/mlx5/mlx5_ifc_vdpa.h>
#include "mlx5_vdpa.h"
MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
MODULE_DESCRIPTION("Mellanox VDPA driver");
MODULE_LICENSE("Dual BSD/GPL");
#define to_mlx5_vdpa_ndev(__mvdev) \
container_of(__mvdev, struct mlx5_vdpa_net, mvdev)
#define to_mvdev(__vdev) container_of((__vdev), struct mlx5_vdpa_dev, vdev)
#define VALID_FEATURES_MASK \
......@@ -159,6 +169,11 @@ static bool mlx5_vdpa_debug;
mlx5_vdpa_info(mvdev, "%s\n", #_status); \
} while (0)
static inline u32 mlx5_vdpa_max_qps(int max_vqs)
{
return max_vqs / 2;
}
static void print_status(struct mlx5_vdpa_dev *mvdev, u8 status, bool set)
{
if (status & ~VALID_STATUS_MASK)
......@@ -1928,8 +1943,11 @@ static void init_mvqs(struct mlx5_vdpa_net *ndev)
}
}
void *mlx5_vdpa_add_dev(struct mlx5_core_dev *mdev)
static int mlx5v_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx5_adev *madev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = madev->mdev;
struct virtio_net_config *config;
struct mlx5_vdpa_dev *mvdev;
struct mlx5_vdpa_net *ndev;
......@@ -1943,7 +1961,7 @@ void *mlx5_vdpa_add_dev(struct mlx5_core_dev *mdev)
ndev = vdpa_alloc_device(struct mlx5_vdpa_net, mvdev.vdev, mdev->device, &mlx5_vdpa_ops,
2 * mlx5_vdpa_max_qps(max_vqs));
if (IS_ERR(ndev))
return ndev;
return PTR_ERR(ndev);
ndev->mvdev.max_vqs = max_vqs;
mvdev = &ndev->mvdev;
......@@ -1972,7 +1990,8 @@ void *mlx5_vdpa_add_dev(struct mlx5_core_dev *mdev)
if (err)
goto err_reg;
return ndev;
dev_set_drvdata(&adev->dev, ndev);
return 0;
err_reg:
free_resources(ndev);
......@@ -1981,10 +2000,28 @@ void *mlx5_vdpa_add_dev(struct mlx5_core_dev *mdev)
err_mtu:
mutex_destroy(&ndev->reslock);
put_device(&mvdev->vdev.dev);
return ERR_PTR(err);
return err;
}
void mlx5_vdpa_remove_dev(struct mlx5_vdpa_dev *mvdev)
static void mlx5v_remove(struct auxiliary_device *adev)
{
struct mlx5_vdpa_dev *mvdev = dev_get_drvdata(&adev->dev);
vdpa_unregister_device(&mvdev->vdev);
}
static const struct auxiliary_device_id mlx5v_id_table[] = {
{ .name = MLX5_ADEV_NAME ".vnet", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, mlx5v_id_table);
static struct auxiliary_driver mlx5v_driver = {
.name = "vnet",
.probe = mlx5v_probe,
.remove = mlx5v_remove,
.id_table = mlx5v_id_table,
};
module_auxiliary_driver(mlx5v_driver);
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#ifndef __MLX5_VNET_H_
#define __MLX5_VNET_H_
#include <linux/vdpa.h>
#include <linux/virtio_net.h>
#include <linux/vringh.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/cq.h>
#include <linux/mlx5/qp.h>
#include "mlx5_vdpa.h"
static inline u32 mlx5_vdpa_max_qps(int max_vqs)
{
return max_vqs / 2;
}
#define to_mlx5_vdpa_ndev(__mvdev) container_of(__mvdev, struct mlx5_vdpa_net, mvdev)
void *mlx5_vdpa_add_dev(struct mlx5_core_dev *mdev);
void mlx5_vdpa_remove_dev(struct mlx5_vdpa_dev *mvdev);
#endif /* __MLX5_VNET_H_ */
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019-2020 Intel Corporation
*
* Please see Documentation/driver-api/auxiliary_bus.rst for more information.
*/
#ifndef _AUXILIARY_BUS_H_
#define _AUXILIARY_BUS_H_
#include <linux/device.h>
#include <linux/mod_devicetable.h>
struct auxiliary_device {
struct device dev;
const char *name;
u32 id;
};
struct auxiliary_driver {
int (*probe)(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id);
void (*remove)(struct auxiliary_device *auxdev);
void (*shutdown)(struct auxiliary_device *auxdev);
int (*suspend)(struct auxiliary_device *auxdev, pm_message_t state);
int (*resume)(struct auxiliary_device *auxdev);
const char *name;
struct device_driver driver;
const struct auxiliary_device_id *id_table;
};
static inline struct auxiliary_device *to_auxiliary_dev(struct device *dev)
{
return container_of(dev, struct auxiliary_device, dev);
}
static inline struct auxiliary_driver *to_auxiliary_drv(struct device_driver *drv)
{
return container_of(drv, struct auxiliary_driver, driver);
}
int auxiliary_device_init(struct auxiliary_device *auxdev);
int __auxiliary_device_add(struct auxiliary_device *auxdev, const char *modname);
#define auxiliary_device_add(auxdev) __auxiliary_device_add(auxdev, KBUILD_MODNAME)
static inline void auxiliary_device_uninit(struct auxiliary_device *auxdev)
{
put_device(&auxdev->dev);
}
static inline void auxiliary_device_delete(struct auxiliary_device *auxdev)
{
device_del(&auxdev->dev);
}
int __auxiliary_driver_register(struct auxiliary_driver *auxdrv, struct module *owner,
const char *modname);
#define auxiliary_driver_register(auxdrv) \
__auxiliary_driver_register(auxdrv, THIS_MODULE, KBUILD_MODNAME)
void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv);
/**
* module_auxiliary_driver() - Helper macro for registering an auxiliary driver
* @__auxiliary_driver: auxiliary driver struct
*
* Helper macro for auxiliary drivers which do not do anything special in
* module init/exit. This eliminates a lot of boilerplate. Each module may only
* use this macro once, and calling it replaces module_init() and module_exit()
*/
#define module_auxiliary_driver(__auxiliary_driver) \
module_driver(__auxiliary_driver, auxiliary_driver_register, auxiliary_driver_unregister)
struct auxiliary_device *auxiliary_find_device(struct device *start,
const void *data,
int (*match)(struct device *dev, const void *data));
#endif /* _AUXILIARY_BUS_H_ */
......@@ -48,6 +48,7 @@
#include <linux/idr.h>
#include <linux/notifier.h>
#include <linux/refcount.h>
#include <linux/auxiliary_bus.h>
#include <linux/mlx5/device.h>
#include <linux/mlx5/doorbell.h>
......@@ -56,6 +57,8 @@
#include <linux/ptp_clock_kernel.h>
#include <net/devlink.h>
#define MLX5_ADEV_NAME "mlx5_core"
enum {
MLX5_BOARD_ID_LEN = 64,
};
......@@ -534,6 +537,17 @@ struct mlx5_core_roce {
struct mlx5_flow_handle *allow_rule;
};
enum {
MLX5_PRIV_FLAGS_DISABLE_IB_ADEV = 1 << 0,
MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV = 1 << 1,
};
struct mlx5_adev {
struct auxiliary_device adev;
struct mlx5_core_dev *mdev;
int idx;
};
struct mlx5_priv {
/* IRQ table valid only for real pci devices PF or VF */
struct mlx5_irq_table *irq_table;
......@@ -571,6 +585,8 @@ struct mlx5_priv {
struct list_head dev_list;
struct list_head ctx_list;
spinlock_t ctx_lock;
struct mlx5_adev **adev;
int adev_idx;
struct mlx5_events *events;
struct mlx5_flow_steering *steering;
......@@ -578,6 +594,7 @@ struct mlx5_priv {
struct mlx5_eswitch *eswitch;
struct mlx5_core_sriov sriov;
struct mlx5_lag *lag;
u32 flags;
struct mlx5_devcom *devcom;
struct mlx5_fw_reset *fw_reset;
struct mlx5_core_roce roce;
......@@ -1055,23 +1072,6 @@ enum {
MAX_MR_CACHE_ENTRIES
};
enum {
MLX5_INTERFACE_PROTOCOL_IB = 0,
MLX5_INTERFACE_PROTOCOL_ETH = 1,
MLX5_INTERFACE_PROTOCOL_VDPA = 2,
};
struct mlx5_interface {
void * (*add)(struct mlx5_core_dev *dev);
void (*remove)(struct mlx5_core_dev *dev, void *context);
int (*attach)(struct mlx5_core_dev *dev, void *context);
void (*detach)(struct mlx5_core_dev *dev, void *context);
int protocol;
struct list_head list;
};
int mlx5_register_interface(struct mlx5_interface *intf);
void mlx5_unregister_interface(struct mlx5_interface *intf);
int mlx5_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb);
int mlx5_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb);
int mlx5_eq_notifier_register(struct mlx5_core_dev *dev, struct mlx5_nb *nb);
......
......@@ -96,10 +96,10 @@ static inline u32 mlx5_eswitch_get_vport_metadata_mask(void)
u32 mlx5_eswitch_get_vport_metadata_for_match(struct mlx5_eswitch *esw,
u16 vport_num);
u8 mlx5_eswitch_mode(struct mlx5_eswitch *esw);
u8 mlx5_eswitch_mode(struct mlx5_core_dev *dev);
#else /* CONFIG_MLX5_ESWITCH */
static inline u8 mlx5_eswitch_mode(struct mlx5_eswitch *esw)
static inline u8 mlx5_eswitch_mode(struct mlx5_core_dev *dev)
{
return MLX5_ESWITCH_NONE;
}
......@@ -136,4 +136,8 @@ mlx5_eswitch_get_vport_metadata_mask(void)
}
#endif /* CONFIG_MLX5_ESWITCH */
static inline bool is_mdev_switchdev_mode(struct mlx5_core_dev *dev)
{
return mlx5_eswitch_mode(dev) == MLX5_ESWITCH_OFFLOADS;
}
#endif
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#ifndef __MLX5_VDPA_IFC_H_
#define __MLX5_VDPA_IFC_H_
#include <linux/mlx5/mlx5_ifc.h>
#ifndef __MLX5_IFC_VDPA_H_
#define __MLX5_IFC_VDPA_H_
enum {
MLX5_VIRTIO_Q_EVENT_MODE_NO_MSIX_MODE = 0x0,
......@@ -165,4 +163,4 @@ struct mlx5_ifc_modify_virtio_net_q_out_bits {
struct mlx5_ifc_general_obj_out_cmd_hdr_bits general_obj_out_cmd_hdr;
};
#endif /* __MLX5_VDPA_IFC_H_ */
#endif /* __MLX5_IFC_VDPA_H_ */
......@@ -838,4 +838,12 @@ struct mhi_device_id {
kernel_ulong_t driver_data;
};
#define AUXILIARY_NAME_SIZE 32
#define AUXILIARY_MODULE_PREFIX "auxiliary:"
struct auxiliary_device_id {
char name[AUXILIARY_NAME_SIZE];
kernel_ulong_t driver_data;
};
#endif /* LINUX_MOD_DEVICETABLE_H */
......@@ -243,5 +243,8 @@ int main(void)
DEVID(mhi_device_id);
DEVID_FIELD(mhi_device_id, chan);
DEVID(auxiliary_device_id);
DEVID_FIELD(auxiliary_device_id, name);
return 0;
}
......@@ -1364,6 +1364,13 @@ static int do_mhi_entry(const char *filename, void *symval, char *alias)
{
DEF_FIELD_ADDR(symval, mhi_device_id, chan);
sprintf(alias, MHI_DEVICE_MODALIAS_FMT, *chan);
return 1;
}
static int do_auxiliary_entry(const char *filename, void *symval, char *alias)
{
DEF_FIELD_ADDR(symval, auxiliary_device_id, name);
sprintf(alias, AUXILIARY_MODULE_PREFIX "%s", *name);
return 1;
}
......@@ -1442,6 +1449,7 @@ static const struct devtable devtable[] = {
{"tee", SIZE_tee_client_device_id, do_tee_entry},
{"wmi", SIZE_wmi_device_id, do_wmi_entry},
{"mhi", SIZE_mhi_device_id, do_mhi_entry},
{"auxiliary", SIZE_auxiliary_device_id, do_auxiliary_entry},
};
/* Create MODULE_ALIAS() statements.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment