1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2018 Mellanox Technologies */
3 
4 #include <linux/mlx5/vport.h>
5 #include "lib/devcom.h"
6 
7 static LIST_HEAD(devcom_list);
8 
9 #define devcom_for_each_component(priv, comp, iter) \
10 	for (iter = 0; \
11 	     comp = &(priv)->components[iter], iter < MLX5_DEVCOM_NUM_COMPONENTS; \
12 	     iter++)
13 
14 struct mlx5_devcom_component {
15 	struct {
16 		void *data;
17 	} device[MLX5_DEVCOM_PORTS_SUPPORTED];
18 
19 	mlx5_devcom_event_handler_t handler;
20 	struct rw_semaphore sem;
21 	bool paired;
22 };
23 
24 struct mlx5_devcom_list {
25 	struct list_head list;
26 
27 	struct mlx5_devcom_component components[MLX5_DEVCOM_NUM_COMPONENTS];
28 	struct mlx5_core_dev *devs[MLX5_DEVCOM_PORTS_SUPPORTED];
29 };
30 
31 struct mlx5_devcom {
32 	struct mlx5_devcom_list *priv;
33 	int idx;
34 };
35 
mlx5_devcom_list_alloc(void)36 static struct mlx5_devcom_list *mlx5_devcom_list_alloc(void)
37 {
38 	struct mlx5_devcom_component *comp;
39 	struct mlx5_devcom_list *priv;
40 	int i;
41 
42 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
43 	if (!priv)
44 		return NULL;
45 
46 	devcom_for_each_component(priv, comp, i)
47 		init_rwsem(&comp->sem);
48 
49 	return priv;
50 }
51 
mlx5_devcom_alloc(struct mlx5_devcom_list * priv,u8 idx)52 static struct mlx5_devcom *mlx5_devcom_alloc(struct mlx5_devcom_list *priv,
53 					     u8 idx)
54 {
55 	struct mlx5_devcom *devcom;
56 
57 	devcom = kzalloc(sizeof(*devcom), GFP_KERNEL);
58 	if (!devcom)
59 		return NULL;
60 
61 	devcom->priv = priv;
62 	devcom->idx = idx;
63 	return devcom;
64 }
65 
66 /* Must be called with intf_mutex held */
mlx5_devcom_register_device(struct mlx5_core_dev * dev)67 struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev)
68 {
69 	struct mlx5_devcom_list *priv = NULL, *iter;
70 	struct mlx5_devcom *devcom = NULL;
71 	bool new_priv = false;
72 	u64 sguid0, sguid1;
73 	int idx, i;
74 
75 	if (!mlx5_core_is_pf(dev))
76 		return NULL;
77 	if (MLX5_CAP_GEN(dev, num_lag_ports) != MLX5_DEVCOM_PORTS_SUPPORTED)
78 		return NULL;
79 
80 	sguid0 = mlx5_query_nic_system_image_guid(dev);
81 	list_for_each_entry(iter, &devcom_list, list) {
82 		struct mlx5_core_dev *tmp_dev = NULL;
83 
84 		idx = -1;
85 		for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) {
86 			if (iter->devs[i])
87 				tmp_dev = iter->devs[i];
88 			else
89 				idx = i;
90 		}
91 
92 		if (idx == -1)
93 			continue;
94 
95 		sguid1 = mlx5_query_nic_system_image_guid(tmp_dev);
96 		if (sguid0 != sguid1)
97 			continue;
98 
99 		priv = iter;
100 		break;
101 	}
102 
103 	if (!priv) {
104 		priv = mlx5_devcom_list_alloc();
105 		if (!priv)
106 			return ERR_PTR(-ENOMEM);
107 
108 		idx = 0;
109 		new_priv = true;
110 	}
111 
112 	priv->devs[idx] = dev;
113 	devcom = mlx5_devcom_alloc(priv, idx);
114 	if (!devcom) {
115 		kfree(priv);
116 		return ERR_PTR(-ENOMEM);
117 	}
118 
119 	if (new_priv)
120 		list_add(&priv->list, &devcom_list);
121 
122 	return devcom;
123 }
124 
125 /* Must be called with intf_mutex held */
mlx5_devcom_unregister_device(struct mlx5_devcom * devcom)126 void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom)
127 {
128 	struct mlx5_devcom_list *priv;
129 	int i;
130 
131 	if (IS_ERR_OR_NULL(devcom))
132 		return;
133 
134 	priv = devcom->priv;
135 	priv->devs[devcom->idx] = NULL;
136 
137 	kfree(devcom);
138 
139 	for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
140 		if (priv->devs[i])
141 			break;
142 
143 	if (i != MLX5_DEVCOM_PORTS_SUPPORTED)
144 		return;
145 
146 	list_del(&priv->list);
147 	kfree(priv);
148 }
149 
mlx5_devcom_register_component(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,mlx5_devcom_event_handler_t handler,void * data)150 void mlx5_devcom_register_component(struct mlx5_devcom *devcom,
151 				    enum mlx5_devcom_components id,
152 				    mlx5_devcom_event_handler_t handler,
153 				    void *data)
154 {
155 	struct mlx5_devcom_component *comp;
156 
157 	if (IS_ERR_OR_NULL(devcom))
158 		return;
159 
160 	WARN_ON(!data);
161 
162 	comp = &devcom->priv->components[id];
163 	down_write(&comp->sem);
164 	comp->handler = handler;
165 	comp->device[devcom->idx].data = data;
166 	up_write(&comp->sem);
167 }
168 
mlx5_devcom_unregister_component(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)169 void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom,
170 				      enum mlx5_devcom_components id)
171 {
172 	struct mlx5_devcom_component *comp;
173 
174 	if (IS_ERR_OR_NULL(devcom))
175 		return;
176 
177 	comp = &devcom->priv->components[id];
178 	down_write(&comp->sem);
179 	comp->device[devcom->idx].data = NULL;
180 	up_write(&comp->sem);
181 }
182 
mlx5_devcom_send_event(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,int event,void * event_data)183 int mlx5_devcom_send_event(struct mlx5_devcom *devcom,
184 			   enum mlx5_devcom_components id,
185 			   int event,
186 			   void *event_data)
187 {
188 	struct mlx5_devcom_component *comp;
189 	int err = -ENODEV, i;
190 
191 	if (IS_ERR_OR_NULL(devcom))
192 		return err;
193 
194 	comp = &devcom->priv->components[id];
195 	down_write(&comp->sem);
196 	for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
197 		if (i != devcom->idx && comp->device[i].data) {
198 			err = comp->handler(event, comp->device[i].data,
199 					    event_data);
200 			break;
201 		}
202 
203 	up_write(&comp->sem);
204 	return err;
205 }
206 
mlx5_devcom_set_paired(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,bool paired)207 void mlx5_devcom_set_paired(struct mlx5_devcom *devcom,
208 			    enum mlx5_devcom_components id,
209 			    bool paired)
210 {
211 	struct mlx5_devcom_component *comp;
212 
213 	comp = &devcom->priv->components[id];
214 	WARN_ON(!rwsem_is_locked(&comp->sem));
215 
216 	comp->paired = paired;
217 }
218 
mlx5_devcom_is_paired(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)219 bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom,
220 			   enum mlx5_devcom_components id)
221 {
222 	if (IS_ERR_OR_NULL(devcom))
223 		return false;
224 
225 	return devcom->priv->components[id].paired;
226 }
227 
mlx5_devcom_get_peer_data(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)228 void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom,
229 				enum mlx5_devcom_components id)
230 {
231 	struct mlx5_devcom_component *comp;
232 	int i;
233 
234 	if (IS_ERR_OR_NULL(devcom))
235 		return NULL;
236 
237 	comp = &devcom->priv->components[id];
238 	down_read(&comp->sem);
239 	if (!comp->paired) {
240 		up_read(&comp->sem);
241 		return NULL;
242 	}
243 
244 	for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
245 		if (i != devcom->idx)
246 			break;
247 
248 	return comp->device[i].data;
249 }
250 
mlx5_devcom_release_peer_data(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)251 void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom,
252 				   enum mlx5_devcom_components id)
253 {
254 	struct mlx5_devcom_component *comp = &devcom->priv->components[id];
255 
256 	up_read(&comp->sem);
257 }
258