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