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 <linux/mlx5/device.h>
6 #include "mlx5_core.h"
7 #include "dev.h"
8 #include "sf/vhca_event.h"
9 #include "sf/sf.h"
10 #include "sf/mlx5_ifc_vhca_event.h"
11 #include "ecpf.h"
12 
13 struct mlx5_sf_dev_table {
14 	struct xarray devices;
15 	unsigned int max_sfs;
16 	phys_addr_t base_address;
17 	u64 sf_bar_length;
18 	struct notifier_block nb;
19 	struct mlx5_core_dev *dev;
20 };
21 
mlx5_sf_dev_supported(const struct mlx5_core_dev * dev)22 static bool mlx5_sf_dev_supported(const struct mlx5_core_dev *dev)
23 {
24 	return MLX5_CAP_GEN(dev, sf) && mlx5_vhca_event_supported(dev);
25 }
26 
mlx5_sf_dev_allocated(const struct mlx5_core_dev * dev)27 bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev)
28 {
29 	struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
30 
31 	if (!mlx5_sf_dev_supported(dev))
32 		return false;
33 
34 	return !xa_empty(&table->devices);
35 }
36 
sfnum_show(struct device * dev,struct device_attribute * attr,char * buf)37 static ssize_t sfnum_show(struct device *dev, struct device_attribute *attr, char *buf)
38 {
39 	struct auxiliary_device *adev = container_of(dev, struct auxiliary_device, dev);
40 	struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev);
41 
42 	return scnprintf(buf, PAGE_SIZE, "%u\n", sf_dev->sfnum);
43 }
44 static DEVICE_ATTR_RO(sfnum);
45 
46 static struct attribute *sf_device_attrs[] = {
47 	&dev_attr_sfnum.attr,
48 	NULL,
49 };
50 
51 static const struct attribute_group sf_attr_group = {
52 	.attrs = sf_device_attrs,
53 };
54 
55 static const struct attribute_group *sf_attr_groups[2] = {
56 	&sf_attr_group,
57 	NULL
58 };
59 
mlx5_sf_dev_release(struct device * device)60 static void mlx5_sf_dev_release(struct device *device)
61 {
62 	struct auxiliary_device *adev = container_of(device, struct auxiliary_device, dev);
63 	struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev);
64 
65 	mlx5_adev_idx_free(adev->id);
66 	kfree(sf_dev);
67 }
68 
mlx5_sf_dev_remove(struct mlx5_sf_dev * sf_dev)69 static void mlx5_sf_dev_remove(struct mlx5_sf_dev *sf_dev)
70 {
71 	auxiliary_device_delete(&sf_dev->adev);
72 	auxiliary_device_uninit(&sf_dev->adev);
73 }
74 
mlx5_sf_dev_add(struct mlx5_core_dev * dev,u16 sf_index,u32 sfnum)75 static void mlx5_sf_dev_add(struct mlx5_core_dev *dev, u16 sf_index, u32 sfnum)
76 {
77 	struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
78 	struct mlx5_sf_dev *sf_dev;
79 	struct pci_dev *pdev;
80 	int err;
81 	int id;
82 
83 	id = mlx5_adev_idx_alloc();
84 	if (id < 0) {
85 		err = id;
86 		goto add_err;
87 	}
88 
89 	sf_dev = kzalloc(sizeof(*sf_dev), GFP_KERNEL);
90 	if (!sf_dev) {
91 		mlx5_adev_idx_free(id);
92 		err = -ENOMEM;
93 		goto add_err;
94 	}
95 	pdev = dev->pdev;
96 	sf_dev->adev.id = id;
97 	sf_dev->adev.name = MLX5_SF_DEV_ID_NAME;
98 	sf_dev->adev.dev.release = mlx5_sf_dev_release;
99 	sf_dev->adev.dev.parent = &pdev->dev;
100 	sf_dev->adev.dev.groups = sf_attr_groups;
101 	sf_dev->sfnum = sfnum;
102 	sf_dev->parent_mdev = dev;
103 
104 	if (!table->max_sfs) {
105 		mlx5_adev_idx_free(id);
106 		kfree(sf_dev);
107 		err = -EOPNOTSUPP;
108 		goto add_err;
109 	}
110 	sf_dev->bar_base_addr = table->base_address + (sf_index * table->sf_bar_length);
111 
112 	err = auxiliary_device_init(&sf_dev->adev);
113 	if (err) {
114 		mlx5_adev_idx_free(id);
115 		kfree(sf_dev);
116 		goto add_err;
117 	}
118 
119 	err = auxiliary_device_add(&sf_dev->adev);
120 	if (err) {
121 		put_device(&sf_dev->adev.dev);
122 		goto add_err;
123 	}
124 
125 	err = xa_insert(&table->devices, sf_index, sf_dev, GFP_KERNEL);
126 	if (err)
127 		goto xa_err;
128 	return;
129 
130 xa_err:
131 	mlx5_sf_dev_remove(sf_dev);
132 add_err:
133 	mlx5_core_err(dev, "SF DEV: fail device add for index=%d sfnum=%d err=%d\n",
134 		      sf_index, sfnum, err);
135 }
136 
mlx5_sf_dev_del(struct mlx5_core_dev * dev,struct mlx5_sf_dev * sf_dev,u16 sf_index)137 static void mlx5_sf_dev_del(struct mlx5_core_dev *dev, struct mlx5_sf_dev *sf_dev, u16 sf_index)
138 {
139 	struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
140 
141 	xa_erase(&table->devices, sf_index);
142 	mlx5_sf_dev_remove(sf_dev);
143 }
144 
145 static int
mlx5_sf_dev_state_change_handler(struct notifier_block * nb,unsigned long event_code,void * data)146 mlx5_sf_dev_state_change_handler(struct notifier_block *nb, unsigned long event_code, void *data)
147 {
148 	struct mlx5_sf_dev_table *table = container_of(nb, struct mlx5_sf_dev_table, nb);
149 	const struct mlx5_vhca_state_event *event = data;
150 	struct mlx5_sf_dev *sf_dev;
151 	u16 max_functions;
152 	u16 sf_index;
153 	u16 base_id;
154 
155 	max_functions = mlx5_sf_max_functions(table->dev);
156 	if (!max_functions)
157 		return 0;
158 
159 	base_id = MLX5_CAP_GEN(table->dev, sf_base_id);
160 	if (event->function_id < base_id || event->function_id >= (base_id + max_functions))
161 		return 0;
162 
163 	sf_index = event->function_id - base_id;
164 	sf_dev = xa_load(&table->devices, sf_index);
165 	switch (event->new_vhca_state) {
166 	case MLX5_VHCA_STATE_ALLOCATED:
167 		if (sf_dev)
168 			mlx5_sf_dev_del(table->dev, sf_dev, sf_index);
169 		break;
170 	case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
171 		if (sf_dev)
172 			mlx5_sf_dev_del(table->dev, sf_dev, sf_index);
173 		else
174 			mlx5_core_err(table->dev,
175 				      "SF DEV: teardown state for invalid dev index=%d fn_id=0x%x\n",
176 				      sf_index, event->sw_function_id);
177 		break;
178 	case MLX5_VHCA_STATE_ACTIVE:
179 		if (!sf_dev)
180 			mlx5_sf_dev_add(table->dev, sf_index, event->sw_function_id);
181 		break;
182 	default:
183 		break;
184 	}
185 	return 0;
186 }
187 
mlx5_sf_dev_vhca_arm_all(struct mlx5_sf_dev_table * table)188 static int mlx5_sf_dev_vhca_arm_all(struct mlx5_sf_dev_table *table)
189 {
190 	struct mlx5_core_dev *dev = table->dev;
191 	u16 max_functions;
192 	u16 function_id;
193 	int err = 0;
194 	int i;
195 
196 	max_functions = mlx5_sf_max_functions(dev);
197 	function_id = MLX5_CAP_GEN(dev, sf_base_id);
198 	/* Arm the vhca context as the vhca event notifier */
199 	for (i = 0; i < max_functions; i++) {
200 		err = mlx5_vhca_event_arm(dev, function_id);
201 		if (err)
202 			return err;
203 
204 		function_id++;
205 	}
206 	return 0;
207 }
208 
mlx5_sf_dev_table_create(struct mlx5_core_dev * dev)209 void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev)
210 {
211 	struct mlx5_sf_dev_table *table;
212 	unsigned int max_sfs;
213 	int err;
214 
215 	if (!mlx5_sf_dev_supported(dev) || !mlx5_vhca_event_supported(dev))
216 		return;
217 
218 	table = kzalloc(sizeof(*table), GFP_KERNEL);
219 	if (!table) {
220 		err = -ENOMEM;
221 		goto table_err;
222 	}
223 
224 	table->nb.notifier_call = mlx5_sf_dev_state_change_handler;
225 	table->dev = dev;
226 	if (MLX5_CAP_GEN(dev, max_num_sf))
227 		max_sfs = MLX5_CAP_GEN(dev, max_num_sf);
228 	else
229 		max_sfs = 1 << MLX5_CAP_GEN(dev, log_max_sf);
230 	table->sf_bar_length = 1 << (MLX5_CAP_GEN(dev, log_min_sf_size) + 12);
231 	table->base_address = pci_resource_start(dev->pdev, 2);
232 	table->max_sfs = max_sfs;
233 	xa_init(&table->devices);
234 	dev->priv.sf_dev_table = table;
235 
236 	err = mlx5_vhca_event_notifier_register(dev, &table->nb);
237 	if (err)
238 		goto vhca_err;
239 	err = mlx5_sf_dev_vhca_arm_all(table);
240 	if (err)
241 		goto arm_err;
242 	mlx5_core_dbg(dev, "SF DEV: max sf devices=%d\n", max_sfs);
243 	return;
244 
245 arm_err:
246 	mlx5_vhca_event_notifier_unregister(dev, &table->nb);
247 vhca_err:
248 	table->max_sfs = 0;
249 	kfree(table);
250 	dev->priv.sf_dev_table = NULL;
251 table_err:
252 	mlx5_core_err(dev, "SF DEV table create err = %d\n", err);
253 }
254 
mlx5_sf_dev_destroy_all(struct mlx5_sf_dev_table * table)255 static void mlx5_sf_dev_destroy_all(struct mlx5_sf_dev_table *table)
256 {
257 	struct mlx5_sf_dev *sf_dev;
258 	unsigned long index;
259 
260 	xa_for_each(&table->devices, index, sf_dev) {
261 		xa_erase(&table->devices, index);
262 		mlx5_sf_dev_remove(sf_dev);
263 	}
264 }
265 
mlx5_sf_dev_table_destroy(struct mlx5_core_dev * dev)266 void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev)
267 {
268 	struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
269 
270 	if (!table)
271 		return;
272 
273 	mlx5_vhca_event_notifier_unregister(dev, &table->nb);
274 
275 	/* Now that event handler is not running, it is safe to destroy
276 	 * the sf device without race.
277 	 */
278 	mlx5_sf_dev_destroy_all(table);
279 
280 	WARN_ON(!xa_empty(&table->devices));
281 	kfree(table);
282 	dev->priv.sf_dev_table = NULL;
283 }
284