Contributors: 17
Author Tokens Token Proportion Commits Commit Proportion
Parav Pandit 632 50.16% 3 10.71%
Zhang Wei 208 16.51% 2 7.14%
Saeed Mahameed 116 9.21% 6 21.43%
Eli Cohen 111 8.81% 2 7.14%
Aya Levin 52 4.13% 1 3.57%
Cosmin Ratiu 46 3.65% 1 3.57%
Yuval Avnery 38 3.02% 2 7.14%
Daniel Jurgens 17 1.35% 2 7.14%
Huy Nguyen 15 1.19% 1 3.57%
Jack Morgenstein 6 0.48% 1 3.57%
Shay Drory 5 0.40% 1 3.57%
Leon Romanovsky 4 0.32% 1 3.57%
Bodong Wang 3 0.24% 1 3.57%
Yonatan Cohen 2 0.16% 1 3.57%
Maor Gottlieb 2 0.16% 1 3.57%
Achiad Shochat 2 0.16% 1 3.57%
Tariq Toukan 1 0.08% 1 3.57%
Total 1260 28


// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2020 Mellanox Technologies Ltd */

#include <linux/mlx5/driver.h>
#include "mlx5_ifc_vhca_event.h"
#include "mlx5_core.h"
#include "vhca_event.h"
#include "ecpf.h"
#define CREATE_TRACE_POINTS
#include "diag/vhca_tracepoint.h"

struct mlx5_vhca_event_work {
	struct work_struct work;
	struct mlx5_core_dev *dev;
	struct mlx5_vhca_state_event event;
};

struct mlx5_vhca_event_handler {
	struct workqueue_struct *wq;
};

struct mlx5_vhca_events {
	struct mlx5_core_dev *dev;
	struct mlx5_vhca_event_handler handler[MLX5_DEV_MAX_WQS];
};

int mlx5_cmd_query_vhca_state(struct mlx5_core_dev *dev, u16 function_id, u32 *out, u32 outlen)
{
	u32 in[MLX5_ST_SZ_DW(query_vhca_state_in)] = {};

	MLX5_SET(query_vhca_state_in, in, opcode, MLX5_CMD_OP_QUERY_VHCA_STATE);
	MLX5_SET(query_vhca_state_in, in, function_id, function_id);
	MLX5_SET(query_vhca_state_in, in, embedded_cpu_function, 0);

	return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
}

static int mlx5_cmd_modify_vhca_state(struct mlx5_core_dev *dev, u16 function_id,
				      u32 *in, u32 inlen)
{
	u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {};

	MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE);
	MLX5_SET(modify_vhca_state_in, in, function_id, function_id);
	MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, 0);

	return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
}

int mlx5_modify_vhca_sw_id(struct mlx5_core_dev *dev, u16 function_id, u32 sw_fn_id)
{
	u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {};
	u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {};

	MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE);
	MLX5_SET(modify_vhca_state_in, in, function_id, function_id);
	MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, 0);
	MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.sw_function_id, 1);
	MLX5_SET(modify_vhca_state_in, in, vhca_state_context.sw_function_id, sw_fn_id);

	return mlx5_cmd_exec_inout(dev, modify_vhca_state, in, out);
}

int mlx5_vhca_event_arm(struct mlx5_core_dev *dev, u16 function_id)
{
	u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {};

	MLX5_SET(modify_vhca_state_in, in, vhca_state_context.arm_change_event, 1);
	MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.arm_change_event, 1);

	return mlx5_cmd_modify_vhca_state(dev, function_id, in, sizeof(in));
}

static void
mlx5_vhca_event_notify(struct mlx5_core_dev *dev, struct mlx5_vhca_state_event *event)
{
	u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {};
	int err;

	err = mlx5_cmd_query_vhca_state(dev, event->function_id, out, sizeof(out));
	if (err)
		return;

	event->sw_function_id = MLX5_GET(query_vhca_state_out, out,
					 vhca_state_context.sw_function_id);
	event->new_vhca_state = MLX5_GET(query_vhca_state_out, out,
					 vhca_state_context.vhca_state);

	mlx5_vhca_event_arm(dev, event->function_id);
	trace_mlx5_sf_vhca_event(dev, event);

	blocking_notifier_call_chain(&dev->priv.vhca_state_n_head, 0, event);
}

static void mlx5_vhca_state_work_handler(struct work_struct *_work)
{
	struct mlx5_vhca_event_work *work = container_of(_work, struct mlx5_vhca_event_work, work);

	mlx5_vhca_event_notify(work->dev, &work->event);
	kfree(work);
}

