1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2020 Mellanox Technologies Ltd */
3
4 #include <linux/mlx5/driver.h>
5 #include "mlx5_ifc_vhca_event.h"
6 #include "mlx5_core.h"
7 #include "vhca_event.h"
8 #include "ecpf.h"
9 #define CREATE_TRACE_POINTS
10 #include "diag/vhca_tracepoint.h"
11
12 struct mlx5_vhca_state_notifier {
13 struct mlx5_core_dev *dev;
14 struct mlx5_nb nb;
15 struct blocking_notifier_head n_head;
16 };
17
18 struct mlx5_vhca_event_work {
19 struct work_struct work;
20 struct mlx5_vhca_state_notifier *notifier;
21 struct mlx5_vhca_state_event event;
22 };
23
mlx5_cmd_query_vhca_state(struct mlx5_core_dev * dev,u16 function_id,u32 * out,u32 outlen)24 int mlx5_cmd_query_vhca_state(struct mlx5_core_dev *dev, u16 function_id, u32 *out, u32 outlen)
25 {
26 u32 in[MLX5_ST_SZ_DW(query_vhca_state_in)] = {};
27
28 MLX5_SET(query_vhca_state_in, in, opcode, MLX5_CMD_OP_QUERY_VHCA_STATE);
29 MLX5_SET(query_vhca_state_in, in, function_id, function_id);
30 MLX5_SET(query_vhca_state_in, in, embedded_cpu_function, 0);
31
32 return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
33 }
34
mlx5_cmd_modify_vhca_state(struct mlx5_core_dev * dev,u16 function_id,u32 * in,u32 inlen)35 static int mlx5_cmd_modify_vhca_state(struct mlx5_core_dev *dev, u16 function_id,
36 u32 *in, u32 inlen)
37 {
38 u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {};
39
40 MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE);
41 MLX5_SET(modify_vhca_state_in, in, function_id, function_id);
42 MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, 0);
43
44 return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
45 }
46
mlx5_modify_vhca_sw_id(struct mlx5_core_dev * dev,u16 function_id,u32 sw_fn_id)47 int mlx5_modify_vhca_sw_id(struct mlx5_core_dev *dev, u16 function_id, u32 sw_fn_id)
48 {
49 u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {};
50 u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {};
51
52 MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE);
53 MLX5_SET(modify_vhca_state_in, in, function_id, function_id);
54 MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, 0);
55 MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.sw_function_id, 1);
56 MLX5_SET(modify_vhca_state_in, in, vhca_state_context.sw_function_id, sw_fn_id);
57
58 return mlx5_cmd_exec_inout(dev, modify_vhca_state, in, out);
59 }
60
mlx5_vhca_event_arm(struct mlx5_core_dev * dev,u16 function_id)61 int mlx5_vhca_event_arm(struct mlx5_core_dev *dev, u16 function_id)
62 {
63 u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {};
64
65 MLX5_SET(modify_vhca_state_in, in, vhca_state_context.arm_change_event, 1);
66 MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.arm_change_event, 1);
67
68 return mlx5_cmd_modify_vhca_state(dev, function_id, in, sizeof(in));
69 }
70
71 static void
mlx5_vhca_event_notify(struct mlx5_core_dev * dev,struct mlx5_vhca_state_event * event)72 mlx5_vhca_event_notify(struct mlx5_core_dev *dev, struct mlx5_vhca_state_event *event)
73 {
74 u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {};
75 int err;
76
77 err = mlx5_cmd_query_vhca_state(dev, event->function_id, out, sizeof(out));
78 if (err)
79 return;
80
81 event->sw_function_id = MLX5_GET(query_vhca_state_out, out,
82 vhca_state_context.sw_function_id);
83 event->new_vhca_state = MLX5_GET(query_vhca_state_out, out,
84 vhca_state_context.vhca_state);
85
86 mlx5_vhca_event_arm(dev, event->function_id);
87 trace_mlx5_sf_vhca_event(dev, event);
88
89 blocking_notifier_call_chain(&dev->priv.vhca_state_notifier->n_head, 0, event);
90 }
91
mlx5_vhca_state_work_handler(struct work_struct * _work)92 static void mlx5_vhca_state_work_handler(struct work_struct *_work)
93 {
94 struct mlx5_vhca_event_work *work = container_of(_work, struct mlx5_vhca_event_work, work);
95 struct mlx5_vhca_state_notifier *notifier = work->notifier;
96 struct mlx5_core_dev *dev = notifier->dev;
97
98 mlx5_vhca_event_notify(dev, &work->event);
99 kfree(work);
100 }
101
102 static int
mlx5_vhca_state_change_notifier(struct notifier_block * nb,unsigned long type,void * data)103 mlx5_vhca_state_change_notifier(struct notifier_block *nb, unsigned long type, void *data)
104 {
105 struct mlx5_vhca_state_notifier *notifier =
106 mlx5_nb_cof(nb, struct mlx5_vhca_state_notifier, nb);
107 struct mlx5_vhca_event_work *work;
108 struct mlx5_eqe *eqe = data;
109
110 work = kzalloc(sizeof(*work), GFP_ATOMIC);
111 if (!work)
112 return NOTIFY_DONE;
113 INIT_WORK(&work->work, &mlx5_vhca_state_work_handler);
114 work->notifier = notifier;
115 work->event.function_id = be16_to_cpu(eqe->data.vhca_state.function_id);
116 mlx5_events_work_enqueue(notifier->dev, &work->work);
117 return NOTIFY_OK;
118 }
119
mlx5_vhca_state_cap_handle(struct mlx5_core_dev * dev,void * set_hca_cap)120 void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap)
121 {
122 if (!mlx5_vhca_event_supported(dev))
123 return;
124
125 MLX5_SET(cmd_hca_cap, set_hca_cap, vhca_state, 1);
126 MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_allocated, 1);
127 MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_active, 1);
128 MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_in_use, 1);
129 MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_teardown_request, 1);
130 }
131
mlx5_vhca_event_init(struct mlx5_core_dev * dev)132 int mlx5_vhca_event_init(struct mlx5_core_dev *dev)
133 {
134 struct mlx5_vhca_state_notifier *notifier;
135
136 if (!mlx5_vhca_event_supported(dev))
137 return 0;
138
139 notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
140 if (!notifier)
141 return -ENOMEM;
142
143 dev->priv.vhca_state_notifier = notifier;
144 notifier->dev = dev;
145 BLOCKING_INIT_NOTIFIER_HEAD(¬ifier->n_head);
146 MLX5_NB_INIT(¬ifier->nb, mlx5_vhca_state_change_notifier, VHCA_STATE_CHANGE);
147 return 0;
148 }
149
mlx5_vhca_event_cleanup(struct mlx5_core_dev * dev)150 void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev)
151 {
152 if (!mlx5_vhca_event_supported(dev))
153 return;
154
155 kfree(dev->priv.vhca_state_notifier);
156 dev->priv.vhca_state_notifier = NULL;
157 }
158
mlx5_vhca_event_start(struct mlx5_core_dev * dev)159 void mlx5_vhca_event_start(struct mlx5_core_dev *dev)
160 {
161 struct mlx5_vhca_state_notifier *notifier;
162
163 if (!dev->priv.vhca_state_notifier)
164 return;
165
166 notifier = dev->priv.vhca_state_notifier;
167 mlx5_eq_notifier_register(dev, ¬ifier->nb);
168 }
169
mlx5_vhca_event_stop(struct mlx5_core_dev * dev)170 void mlx5_vhca_event_stop(struct mlx5_core_dev *dev)
171 {
172 struct mlx5_vhca_state_notifier *notifier;
173
174 if (!dev->priv.vhca_state_notifier)
175 return;
176
177 notifier = dev->priv.vhca_state_notifier;
178 mlx5_eq_notifier_unregister(dev, ¬ifier->nb);
179 }
180
mlx5_vhca_event_notifier_register(struct mlx5_core_dev * dev,struct notifier_block * nb)181 int mlx5_vhca_event_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb)
182 {
183 if (!dev->priv.vhca_state_notifier)
184 return -EOPNOTSUPP;
185 return blocking_notifier_chain_register(&dev->priv.vhca_state_notifier->n_head, nb);
186 }
187
mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev * dev,struct notifier_block * nb)188 void mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb)
189 {
190 blocking_notifier_chain_unregister(&dev->priv.vhca_state_notifier->n_head, nb);
191 }
192