xref: /openbmc/linux/net/devlink/linecard.c (revision 6db6b729)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5  */
6 
7 #include "devl_internal.h"
8 
9 static struct devlink_linecard *
10 devlink_linecard_get_by_index(struct devlink *devlink,
11 			      unsigned int linecard_index)
12 {
13 	struct devlink_linecard *devlink_linecard;
14 
15 	list_for_each_entry(devlink_linecard, &devlink->linecard_list, list) {
16 		if (devlink_linecard->index == linecard_index)
17 			return devlink_linecard;
18 	}
19 	return NULL;
20 }
21 
22 static bool devlink_linecard_index_exists(struct devlink *devlink,
23 					  unsigned int linecard_index)
24 {
25 	return devlink_linecard_get_by_index(devlink, linecard_index);
26 }
27 
28 static struct devlink_linecard *
29 devlink_linecard_get_from_attrs(struct devlink *devlink, struct nlattr **attrs)
30 {
31 	if (attrs[DEVLINK_ATTR_LINECARD_INDEX]) {
32 		u32 linecard_index = nla_get_u32(attrs[DEVLINK_ATTR_LINECARD_INDEX]);
33 		struct devlink_linecard *linecard;
34 
35 		linecard = devlink_linecard_get_by_index(devlink, linecard_index);
36 		if (!linecard)
37 			return ERR_PTR(-ENODEV);
38 		return linecard;
39 	}
40 	return ERR_PTR(-EINVAL);
41 }
42 
43 static struct devlink_linecard *
44 devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info)
45 {
46 	return devlink_linecard_get_from_attrs(devlink, info->attrs);
47 }
48 
49 static int devlink_nl_put_nested_handle(struct sk_buff *msg, struct devlink *devlink)
50 {
51 	struct nlattr *nested_attr;
52 
53 	nested_attr = nla_nest_start(msg, DEVLINK_ATTR_NESTED_DEVLINK);
54 	if (!nested_attr)
55 		return -EMSGSIZE;
56 	if (devlink_nl_put_handle(msg, devlink))
57 		goto nla_put_failure;
58 
59 	nla_nest_end(msg, nested_attr);
60 	return 0;
61 
62 nla_put_failure:
63 	nla_nest_cancel(msg, nested_attr);
64 	return -EMSGSIZE;
65 }
66 
67 struct devlink_linecard_type {
68 	const char *type;
69 	const void *priv;
70 };
71 
72 static int devlink_nl_linecard_fill(struct sk_buff *msg,
73 				    struct devlink *devlink,
74 				    struct devlink_linecard *linecard,
75 				    enum devlink_command cmd, u32 portid,
76 				    u32 seq, int flags,
77 				    struct netlink_ext_ack *extack)
78 {
79 	struct devlink_linecard_type *linecard_type;
80 	struct nlattr *attr;
81 	void *hdr;
82 	int i;
83 
84 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
85 	if (!hdr)
86 		return -EMSGSIZE;
87 
88 	if (devlink_nl_put_handle(msg, devlink))
89 		goto nla_put_failure;
90 	if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index))
91 		goto nla_put_failure;
92 	if (nla_put_u8(msg, DEVLINK_ATTR_LINECARD_STATE, linecard->state))
93 		goto nla_put_failure;
94 	if (linecard->type &&
95 	    nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, linecard->type))
96 		goto nla_put_failure;
97 
98 	if (linecard->types_count) {
99 		attr = nla_nest_start(msg,
100 				      DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES);
101 		if (!attr)
102 			goto nla_put_failure;
103 		for (i = 0; i < linecard->types_count; i++) {
104 			linecard_type = &linecard->types[i];
105 			if (nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE,
106 					   linecard_type->type)) {
107 				nla_nest_cancel(msg, attr);
108 				goto nla_put_failure;
109 			}
110 		}
111 		nla_nest_end(msg, attr);
112 	}
113 
114 	if (linecard->nested_devlink &&
115 	    devlink_nl_put_nested_handle(msg, linecard->nested_devlink))
116 		goto nla_put_failure;
117 
118 	genlmsg_end(msg, hdr);
119 	return 0;
120 
121 nla_put_failure:
122 	genlmsg_cancel(msg, hdr);
123 	return -EMSGSIZE;
124 }
125 
126 static void devlink_linecard_notify(struct devlink_linecard *linecard,
127 				    enum devlink_command cmd)
128 {
129 	struct devlink *devlink = linecard->devlink;
130 	struct sk_buff *msg;
131 	int err;
132 
133 	WARN_ON(cmd != DEVLINK_CMD_LINECARD_NEW &&
134 		cmd != DEVLINK_CMD_LINECARD_DEL);
135 
136 	if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
137 		return;
138 
139 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
140 	if (!msg)
141 		return;
142 
143 	err = devlink_nl_linecard_fill(msg, devlink, linecard, cmd, 0, 0, 0,
144 				       NULL);
145 	if (err) {
146 		nlmsg_free(msg);
147 		return;
148 	}
149 
150 	genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
151 				msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
152 }
153 
154 void devlink_linecards_notify_register(struct devlink *devlink)
155 {
156 	struct devlink_linecard *linecard;
157 
158 	list_for_each_entry(linecard, &devlink->linecard_list, list)
159 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
160 }
161 
162 void devlink_linecards_notify_unregister(struct devlink *devlink)
163 {
164 	struct devlink_linecard *linecard;
165 
166 	list_for_each_entry_reverse(linecard, &devlink->linecard_list, list)
167 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
168 }
169 
170 int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info)
171 {
172 	struct devlink *devlink = info->user_ptr[0];
173 	struct devlink_linecard *linecard;
174 	struct sk_buff *msg;
175 	int err;
176 
177 	linecard = devlink_linecard_get_from_info(devlink, info);
178 	if (IS_ERR(linecard))
179 		return PTR_ERR(linecard);
180 
181 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
182 	if (!msg)
183 		return -ENOMEM;
184 
185 	mutex_lock(&linecard->state_lock);
186 	err = devlink_nl_linecard_fill(msg, devlink, linecard,
187 				       DEVLINK_CMD_LINECARD_NEW,
188 				       info->snd_portid, info->snd_seq, 0,
189 				       info->extack);
190 	mutex_unlock(&linecard->state_lock);
191 	if (err) {
192 		nlmsg_free(msg);
193 		return err;
194 	}
195 
196 	return genlmsg_reply(msg, info);
197 }
198 
199 static int devlink_nl_linecard_get_dump_one(struct sk_buff *msg,
200 					    struct devlink *devlink,
201 					    struct netlink_callback *cb,
202 					    int flags)
203 {
204 	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
205 	struct devlink_linecard *linecard;
206 	int idx = 0;
207 	int err = 0;
208 
209 	list_for_each_entry(linecard, &devlink->linecard_list, list) {
210 		if (idx < state->idx) {
211 			idx++;
212 			continue;
213 		}
214 		mutex_lock(&linecard->state_lock);
215 		err = devlink_nl_linecard_fill(msg, devlink, linecard,
216 					       DEVLINK_CMD_LINECARD_NEW,
217 					       NETLINK_CB(cb->skb).portid,
218 					       cb->nlh->nlmsg_seq, flags,
219 					       cb->extack);
220 		mutex_unlock(&linecard->state_lock);
221 		if (err) {
222 			state->idx = idx;
223 			break;
224 		}
225 		idx++;
226 	}
227 
228 	return err;
229 }
230 
231 int devlink_nl_linecard_get_dumpit(struct sk_buff *skb,
232 				   struct netlink_callback *cb)
233 {
234 	return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one);
235 }
236 
237 static struct devlink_linecard_type *
238 devlink_linecard_type_lookup(struct devlink_linecard *linecard,
239 			     const char *type)
240 {
241 	struct devlink_linecard_type *linecard_type;
242 	int i;
243 
244 	for (i = 0; i < linecard->types_count; i++) {
245 		linecard_type = &linecard->types[i];
246 		if (!strcmp(type, linecard_type->type))
247 			return linecard_type;
248 	}
249 	return NULL;
250 }
251 
252 static int devlink_linecard_type_set(struct devlink_linecard *linecard,
253 				     const char *type,
254 				     struct netlink_ext_ack *extack)
255 {
256 	const struct devlink_linecard_ops *ops = linecard->ops;
257 	struct devlink_linecard_type *linecard_type;
258 	int err;
259 
260 	mutex_lock(&linecard->state_lock);
261 	if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
262 		NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
263 		err = -EBUSY;
264 		goto out;
265 	}
266 	if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
267 		NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
268 		err = -EBUSY;
269 		goto out;
270 	}
271 
272 	linecard_type = devlink_linecard_type_lookup(linecard, type);
273 	if (!linecard_type) {
274 		NL_SET_ERR_MSG(extack, "Unsupported line card type provided");
275 		err = -EINVAL;
276 		goto out;
277 	}
278 
279 	if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED &&
280 	    linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
281 		NL_SET_ERR_MSG(extack, "Line card already provisioned");
282 		err = -EBUSY;
283 		/* Check if the line card is provisioned in the same
284 		 * way the user asks. In case it is, make the operation
285 		 * to return success.
286 		 */
287 		if (ops->same_provision &&
288 		    ops->same_provision(linecard, linecard->priv,
289 					linecard_type->type,
290 					linecard_type->priv))
291 			err = 0;
292 		goto out;
293 	}
294 
295 	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING;
296 	linecard->type = linecard_type->type;
297 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
298 	mutex_unlock(&linecard->state_lock);
299 	err = ops->provision(linecard, linecard->priv, linecard_type->type,
300 			     linecard_type->priv, extack);
301 	if (err) {
302 		/* Provisioning failed. Assume the linecard is unprovisioned
303 		 * for future operations.
304 		 */
305 		mutex_lock(&linecard->state_lock);
306 		linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
307 		linecard->type = NULL;
308 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
309 		mutex_unlock(&linecard->state_lock);
310 	}
311 	return err;
312 
313 out:
314 	mutex_unlock(&linecard->state_lock);
315 	return err;
316 }
317 
318 static int devlink_linecard_type_unset(struct devlink_linecard *linecard,
319 				       struct netlink_ext_ack *extack)
320 {
321 	int err;
322 
323 	mutex_lock(&linecard->state_lock);
324 	if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
325 		NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
326 		err = -EBUSY;
327 		goto out;
328 	}
329 	if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
330 		NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
331 		err = -EBUSY;
332 		goto out;
333 	}
334 	if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
335 		linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
336 		linecard->type = NULL;
337 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
338 		err = 0;
339 		goto out;
340 	}
341 
342 	if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) {
343 		NL_SET_ERR_MSG(extack, "Line card is not provisioned");
344 		err = 0;
345 		goto out;
346 	}
347 	linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING;
348 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
349 	mutex_unlock(&linecard->state_lock);
350 	err = linecard->ops->unprovision(linecard, linecard->priv,
351 					 extack);
352 	if (err) {
353 		/* Unprovisioning failed. Assume the linecard is unprovisioned
354 		 * for future operations.
355 		 */
356 		mutex_lock(&linecard->state_lock);
357 		linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
358 		linecard->type = NULL;
359 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
360 		mutex_unlock(&linecard->state_lock);
361 	}
362 	return err;
363 
364 out:
365 	mutex_unlock(&linecard->state_lock);
366 	return err;
367 }
368 
369 int devlink_nl_cmd_linecard_set_doit(struct sk_buff *skb,
370 				     struct genl_info *info)
371 {
372 	struct netlink_ext_ack *extack = info->extack;
373 	struct devlink *devlink = info->user_ptr[0];
374 	struct devlink_linecard *linecard;
375 	int err;
376 
377 	linecard = devlink_linecard_get_from_info(devlink, info);
378 	if (IS_ERR(linecard))
379 		return PTR_ERR(linecard);
380 
381 	if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
382 		const char *type;
383 
384 		type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);
385 		if (strcmp(type, "")) {
386 			err = devlink_linecard_type_set(linecard, type, extack);
387 			if (err)
388 				return err;
389 		} else {
390 			err = devlink_linecard_type_unset(linecard, extack);
391 			if (err)
392 				return err;
393 		}
394 	}
395 
396 	return 0;
397 }
398 
399 static int devlink_linecard_types_init(struct devlink_linecard *linecard)
400 {
401 	struct devlink_linecard_type *linecard_type;
402 	unsigned int count;
403 	int i;
404 
405 	count = linecard->ops->types_count(linecard, linecard->priv);
406 	linecard->types = kmalloc_array(count, sizeof(*linecard_type),
407 					GFP_KERNEL);
408 	if (!linecard->types)
409 		return -ENOMEM;
410 	linecard->types_count = count;
411 
412 	for (i = 0; i < count; i++) {
413 		linecard_type = &linecard->types[i];
414 		linecard->ops->types_get(linecard, linecard->priv, i,
415 					 &linecard_type->type,
416 					 &linecard_type->priv);
417 	}
418 	return 0;
419 }
420 
421 static void devlink_linecard_types_fini(struct devlink_linecard *linecard)
422 {
423 	kfree(linecard->types);
424 }
425 
426 /**
427  *	devl_linecard_create - Create devlink linecard
428  *
429  *	@devlink: devlink
430  *	@linecard_index: driver-specific numerical identifier of the linecard
431  *	@ops: linecards ops
432  *	@priv: user priv pointer
433  *
434  *	Create devlink linecard instance with provided linecard index.
435  *	Caller can use any indexing, even hw-related one.
436  *
437  *	Return: Line card structure or an ERR_PTR() encoded error code.
438  */
439 struct devlink_linecard *
440 devl_linecard_create(struct devlink *devlink, unsigned int linecard_index,
441 		     const struct devlink_linecard_ops *ops, void *priv)
442 {
443 	struct devlink_linecard *linecard;
444 	int err;
445 
446 	if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||
447 		    !ops->types_count || !ops->types_get))
448 		return ERR_PTR(-EINVAL);
449 
450 	if (devlink_linecard_index_exists(devlink, linecard_index))
451 		return ERR_PTR(-EEXIST);
452 
453 	linecard = kzalloc(sizeof(*linecard), GFP_KERNEL);
454 	if (!linecard)
455 		return ERR_PTR(-ENOMEM);
456 
457 	linecard->devlink = devlink;
458 	linecard->index = linecard_index;
459 	linecard->ops = ops;
460 	linecard->priv = priv;
461 	linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
462 	mutex_init(&linecard->state_lock);
463 
464 	err = devlink_linecard_types_init(linecard);
465 	if (err) {
466 		mutex_destroy(&linecard->state_lock);
467 		kfree(linecard);
468 		return ERR_PTR(err);
469 	}
470 
471 	list_add_tail(&linecard->list, &devlink->linecard_list);
472 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
473 	return linecard;
474 }
475 EXPORT_SYMBOL_GPL(devl_linecard_create);
476 
477 /**
478  *	devl_linecard_destroy - Destroy devlink linecard
479  *
480  *	@linecard: devlink linecard
481  */
482 void devl_linecard_destroy(struct devlink_linecard *linecard)
483 {
484 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
485 	list_del(&linecard->list);
486 	devlink_linecard_types_fini(linecard);
487 	mutex_destroy(&linecard->state_lock);
488 	kfree(linecard);
489 }
490 EXPORT_SYMBOL_GPL(devl_linecard_destroy);
491 
492 /**
493  *	devlink_linecard_provision_set - Set provisioning on linecard
494  *
495  *	@linecard: devlink linecard
496  *	@type: linecard type
497  *
498  *	This is either called directly from the provision() op call or
499  *	as a result of the provision() op call asynchronously.
500  */
501 void devlink_linecard_provision_set(struct devlink_linecard *linecard,
502 				    const char *type)
503 {
504 	mutex_lock(&linecard->state_lock);
505 	WARN_ON(linecard->type && strcmp(linecard->type, type));
506 	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
507 	linecard->type = type;
508 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
509 	mutex_unlock(&linecard->state_lock);
510 }
511 EXPORT_SYMBOL_GPL(devlink_linecard_provision_set);
512 
513 /**
514  *	devlink_linecard_provision_clear - Clear provisioning on linecard
515  *
516  *	@linecard: devlink linecard
517  *
518  *	This is either called directly from the unprovision() op call or
519  *	as a result of the unprovision() op call asynchronously.
520  */
521 void devlink_linecard_provision_clear(struct devlink_linecard *linecard)
522 {
523 	mutex_lock(&linecard->state_lock);
524 	WARN_ON(linecard->nested_devlink);
525 	linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
526 	linecard->type = NULL;
527 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
528 	mutex_unlock(&linecard->state_lock);
529 }
530 EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear);
531 
532 /**
533  *	devlink_linecard_provision_fail - Fail provisioning on linecard
534  *
535  *	@linecard: devlink linecard
536  *
537  *	This is either called directly from the provision() op call or
538  *	as a result of the provision() op call asynchronously.
539  */
540 void devlink_linecard_provision_fail(struct devlink_linecard *linecard)
541 {
542 	mutex_lock(&linecard->state_lock);
543 	WARN_ON(linecard->nested_devlink);
544 	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
545 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
546 	mutex_unlock(&linecard->state_lock);
547 }
548 EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
549 
550 /**
551  *	devlink_linecard_activate - Set linecard active
552  *
553  *	@linecard: devlink linecard
554  */
555 void devlink_linecard_activate(struct devlink_linecard *linecard)
556 {
557 	mutex_lock(&linecard->state_lock);
558 	WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED);
559 	linecard->state = DEVLINK_LINECARD_STATE_ACTIVE;
560 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
561 	mutex_unlock(&linecard->state_lock);
562 }
563 EXPORT_SYMBOL_GPL(devlink_linecard_activate);
564 
565 /**
566  *	devlink_linecard_deactivate - Set linecard inactive
567  *
568  *	@linecard: devlink linecard
569  */
570 void devlink_linecard_deactivate(struct devlink_linecard *linecard)
571 {
572 	mutex_lock(&linecard->state_lock);
573 	switch (linecard->state) {
574 	case DEVLINK_LINECARD_STATE_ACTIVE:
575 		linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
576 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
577 		break;
578 	case DEVLINK_LINECARD_STATE_UNPROVISIONING:
579 		/* Line card is being deactivated as part
580 		 * of unprovisioning flow.
581 		 */
582 		break;
583 	default:
584 		WARN_ON(1);
585 		break;
586 	}
587 	mutex_unlock(&linecard->state_lock);
588 }
589 EXPORT_SYMBOL_GPL(devlink_linecard_deactivate);
590 
591 /**
592  *	devlink_linecard_nested_dl_set - Attach/detach nested devlink
593  *					 instance to linecard.
594  *
595  *	@linecard: devlink linecard
596  *	@nested_devlink: devlink instance to attach or NULL to detach
597  */
598 void devlink_linecard_nested_dl_set(struct devlink_linecard *linecard,
599 				    struct devlink *nested_devlink)
600 {
601 	mutex_lock(&linecard->state_lock);
602 	linecard->nested_devlink = nested_devlink;
603 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
604 	mutex_unlock(&linecard->state_lock);
605 }
606 EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);
607