1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * DSA devlink handling |
4 | */ |
5 | |
6 | #include <net/dsa.h> |
7 | #include <net/devlink.h> |
8 | |
9 | #include "devlink.h" |
10 | |
11 | static int dsa_devlink_info_get(struct devlink *dl, |
12 | struct devlink_info_req *req, |
13 | struct netlink_ext_ack *extack) |
14 | { |
15 | struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
16 | |
17 | if (ds->ops->devlink_info_get) |
18 | return ds->ops->devlink_info_get(ds, req, extack); |
19 | |
20 | return -EOPNOTSUPP; |
21 | } |
22 | |
23 | static int dsa_devlink_sb_pool_get(struct devlink *dl, |
24 | unsigned int sb_index, u16 pool_index, |
25 | struct devlink_sb_pool_info *pool_info) |
26 | { |
27 | struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
28 | |
29 | if (!ds->ops->devlink_sb_pool_get) |
30 | return -EOPNOTSUPP; |
31 | |
32 | return ds->ops->devlink_sb_pool_get(ds, sb_index, pool_index, |
33 | pool_info); |
34 | } |
35 | |
36 | static int dsa_devlink_sb_pool_set(struct devlink *dl, unsigned int sb_index, |
37 | u16 pool_index, u32 size, |
38 | enum devlink_sb_threshold_type threshold_type, |
39 | struct netlink_ext_ack *extack) |
40 | { |
41 | struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
42 | |
43 | if (!ds->ops->devlink_sb_pool_set) |
44 | return -EOPNOTSUPP; |
45 | |
46 | return ds->ops->devlink_sb_pool_set(ds, sb_index, pool_index, size, |
47 | threshold_type, extack); |
48 | } |
49 | |
50 | static int dsa_devlink_sb_port_pool_get(struct devlink_port *dlp, |
51 | unsigned int sb_index, u16 pool_index, |
52 | u32 *p_threshold) |
53 | { |
54 | struct dsa_switch *ds = dsa_devlink_port_to_ds(port: dlp); |
55 | int port = dsa_devlink_port_to_port(port: dlp); |
56 | |
57 | if (!ds->ops->devlink_sb_port_pool_get) |
58 | return -EOPNOTSUPP; |
59 | |
60 | return ds->ops->devlink_sb_port_pool_get(ds, port, sb_index, |
61 | pool_index, p_threshold); |
62 | } |
63 | |
64 | static int dsa_devlink_sb_port_pool_set(struct devlink_port *dlp, |
65 | unsigned int sb_index, u16 pool_index, |
66 | u32 threshold, |
67 | struct netlink_ext_ack *extack) |
68 | { |
69 | struct dsa_switch *ds = dsa_devlink_port_to_ds(port: dlp); |
70 | int port = dsa_devlink_port_to_port(port: dlp); |
71 | |
72 | if (!ds->ops->devlink_sb_port_pool_set) |
73 | return -EOPNOTSUPP; |
74 | |
75 | return ds->ops->devlink_sb_port_pool_set(ds, port, sb_index, |
76 | pool_index, threshold, extack); |
77 | } |
78 | |
79 | static int |
80 | dsa_devlink_sb_tc_pool_bind_get(struct devlink_port *dlp, |
81 | unsigned int sb_index, u16 tc_index, |
82 | enum devlink_sb_pool_type pool_type, |
83 | u16 *p_pool_index, u32 *p_threshold) |
84 | { |
85 | struct dsa_switch *ds = dsa_devlink_port_to_ds(port: dlp); |
86 | int port = dsa_devlink_port_to_port(port: dlp); |
87 | |
88 | if (!ds->ops->devlink_sb_tc_pool_bind_get) |
89 | return -EOPNOTSUPP; |
90 | |
91 | return ds->ops->devlink_sb_tc_pool_bind_get(ds, port, sb_index, |
92 | tc_index, pool_type, |
93 | p_pool_index, p_threshold); |
94 | } |
95 | |
96 | static int |
97 | dsa_devlink_sb_tc_pool_bind_set(struct devlink_port *dlp, |
98 | unsigned int sb_index, u16 tc_index, |
99 | enum devlink_sb_pool_type pool_type, |
100 | u16 pool_index, u32 threshold, |
101 | struct netlink_ext_ack *extack) |
102 | { |
103 | struct dsa_switch *ds = dsa_devlink_port_to_ds(port: dlp); |
104 | int port = dsa_devlink_port_to_port(port: dlp); |
105 | |
106 | if (!ds->ops->devlink_sb_tc_pool_bind_set) |
107 | return -EOPNOTSUPP; |
108 | |
109 | return ds->ops->devlink_sb_tc_pool_bind_set(ds, port, sb_index, |
110 | tc_index, pool_type, |
111 | pool_index, threshold, |
112 | extack); |
113 | } |
114 | |
115 | static int dsa_devlink_sb_occ_snapshot(struct devlink *dl, |
116 | unsigned int sb_index) |
117 | { |
118 | struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
119 | |
120 | if (!ds->ops->devlink_sb_occ_snapshot) |
121 | return -EOPNOTSUPP; |
122 | |
123 | return ds->ops->devlink_sb_occ_snapshot(ds, sb_index); |
124 | } |
125 | |
126 | static int dsa_devlink_sb_occ_max_clear(struct devlink *dl, |
127 | unsigned int sb_index) |
128 | { |
129 | struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
130 | |
131 | if (!ds->ops->devlink_sb_occ_max_clear) |
132 | return -EOPNOTSUPP; |
133 | |
134 | return ds->ops->devlink_sb_occ_max_clear(ds, sb_index); |
135 | } |
136 | |
137 | static int dsa_devlink_sb_occ_port_pool_get(struct devlink_port *dlp, |
138 | unsigned int sb_index, |
139 | u16 pool_index, u32 *p_cur, |
140 | u32 *p_max) |
141 | { |
142 | struct dsa_switch *ds = dsa_devlink_port_to_ds(port: dlp); |
143 | int port = dsa_devlink_port_to_port(port: dlp); |
144 | |
145 | if (!ds->ops->devlink_sb_occ_port_pool_get) |
146 | return -EOPNOTSUPP; |
147 | |
148 | return ds->ops->devlink_sb_occ_port_pool_get(ds, port, sb_index, |
149 | pool_index, p_cur, p_max); |
150 | } |
151 | |
152 | static int |
153 | dsa_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp, |
154 | unsigned int sb_index, u16 tc_index, |
155 | enum devlink_sb_pool_type pool_type, |
156 | u32 *p_cur, u32 *p_max) |
157 | { |
158 | struct dsa_switch *ds = dsa_devlink_port_to_ds(port: dlp); |
159 | int port = dsa_devlink_port_to_port(port: dlp); |
160 | |
161 | if (!ds->ops->devlink_sb_occ_tc_port_bind_get) |
162 | return -EOPNOTSUPP; |
163 | |
164 | return ds->ops->devlink_sb_occ_tc_port_bind_get(ds, port, |
165 | sb_index, tc_index, |
166 | pool_type, p_cur, |
167 | p_max); |
168 | } |
169 | |
170 | static const struct devlink_ops dsa_devlink_ops = { |
171 | .info_get = dsa_devlink_info_get, |
172 | .sb_pool_get = dsa_devlink_sb_pool_get, |
173 | .sb_pool_set = dsa_devlink_sb_pool_set, |
174 | .sb_port_pool_get = dsa_devlink_sb_port_pool_get, |
175 | .sb_port_pool_set = dsa_devlink_sb_port_pool_set, |
176 | .sb_tc_pool_bind_get = dsa_devlink_sb_tc_pool_bind_get, |
177 | .sb_tc_pool_bind_set = dsa_devlink_sb_tc_pool_bind_set, |
178 | .sb_occ_snapshot = dsa_devlink_sb_occ_snapshot, |
179 | .sb_occ_max_clear = dsa_devlink_sb_occ_max_clear, |
180 | .sb_occ_port_pool_get = dsa_devlink_sb_occ_port_pool_get, |
181 | .sb_occ_tc_port_bind_get = dsa_devlink_sb_occ_tc_port_bind_get, |
182 | }; |
183 | |
184 | int dsa_devlink_param_get(struct devlink *dl, u32 id, |
185 | struct devlink_param_gset_ctx *ctx) |
186 | { |
187 | struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
188 | |
189 | if (!ds->ops->devlink_param_get) |
190 | return -EOPNOTSUPP; |
191 | |
192 | return ds->ops->devlink_param_get(ds, id, ctx); |
193 | } |
194 | EXPORT_SYMBOL_GPL(dsa_devlink_param_get); |
195 | |
196 | int dsa_devlink_param_set(struct devlink *dl, u32 id, |
197 | struct devlink_param_gset_ctx *ctx) |
198 | { |
199 | struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
200 | |
201 | if (!ds->ops->devlink_param_set) |
202 | return -EOPNOTSUPP; |
203 | |
204 | return ds->ops->devlink_param_set(ds, id, ctx); |
205 | } |
206 | EXPORT_SYMBOL_GPL(dsa_devlink_param_set); |
207 | |
208 | int dsa_devlink_params_register(struct dsa_switch *ds, |
209 | const struct devlink_param *params, |
210 | size_t params_count) |
211 | { |
212 | return devlink_params_register(devlink: ds->devlink, params, params_count); |
213 | } |
214 | EXPORT_SYMBOL_GPL(dsa_devlink_params_register); |
215 | |
216 | void dsa_devlink_params_unregister(struct dsa_switch *ds, |
217 | const struct devlink_param *params, |
218 | size_t params_count) |
219 | { |
220 | devlink_params_unregister(devlink: ds->devlink, params, params_count); |
221 | } |
222 | EXPORT_SYMBOL_GPL(dsa_devlink_params_unregister); |
223 | |
224 | int dsa_devlink_resource_register(struct dsa_switch *ds, |
225 | const char *resource_name, |
226 | u64 resource_size, |
227 | u64 resource_id, |
228 | u64 parent_resource_id, |
229 | const struct devlink_resource_size_params *size_params) |
230 | { |
231 | return devlink_resource_register(devlink: ds->devlink, resource_name, |
232 | resource_size, resource_id, |
233 | parent_resource_id, |
234 | size_params); |
235 | } |
236 | EXPORT_SYMBOL_GPL(dsa_devlink_resource_register); |
237 | |
238 | void dsa_devlink_resources_unregister(struct dsa_switch *ds) |
239 | { |
240 | devlink_resources_unregister(devlink: ds->devlink); |
241 | } |
242 | EXPORT_SYMBOL_GPL(dsa_devlink_resources_unregister); |
243 | |
244 | void dsa_devlink_resource_occ_get_register(struct dsa_switch *ds, |
245 | u64 resource_id, |
246 | devlink_resource_occ_get_t *occ_get, |
247 | void *occ_get_priv) |
248 | { |
249 | return devlink_resource_occ_get_register(devlink: ds->devlink, resource_id, |
250 | occ_get, occ_get_priv); |
251 | } |
252 | EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_register); |
253 | |
254 | void dsa_devlink_resource_occ_get_unregister(struct dsa_switch *ds, |
255 | u64 resource_id) |
256 | { |
257 | devlink_resource_occ_get_unregister(devlink: ds->devlink, resource_id); |
258 | } |
259 | EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_unregister); |
260 | |
261 | struct devlink_region * |
262 | dsa_devlink_region_create(struct dsa_switch *ds, |
263 | const struct devlink_region_ops *ops, |
264 | u32 region_max_snapshots, u64 region_size) |
265 | { |
266 | return devlink_region_create(devlink: ds->devlink, ops, region_max_snapshots, |
267 | region_size); |
268 | } |
269 | EXPORT_SYMBOL_GPL(dsa_devlink_region_create); |
270 | |
271 | struct devlink_region * |
272 | dsa_devlink_port_region_create(struct dsa_switch *ds, |
273 | int port, |
274 | const struct devlink_port_region_ops *ops, |
275 | u32 region_max_snapshots, u64 region_size) |
276 | { |
277 | struct dsa_port *dp = dsa_to_port(ds, p: port); |
278 | |
279 | return devlink_port_region_create(port: &dp->devlink_port, ops, |
280 | region_max_snapshots, |
281 | region_size); |
282 | } |
283 | EXPORT_SYMBOL_GPL(dsa_devlink_port_region_create); |
284 | |
285 | void dsa_devlink_region_destroy(struct devlink_region *region) |
286 | { |
287 | devlink_region_destroy(region); |
288 | } |
289 | EXPORT_SYMBOL_GPL(dsa_devlink_region_destroy); |
290 | |
291 | int dsa_port_devlink_setup(struct dsa_port *dp) |
292 | { |
293 | struct devlink_port *dlp = &dp->devlink_port; |
294 | struct dsa_switch_tree *dst = dp->ds->dst; |
295 | struct devlink_port_attrs attrs = {}; |
296 | struct devlink *dl = dp->ds->devlink; |
297 | struct dsa_switch *ds = dp->ds; |
298 | const unsigned char *id; |
299 | unsigned char len; |
300 | int err; |
301 | |
302 | memset(dlp, 0, sizeof(*dlp)); |
303 | devlink_port_init(devlink: dl, devlink_port: dlp); |
304 | |
305 | if (ds->ops->port_setup) { |
306 | err = ds->ops->port_setup(ds, dp->index); |
307 | if (err) |
308 | return err; |
309 | } |
310 | |
311 | id = (const unsigned char *)&dst->index; |
312 | len = sizeof(dst->index); |
313 | |
314 | attrs.phys.port_number = dp->index; |
315 | memcpy(attrs.switch_id.id, id, len); |
316 | attrs.switch_id.id_len = len; |
317 | |
318 | switch (dp->type) { |
319 | case DSA_PORT_TYPE_UNUSED: |
320 | attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED; |
321 | break; |
322 | case DSA_PORT_TYPE_CPU: |
323 | attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU; |
324 | break; |
325 | case DSA_PORT_TYPE_DSA: |
326 | attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA; |
327 | break; |
328 | case DSA_PORT_TYPE_USER: |
329 | attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; |
330 | break; |
331 | } |
332 | |
333 | devlink_port_attrs_set(devlink_port: dlp, devlink_port_attrs: &attrs); |
334 | err = devlink_port_register(devlink: dl, devlink_port: dlp, port_index: dp->index); |
335 | if (err) { |
336 | if (ds->ops->port_teardown) |
337 | ds->ops->port_teardown(ds, dp->index); |
338 | return err; |
339 | } |
340 | |
341 | return 0; |
342 | } |
343 | |
344 | void dsa_port_devlink_teardown(struct dsa_port *dp) |
345 | { |
346 | struct devlink_port *dlp = &dp->devlink_port; |
347 | struct dsa_switch *ds = dp->ds; |
348 | |
349 | devlink_port_unregister(devlink_port: dlp); |
350 | |
351 | if (ds->ops->port_teardown) |
352 | ds->ops->port_teardown(ds, dp->index); |
353 | |
354 | devlink_port_fini(devlink_port: dlp); |
355 | } |
356 | |
357 | void dsa_switch_devlink_register(struct dsa_switch *ds) |
358 | { |
359 | devlink_register(devlink: ds->devlink); |
360 | } |
361 | |
362 | void dsa_switch_devlink_unregister(struct dsa_switch *ds) |
363 | { |
364 | devlink_unregister(devlink: ds->devlink); |
365 | } |
366 | |
367 | int dsa_switch_devlink_alloc(struct dsa_switch *ds) |
368 | { |
369 | struct dsa_devlink_priv *dl_priv; |
370 | struct devlink *dl; |
371 | |
372 | /* Add the switch to devlink before calling setup, so that setup can |
373 | * add dpipe tables |
374 | */ |
375 | dl = devlink_alloc(ops: &dsa_devlink_ops, priv_size: sizeof(*dl_priv), dev: ds->dev); |
376 | if (!dl) |
377 | return -ENOMEM; |
378 | |
379 | ds->devlink = dl; |
380 | |
381 | dl_priv = devlink_priv(devlink: ds->devlink); |
382 | dl_priv->ds = ds; |
383 | |
384 | return 0; |
385 | } |
386 | |
387 | void dsa_switch_devlink_free(struct dsa_switch *ds) |
388 | { |
389 | devlink_free(devlink: ds->devlink); |
390 | ds->devlink = NULL; |
391 | } |
392 | |