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