Commit 3de1484b authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'mlxsw-introduce-modular-system-support-by-minimal-driver'

Petr Machata says:

====================
mlxsw: Introduce modular system support by minimal driver

Vadim Pasternak writes:

This patchset adds line cards support in mlxsw_minimal, which is used
for monitoring purposes on BMC systems. The BMC is connected to the
ASIC over I2C bus, unlike the host CPU that is connected to the ASIC
via PCI bus.

The BMC system needs to be notified whenever line cards become active
or inactive, so that, for example, netdevs will be registered /
unregistered by mlxsw_minimal. However, traps cannot be generated
towards the BMC over the I2C bus. To overcome that, the I2C bus driver
(i.e., mlxsw_i2c) registers an handler for an IRQ that is fired upon
specific system wide changes, like line card activation and
deactivation.

The generated event is handled by mlxsw_core, which checks whether
anything changed in the state of available line cards. If a line card
becomes active or inactive, interested parties such as mlxsw_minimal
are notified via their registered line card event callback.

Patch set overview:

Patches #1 is preparations.

Patches #2-#3 extend mlxsw_core with an infrastructure to handle the
	previously mentioned system events.

Patch #4 extends the I2C bus driver to register an handler for the IRQ
	fired upon specific system wide changes.

Patches #5-#8 gradually add line cards support in mlxsw_minimal by
	dynamically registering / unregistering netdevs for ports found on
	line cards, whenever a line card becomes active / inactive.
====================

