xref: /linux/net/devlink/linecard.c (revision 04317b129e4eb5c6f4a58bb899b2019c1545320b)
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_cmd_linecard_set_doit(struct sk_buff *skb,
373 				     struct genl_info *info)
374 {
375 	struct netlink_ext_ack *extack = info->extack;
376 	struct devlink *devlink = info->user_ptr[0];
377 	struct devlink_linecard *linecard;
378 	int err;
379 
380 	linecard = devlink_linecard_get_from_info(devlink, info);
381 	if (IS_ERR(linecard))
382 		return PTR_ERR(linecard);
383 
384 	if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
385 		const char *type;
386 
387 		type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);
388 		if (strcmp(type, "")) {
389 			err = devlink_linecard_type_set(linecard, type, extack);
390 			if (err)
391 				return err;
392 		} else {
393 			err = devlink_linecard_type_unset(linecard, extack);
394 			if (err)
395 				return err;
396 		}
397 	}
398 
399 	return 0;
400 }
401 
402 static int devlink_linecard_types_init(struct devlink_linecard *linecard)
403 {
404 	struct devlink_linecard_type *linecard_type;
405 	unsigned int count;
406 	int i;
407 
408 	count = linecard->ops->types_count(linecard, linecard->priv);
409 	linecard->types = kmalloc_array(count, sizeof(*linecard_type),
410 					GFP_KERNEL);
411 	if (!linecard->types)
412 		return -ENOMEM;
413 	linecard->types_count = count;
414 
415 	for (i = 0; i < count; i++) {
416 		linecard_type = &linecard->types[i];
417 		linecard->ops->types_get(linecard, linecard->priv, i,
418 					 &linecard_type->type,
419 					 &linecard_type->priv);
420 	}
421 	return 0;
422 }
423 
424 static void devlink_linecard_types_fini(struct devlink_linecard *linecard)
425 {
426 	kfree(linecard->types);
427 }
428 
429 /**
430  *	devl_linecard_create - Create devlink linecard
431  *
432  *	@devlink: devlink
433  *	@linecard_index: driver-specific numerical identifier of the linecard
434  *	@ops: linecards ops
435  *	@priv: user priv pointer
436  *
437  *	Create devlink linecard instance with provided linecard index.
438  *	Caller can use any indexing, even hw-related one.
439  *
440  *	Return: Line card structure or an ERR_PTR() encoded error code.
441  */
442 struct devlink_linecard *
443 devl_linecard_create(struct devlink *devlink, unsigned int linecard_index,
444 		     const struct devlink_linecard_ops *ops, void *priv)
445 {
446 	struct devlink_linecard *linecard;
447 	int err;
448 
449 	if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||
450 		    !ops->types_count || !ops->types_get))
451 		return ERR_PTR(-EINVAL);
452 
453 	if (devlink_linecard_index_exists(devlink, linecard_index))
454 		return ERR_PTR(-EEXIST);
455 
456 	linecard = kzalloc(sizeof(*linecard), GFP_KERNEL);
457 	if (!linecard)
458 		return ERR_PTR(-ENOMEM);
459 
460 	linecard->devlink = devlink;
461 	linecard->index = linecard_index;
462 	linecard->ops = ops;
463 	linecard->priv = priv;
464 	linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
465 	mutex_init(&linecard->state_lock);
466 
467 	err = devlink_linecard_types_init(linecard);
468 	if (err) {
469 		mutex_destroy(&linecard->state_lock);
470 		kfree(linecard);
471 		return ERR_PTR(err);
472 	}
473 
474 	list_add_tail(&linecard->list, &devlink->linecard_list);
475 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
476 	return linecard;
477 }
478 EXPORT_SYMBOL_GPL(devl_linecard_create);
479 
480 /**
481  *	devl_linecard_destroy - Destroy devlink linecard
482  *
483  *	@linecard: devlink linecard
484  */
485 void devl_linecard_destroy(struct devlink_linecard *linecard)
486 {
487 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
488 	list_del(&linecard->list);
489 	devlink_linecard_types_fini(linecard);
490 	mutex_destroy(&linecard->state_lock);
491 	kfree(linecard);
492 }
493 EXPORT_SYMBOL_GPL(devl_linecard_destroy);
494 
495 /**
496  *	devlink_linecard_provision_set - Set provisioning on linecard
497  *
498  *	@linecard: devlink linecard
499  *	@type: linecard type
500  *
501  *	This is either called directly from the provision() op call or
502  *	as a result of the provision() op call asynchronously.
503  */
504 void devlink_linecard_provision_set(struct devlink_linecard *linecard,
505 				    const char *type)
506 {
507 	mutex_lock(&linecard->state_lock);
508 	WARN_ON(linecard->type && strcmp(linecard->type, type));
509 	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
510 	linecard->type = type;
511 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
512 	mutex_unlock(&linecard->state_lock);
513 }
514 EXPORT_SYMBOL_GPL(devlink_linecard_provision_set);
515 
516 /**
517  *	devlink_linecard_provision_clear - Clear provisioning on linecard
518  *
519  *	@linecard: devlink linecard
520  *
521  *	This is either called directly from the unprovision() op call or
522  *	as a result of the unprovision() op call asynchronously.
523  */
524 void devlink_linecard_provision_clear(struct devlink_linecard *linecard)
525 {
526 	mutex_lock(&linecard->state_lock);
527 	linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
528 	linecard->type = NULL;
529 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
530 	mutex_unlock(&linecard->state_lock);
531 }
532 EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear);
533 
534 /**
535  *	devlink_linecard_provision_fail - Fail provisioning on linecard
536  *
537  *	@linecard: devlink linecard
538  *
539  *	This is either called directly from the provision() op call or
540  *	as a result of the provision() op call asynchronously.
541  */
542 void devlink_linecard_provision_fail(struct devlink_linecard *linecard)
543 {
544 	mutex_lock(&linecard->state_lock);
545 	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
546 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
547 	mutex_unlock(&linecard->state_lock);
548 }
549 EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
550 
551 /**
552  *	devlink_linecard_activate - Set linecard active
553  *
554  *	@linecard: devlink linecard
555  */
556 void devlink_linecard_activate(struct devlink_linecard *linecard)
557 {
558 	mutex_lock(&linecard->state_lock);
559 	WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED);
560 	linecard->state = DEVLINK_LINECARD_STATE_ACTIVE;
561 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
562 	mutex_unlock(&linecard->state_lock);
563 }
564 EXPORT_SYMBOL_GPL(devlink_linecard_activate);
565 
566 /**
567  *	devlink_linecard_deactivate - Set linecard inactive
568  *
569  *	@linecard: devlink linecard
570  */
571 void devlink_linecard_deactivate(struct devlink_linecard *linecard)
572 {
573 	mutex_lock(&linecard->state_lock);
574 	switch (linecard->state) {
575 	case DEVLINK_LINECARD_STATE_ACTIVE:
576 		linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
577 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
578 		break;
579 	case DEVLINK_LINECARD_STATE_UNPROVISIONING:
580 		/* Line card is being deactivated as part
581 		 * of unprovisioning flow.
582 		 */
583 		break;
584 	default:
585 		WARN_ON(1);
586 		break;
587 	}
588 	mutex_unlock(&linecard->state_lock);
589 }
590 EXPORT_SYMBOL_GPL(devlink_linecard_deactivate);
591 
592 static void devlink_linecard_rel_notify_cb(struct devlink *devlink,
593 					   u32 linecard_index)
594 {
595 	struct devlink_linecard *linecard;
596 
597 	linecard = devlink_linecard_get_by_index(devlink, linecard_index);
598 	if (!linecard)
599 		return;
600 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
601 }
602 
603 static void devlink_linecard_rel_cleanup_cb(struct devlink *devlink,
604 					    u32 linecard_index, u32 rel_index)
605 {
606 	struct devlink_linecard *linecard;
607 
608 	linecard = devlink_linecard_get_by_index(devlink, linecard_index);
609 	if (linecard && linecard->rel_index == rel_index)
610 		linecard->rel_index = 0;
611 }
612 
613 /**
614  *	devlink_linecard_nested_dl_set - Attach/detach nested devlink
615  *					 instance to linecard.
616  *
617  *	@linecard: devlink linecard
618  *	@nested_devlink: devlink instance to attach or NULL to detach
619  */
620 int devlink_linecard_nested_dl_set(struct devlink_linecard *linecard,
621 				   struct devlink *nested_devlink)
622 {
623 	return devlink_rel_nested_in_add(&linecard->rel_index,
624 					 linecard->devlink->index,
625 					 linecard->index,
626 					 devlink_linecard_rel_notify_cb,
627 					 devlink_linecard_rel_cleanup_cb,
628 					 nested_devlink);
629 }
630 EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);
631