void mlx5_vhca_events_work_enqueue(struct mlx5_core_dev *dev, int idx, struct work_struct *work)
{
	queue_work(dev->priv.vhca_events->handler[idx].wq, work);
}

static int
mlx5_vhca_state_change_notifier(struct notifier_block *nb, unsigned long type, void *data)
{
	struct mlx5_core_dev *dev = mlx5_nb_cof(nb, struct mlx5_core_dev,
						priv.vhca_state_nb);
	struct mlx5_vhca_event_work *work;
	struct mlx5_eqe *eqe = data;
	int wq_idx;

	work = kzalloc(sizeof(*work), GFP_ATOMIC);
	if (!work)
		return NOTIFY_DONE;
	INIT_WORK(&work->work, &mlx5_vhca_state_work_handler);
	work->dev = dev;
	work->event.function_id = be16_to_cpu(eqe->data.vhca_state.function_id);
	wq_idx = work->event.function_id % MLX5_DEV_MAX_WQS;
	mlx5_vhca_events_work_enqueue(dev, wq_idx, &work->work);
	return NOTIFY_OK;
}

void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap)
{
	if (!mlx5_vhca_event_supported(dev))
		return;

	MLX5_SET(cmd_hca_cap, set_hca_cap, vhca_state, 1);
	MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_allocated, 1);
	MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_active, 1);
	MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_in_use, 1);
	MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_teardown_request, 1);
}

void mlx5_vhca_state_notifier_init(struct mlx5_core_dev *dev)
{
	BLOCKING_INIT_NOTIFIER_HEAD(&dev->priv.vhca_state_n_head);
	MLX5_NB_INIT(&dev->priv.vhca_state_nb, mlx5_vhca_state_change_notifier,
		     VHCA_STATE_CHANGE);
}

int mlx5_vhca_event_init(struct mlx5_core_dev *dev)
{
	char wq_name[MLX5_CMD_WQ_MAX_NAME];
	struct mlx5_vhca_events *events;
	int err, i;

	if (!mlx5_vhca_event_supported(dev))
		return 0;

	events = kzalloc(sizeof(*events), GFP_KERNEL);
	if (!events)
		return -ENOMEM;

	events->dev = dev;
	for (i = 0; i < MLX5_DEV_MAX_WQS; i++) {
		snprintf(wq_name, MLX5_CMD_WQ_MAX_NAME, "mlx5_vhca_event%d", i);
		events->handler[i].wq = create_singlethread_workqueue(wq_name);
		if (!events->handler[i].wq) {
			err = -ENOMEM;
			goto err_create_wq;
		}
	}
	dev->priv.vhca_events = events;

	return 0;

err_create_wq:
	for (--i; i >= 0; i--)
		destroy_workqueue(events->handler[i].wq);
	kfree(events);
	return err;
}

void mlx5_vhca_event_work_queues_flush(struct mlx5_core_dev *dev)
{
	struct mlx5_vhca_events *vhca_events;
	int i;

	if (!mlx5_vhca_event_supported(dev))
		return;

	vhca_events = dev->priv.vhca_events;
	for (i = 0; i < MLX5_DEV_MAX_WQS; i++)
		flush_workqueue(vhca_events->handler[i].wq);
}

void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev)
{
	struct mlx5_vhca_events *vhca_events;
	int i;

	if (!mlx5_vhca_event_supported(dev))
		return;

	vhca_events = dev->priv.vhca_events;
	for (i = 0; i < MLX5_DEV_MAX_WQS; i++)
		destroy_workqueue(vhca_events->handler[i].wq);
	kvfree(vhca_events);
}

void mlx5_vhca_event_start(struct mlx5_core_dev *dev)
{
	if (!mlx5_vhca_event_supported(dev))
		return;

	mlx5_eq_notifier_register(dev, &dev->priv.vhca_state_nb);
}

void mlx5_vhca_event_stop(struct mlx5_core_dev *dev)
{
	if (!mlx5_vhca_event_supported(dev))
		return;

	mlx5_eq_notifier_unregister(dev, &dev->priv.vhca_state_nb);

	/* Flush workqueues of all pending events. */
	mlx5_vhca_event_work_queues_flush(dev);
}

int mlx5_vhca_event_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb)
{
	return blocking_notifier_chain_register(&dev->priv.vhca_state_n_head,
						nb);
}

void mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb)
{
	blocking_notifier_chain_unregister(&dev->priv.vhca_state_n_head, nb);
}