Link: https://lore.kernel.org/r/cover.1661093502.git.petrm@nvidia.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 510156a7 706ddb78
......@@ -70,6 +70,8 @@ struct mlxsw_core {
struct workqueue_struct *emad_wq;
struct list_head rx_listener_list;
struct list_head event_listener_list;
struct list_head irq_event_handler_list;
struct mutex irq_event_handler_lock; /* Locks access to handlers list */
struct {
atomic64_t tid;
struct list_head trans_list;
......@@ -2090,6 +2092,18 @@ static void mlxsw_core_health_fini(struct mlxsw_core *mlxsw_core)
devlink_health_reporter_destroy(mlxsw_core->health.fw_fatal);
}
static void mlxsw_core_irq_event_handler_init(struct mlxsw_core *mlxsw_core)
{
INIT_LIST_HEAD(&mlxsw_core->irq_event_handler_list);
mutex_init(&mlxsw_core->irq_event_handler_lock);
}
static void mlxsw_core_irq_event_handler_fini(struct mlxsw_core *mlxsw_core)
{
mutex_destroy(&mlxsw_core->irq_event_handler_lock);
WARN_ON(!list_empty(&mlxsw_core->irq_event_handler_list));
}
static int
__mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
const struct mlxsw_bus *mlxsw_bus,
......@@ -2125,6 +2139,7 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
mlxsw_core->bus = mlxsw_bus;
mlxsw_core->bus_priv = bus_priv;
mlxsw_core->bus_info = mlxsw_bus_info;
mlxsw_core_irq_event_handler_init(mlxsw_core);
err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile,
&mlxsw_core->res);
......@@ -2233,6 +2248,7 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
err_register_resources:
mlxsw_bus->fini(bus_priv);
err_bus_init:
mlxsw_core_irq_event_handler_fini(mlxsw_core);
if (!reload) {
devl_unlock(devlink);
devlink_free(devlink);
......@@ -2302,6 +2318,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
if (!reload)
devl_resources_unregister(devlink);
mlxsw_core->bus->fini(mlxsw_core->bus_priv);
mlxsw_core_irq_event_handler_fini(mlxsw_core);
if (!reload) {
devl_unlock(devlink);
devlink_free(devlink);
......@@ -2772,6 +2789,57 @@ int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list)
}
EXPORT_SYMBOL(mlxsw_reg_trans_bulk_wait);
struct mlxsw_core_irq_event_handler_item {
struct list_head list;
void (*cb)(struct mlxsw_core *mlxsw_core);
};
int mlxsw_core_irq_event_handler_register(struct mlxsw_core *mlxsw_core,
mlxsw_irq_event_cb_t cb)
{
struct mlxsw_core_irq_event_handler_item *item;
item = kzalloc(sizeof(*item), GFP_KERNEL);
if (!item)
return -ENOMEM;
item->cb = cb;
mutex_lock(&mlxsw_core->irq_event_handler_lock);
list_add_tail(&item->list, &mlxsw_core->irq_event_handler_list);
mutex_unlock(&mlxsw_core->irq_event_handler_lock);
return 0;
}
EXPORT_SYMBOL(mlxsw_core_irq_event_handler_register);
void mlxsw_core_irq_event_handler_unregister(struct mlxsw_core *mlxsw_core,
mlxsw_irq_event_cb_t cb)
{
struct mlxsw_core_irq_event_handler_item *item, *tmp;
mutex_lock(&mlxsw_core->irq_event_handler_lock);
list_for_each_entry_safe(item, tmp,
&mlxsw_core->irq_event_handler_list, list) {
if (item->cb == cb) {
list_del(&item->list);
kfree(item);
}
}
mutex_unlock(&mlxsw_core->irq_event_handler_lock);
}
EXPORT_SYMBOL(mlxsw_core_irq_event_handler_unregister);
void mlxsw_core_irq_event_handlers_call(struct mlxsw_core *mlxsw_core)
{
struct mlxsw_core_irq_event_handler_item *item;
mutex_lock(&mlxsw_core->irq_event_handler_lock);
list_for_each_entry(item, &mlxsw_core->irq_event_handler_list, list) {
if (item->cb)
item->cb(mlxsw_core);
}
mutex_unlock(&mlxsw_core->irq_event_handler_lock);
}
EXPORT_SYMBOL(mlxsw_core_irq_event_handlers_call);
static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core,
const struct mlxsw_reg_info *reg,
char *payload,
......
......@@ -215,6 +215,14 @@ int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core,
mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv);
int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list);
typedef void mlxsw_irq_event_cb_t(struct mlxsw_core *mlxsw_core);
int mlxsw_core_irq_event_handler_register(struct mlxsw_core *mlxsw_core,
mlxsw_irq_event_cb_t cb);
void mlxsw_core_irq_event_handler_unregister(struct mlxsw_core *mlxsw_core,
mlxsw_irq_event_cb_t cb);
void mlxsw_core_irq_event_handlers_call(struct mlxsw_core *mlxsw_core);
int mlxsw_reg_query(struct mlxsw_core *mlxsw_core,
const struct mlxsw_reg_info *reg, char *payload);
int mlxsw_reg_write(struct mlxsw_core *mlxsw_core,
......
......@@ -785,6 +785,21 @@ static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,
return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);
}
static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core)
{
struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
int i;
/* Handle change of line card active state. */
for (i = 0; i < linecards->count; i++) {
struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards,
i + 1);
mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
linecard);
}
}
static const char * const mlxsw_linecard_status_event_type_name[] = {
[MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",
[MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",
......@@ -1238,7 +1253,6 @@ static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
{
struct devlink_linecard *devlink_linecard;
struct mlxsw_linecard *linecard;
int err;
linecard = mlxsw_linecard_get(linecards, slot_index);
linecard->slot_index = slot_index;
......@@ -1248,17 +1262,45 @@ static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core),
slot_index, &mlxsw_linecard_ops,
linecard);
if (IS_ERR(devlink_linecard)) {
err = PTR_ERR(devlink_linecard);
goto err_devlink_linecard_create;
}
if (IS_ERR(devlink_linecard))
return PTR_ERR(devlink_linecard);
linecard->devlink_linecard = devlink_linecard;
INIT_DELAYED_WORK(&linecard->status_event_to_dw,
&mlxsw_linecard_status_event_to_work);
return 0;
}
static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
struct mlxsw_linecards *linecards,
u8 slot_index)
{
struct mlxsw_linecard *linecard;
linecard = mlxsw_linecard_get(linecards, slot_index);
cancel_delayed_work_sync(&linecard->status_event_to_dw);
/* Make sure all scheduled events are processed */
mlxsw_core_flush_owq();
if (linecard->active)
mlxsw_linecard_active_clear(linecard);
mlxsw_linecard_bdev_del(linecard);
devlink_linecard_destroy(linecard->devlink_linecard);
mutex_destroy(&linecard->lock);
}
static int
mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core,
struct mlxsw_linecards *linecards,
u8 slot_index)
{
struct mlxsw_linecard *linecard;
int err;
linecard = mlxsw_linecard_get(linecards, slot_index);
err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
if (err)
goto err_event_delivery_set;
return err;
err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
linecard);
......@@ -1269,29 +1311,18 @@ static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
err_status_get_and_process:
mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
err_event_delivery_set:
devlink_linecard_destroy(linecard->devlink_linecard);
err_devlink_linecard_create:
mutex_destroy(&linecard->lock);
return err;
}
static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
struct mlxsw_linecards *linecards,
u8 slot_index)
static void
mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core,
struct mlxsw_linecards *linecards,
u8 slot_index)
{
struct mlxsw_linecard *linecard;
linecard = mlxsw_linecard_get(linecards, slot_index);
mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
cancel_delayed_work_sync(&linecard->status_event_to_dw);
/* Make sure all scheduled events are processed */
mlxsw_core_flush_owq();
if (linecard->active)
mlxsw_linecard_active_clear(linecard);
mlxsw_linecard_bdev_del(linecard);
devlink_linecard_destroy(linecard->devlink_linecard);
mutex_destroy(&linecard->lock);
}
/* LINECARDS INI BUNDLE FILE
......@@ -1505,6 +1536,11 @@ int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
if (err)
goto err_traps_register;
err = mlxsw_core_irq_event_handler_register(mlxsw_core,
mlxsw_linecards_irq_event_handler);
if (err)
goto err_irq_event_handler_register;
mlxsw_core_linecards_set(mlxsw_core, linecards);
for (i = 0; i < linecards->count; i++) {
......@@ -1513,11 +1549,25 @@ int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
goto err_linecard_init;
}
for (i = 0; i < linecards->count; i++) {
err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards,
i + 1);
if (err)
goto err_linecard_event_delivery_init;
}
return 0;
err_linecard_event_delivery_init:
for (i--; i >= 0; i--)
mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
i = linecards->count;
err_linecard_init:
for (i--; i >= 0; i--)
mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
mlxsw_core_irq_event_handler_unregister(mlxsw_core,
mlxsw_linecards_irq_event_handler);
err_irq_event_handler_register:
mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
ARRAY_SIZE(mlxsw_linecard_listener),
mlxsw_core);
......@@ -1535,8 +1585,12 @@ void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
if (!linecards)
return;
for (i = 0; i < linecards->count; i++)
mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
for (i = 0; i < linecards->count; i++)
mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
mlxsw_core_irq_event_handler_unregister(mlxsw_core,
mlxsw_linecards_irq_event_handler);
mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
ARRAY_SIZE(mlxsw_linecard_listener),
mlxsw_core);
......
......@@ -9,6 +9,7 @@
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_data/mlxreg.h>
#include <linux/slab.h>
#include "cmd.h"
......@@ -51,6 +52,15 @@
#define MLXSW_I2C_TIMEOUT_MSECS 5000
#define MLXSW_I2C_MAX_DATA_SIZE 256
/* Driver can be initialized by kernel platform driver or from the user
* space. In the first case IRQ line number is passed through the platform
* data, otherwise default IRQ line is to be used. Default IRQ is relevant
* only for specific I2C slave address, allowing 3.4 MHz I2C path to the chip
* (special hardware feature for I2C acceleration).
*/
#define MLXSW_I2C_DEFAULT_IRQ 17
#define MLXSW_FAST_I2C_SLAVE 0x37
/**
* struct mlxsw_i2c - device private data:
* @cmd: command attributes;
......@@ -63,6 +73,9 @@
* @core: switch core pointer;
* @bus_info: bus info block;
* @block_size: maximum block size allowed to pass to under layer;
* @pdata: device platform data;
* @irq_work: interrupts work item;
* @irq: IRQ line number;
*/
struct mlxsw_i2c {
struct {
......@@ -76,6 +89,9 @@ struct mlxsw_i2c {
struct mlxsw_core *core;
struct mlxsw_bus_info bus_info;
u16 block_size;
struct mlxreg_core_hotplug_platform_data *pdata;
struct work_struct irq_work;
int irq;
};
#define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \
......@@ -546,6 +562,67 @@ static void mlxsw_i2c_fini(void *bus_priv)
mlxsw_i2c->core = NULL;
}
static void mlxsw_i2c_work_handler(struct work_struct *work)
{
struct mlxsw_i2c *mlxsw_i2c;
mlxsw_i2c = container_of(work, struct mlxsw_i2c, irq_work);
mlxsw_core_irq_event_handlers_call(mlxsw_i2c->core);
}
static irqreturn_t mlxsw_i2c_irq_handler(int irq, void *dev)
{
struct mlxsw_i2c *mlxsw_i2c = dev;
mlxsw_core_schedule_work(&mlxsw_i2c->irq_work);
/* Interrupt handler shares IRQ line with 'main' interrupt handler.
* Return here IRQ_NONE, while main handler will return IRQ_HANDLED.
*/
return IRQ_NONE;
}
static int mlxsw_i2c_irq_init(struct mlxsw_i2c *mlxsw_i2c, u8 addr)
{
int err;
/* Initialize interrupt handler if system hotplug driver is reachable,
* otherwise interrupt line is not enabled and interrupts will not be
* raised to CPU. Also request_irq() call will be not valid.
*/
if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG))
return 0;
/* Set default interrupt line. */
if (mlxsw_i2c->pdata && mlxsw_i2c->pdata->irq)
mlxsw_i2c->irq = mlxsw_i2c->pdata->irq;
else if (addr == MLXSW_FAST_I2C_SLAVE)
mlxsw_i2c->irq = MLXSW_I2C_DEFAULT_IRQ;
if (!mlxsw_i2c->irq)
return 0;
INIT_WORK(&mlxsw_i2c->irq_work, mlxsw_i2c_work_handler);
err = request_irq(mlxsw_i2c->irq, mlxsw_i2c_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_SHARED, "mlxsw-i2c",
mlxsw_i2c);
if (err) {
dev_err(mlxsw_i2c->bus_info.dev, "Failed to request irq: %d\n",
err);
return err;
}
return 0;
}
static void mlxsw_i2c_irq_fini(struct mlxsw_i2c *mlxsw_i2c)
{
if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG) || !mlxsw_i2c->irq)
return;
cancel_work_sync(&mlxsw_i2c->irq_work);
free_irq(mlxsw_i2c->irq, mlxsw_i2c);
}
static const struct mlxsw_bus mlxsw_i2c_bus = {
.kind = "i2c",
.init = mlxsw_i2c_init,
......@@ -638,17 +715,24 @@ static int mlxsw_i2c_probe(struct i2c_client *client,
mlxsw_i2c->bus_info.dev = &client->dev;
mlxsw_i2c->bus_info.low_frequency = true;
mlxsw_i2c->dev = &client->dev;
mlxsw_i2c->pdata = client->dev.platform_data;
err = mlxsw_i2c_irq_init(mlxsw_i2c, client->addr);
if (err)
goto errout;
err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info,
&mlxsw_i2c_bus, mlxsw_i2c, false,
NULL, NULL);
if (err) {
dev_err(&client->dev, "Fail to register core bus\n");
return err;
goto err_bus_device_register;
}
return 0;
err_bus_device_register:
mlxsw_i2c_irq_fini(mlxsw_i2c);
errout:
mutex_destroy(&mlxsw_i2c->cmd.lock);
i2c_set_clientdata(client, NULL);
......@@ -661,6 +745,7 @@ static int mlxsw_i2c_remove(struct i2c_client *client)
struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
mlxsw_core_bus_device_unregister(mlxsw_i2c->core, false);
mlxsw_i2c_irq_fini(mlxsw_i2c);
mutex_destroy(&mlxsw_i2c->cmd.lock);
return 0;
......
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