xref: /linux/net/devlink/linecard.c (revision 6e7fd890f1d6ac83805409e9c346240de2705584)
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 (!__devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))
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 	devlink_nl_notify_send(devlink, msg);
154 }
155 
156 void devlink_linecards_notify_register(struct devlink *devlink)
157 {
158 	struct devlink_linecard *linecard;
159 
160 	list_for_each_entry(linecard, &devlink->linecard_list, list)
161 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
162 }
163 
164 void devlink_linecards_notify_unregister(struct devlink *devlink)
165 {
166 	struct devlink_linecard *linecard;
167 
168 	list_for_each_entry_reverse(linecard, &devlink->linecard_list, list)
169 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
170 }
171 
172 int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info)
173 {
174 	struct devlink *devlink = info->user_ptr[0];
175 	struct devlink_linecard *linecard;
176 	struct sk_buff *msg;
177 	int err;
178 
179 	linecard = devlink_linecard_get_from_info(devlink, info);
180 	if (IS_ERR(linecard))
181 		return PTR_ERR(linecard);
182 
183 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
184 	if (!msg)
185 		return -ENOMEM;
186 
187 	mutex_lock(&linecard->state_lock);
188 	err = devlink_nl_linecard_fill(msg, devlink, linecard,
189 				       DEVLINK_CMD_LINECARD_NEW,
190 				       info->snd_portid, info->snd_seq, 0,
191 				       info->extack);
192 	mutex_unlock(&linecard->state_lock);
193 	if (err) {
194 		nlmsg_free(msg);
195 		return err;
196 	}
197 
198 	return genlmsg_reply(msg, info);
199 }
200 
201 static int devlink_nl_linecard_get_dump_one(struct sk_buff *msg,
202 					    struct devlink *devlink,
203 					    struct netlink_callback *cb,
204 					    int flags)
205 {
206 	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
207 	struct devlink_linecard *linecard;
208 	int idx = 0;
209 	int err = 0;
210 
211 	list_for_each_entry(linecard, &devlink->linecard_list, list) {
212 		if (idx < state->idx) {
213 			idx++;
214 			continue;
215 		}
216 		mutex_lock(&linecard->state_lock);
217 		err = devlink_nl_linecard_fill(msg, devlink, linecard,
218 					       DEVLINK_CMD_LINECARD_NEW,
219 					       NETLINK_CB(cb->skb).portid,
220 					       cb->nlh->nlmsg_seq, flags,
221 					       cb->extack);
222 		mutex_unlock(&linecard->state_lock);
223 		if (err) {
224 			state->idx = idx;
225 			break;
226 		}
227 		idx++;
228 	}
229 
230 	return err;
231 }
232 
233 int devlink_nl_linecard_get_dumpit(struct sk_buff *skb,
234 				   struct netlink_callback *cb)
235 {
236 	return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one);
237 }
238 
239 static struct devlink_linecard_type *
240 devlink_linecard_type_lookup(struct devlink_linecard *linecard,
241 			     const char *type)
242 {
243 	struct devlink_linecard_type *linecard_type;
244 	int i;
245 
246 	for (i = 0; i < linecard->types_count; i++) {
247 		linecard_type = &linecard->types[i];
248 		if (!strcmp(type, linecard_type->type))
249 			return linecard_type;
250 	}
251 	return NULL;
252 }
253 
254 static int devlink_linecard_type_set(struct devlink_linecard *linecard,
255 				     const char *type,
256 				     struct netlink_ext_ack *extack)
257 {
258 	const struct devlink_linecard_ops *ops = linecard->ops;
259 	struct devlink_linecard_type *linecard_type;
260 	int err;
261 
262 	mutex_lock(&linecard->state_lock);
263 	if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
264 		NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
265 		err = -EBUSY;
266 		goto out;
267 	}
268 	if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
269 		NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
270 		err = -EBUSY;
271 		goto out;
272 	}
273 
274 	linecard_type = devlink_linecard_type_lookup(linecard, type);
275 	if (!linecard_type) {
276 		NL_SET_ERR_MSG(extack, "Unsupported line card type provided");
277 		err = -EINVAL;
278 		goto out;
279 	}
280 
281 	if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED &&
282 	    linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
283 		NL_SET_ERR_MSG(extack, "Line card already provisioned");
284 		err = -EBUSY;
285 		/* Check if the line card is provisioned in the same
286 		 * way the user asks. In case it is, make the operation
287 		 * to return success.
288 		 */
289 		if (ops->same_provision &&
290 		    ops->same_provision(linecard, linecard->priv,
291 					linecard_type->type,
292 					linecard_type->priv))
293 			err = 0;
294 		goto out;
295 	}
296 
297 	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING;
298 	linecard->type = linecard_type->type;
299 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
300 	mutex_unlock(&linecard->state_lock);
301 	err = ops->provision(linecard, linecard->priv, linecard_type->type,
302 			     linecard_type->priv, extack);
303 	if (err) {
304 		/* Provisioning failed. Assume the linecard is unprovisioned
305 		 * for future operations.
306 		 */
307 		mutex_lock(&linecard->state_lock);
308 		linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
309 		linecard->type = NULL;
310 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
311 		mutex_unlock(&linecard->state_lock);
312 	}
313 	return err;
314 
315 out:
316 	mutex_unlock(&linecard->state_lock);
317 	return err;
318 }
319 
320 static int devlink_linecard_type_unset(struct devlink_linecard *linecard,
321 				       struct netlink_ext_ack *extack)
322 {
323 	int err;
324 
325 	mutex_lock(&linecard->state_lock);
326 	if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
327 		NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
328 		err = -EBUSY;
329 		goto out;
330 	}
331 	if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
332 		NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
333 		err = -EBUSY;
334 		goto out;
335 	}
336 	if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
337 		linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
338 		linecard->type = NULL;
339 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
340 		err = 0;
341 		goto out;
342 	}
343 
344 	if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) {
345 		NL_SET_ERR_MSG(extack, "Line card is not provisioned");
346 		err = 0;
347 		goto out;
348 	}
349 	linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING;
350 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
351 	mutex_unlock(&linecard->state_lock);
352 	err = linecard->ops->unprovision(linecard, linecard->priv,
353 					 extack);
354 	if (err) {
355 		/* Unprovisioning failed. Assume the linecard is unprovisioned
356 		 * for future operations.
357 		 */
358 		mutex_lock(&linecard->state_lock);
359 		linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
360 		linecard->type = NULL;
361 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
362 		mutex_unlock(&linecard->state_lock);
363 	}
364 	return err;
365 
366 out:
367 	mutex_unlock(&linecard->state_lock);
368 	return err;
369 }
370 
371 int devlink_nl_linecard_set_doit(struct sk_buff *skb, struct genl_info *info)
372 {
373 	struct netlink_ext_ack *extack = info->extack;
374 	struct devlink *devlink = info->user_ptr[0];
375 	struct devlink_linecard *linecard;
376 	int err;
377 
378 	linecard = devlink_linecard_get_from_info(devlink, info);
379 	if (IS_ERR(linecard))
380 		return PTR_ERR(linecard);
381 
382 	if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
383 		const char *type;
384 
385 		type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);
386 		if (strcmp(type, "")) {
387 			err = devlink_linecard_type_set(linecard, type, extack);
388 			if (err)
389 				return err;
390 		} else {
391 			err = devlink_linecard_type_unset(linecard, extack);
392 			if (err)
393 				return err;
394 		}
395 	}
396 
397 	return 0;
398 }
399 
400 static int devlink_linecard_types_init(struct devlink_linecard *linecard)
401 {
402 	struct devlink_linecard_type *linecard_type;
403 	unsigned int count;
404 	int i;
405 
406 	count = linecard->ops->types_count(linecard, linecard->priv);
407 	linecard->types = kmalloc_array(count, sizeof(*linecard_type),
408 					GFP_KERNEL);
409 	if (!linecard->types)
410 		return -ENOMEM;
411 	linecard->types_count = count;
412 
413 	for (i = 0; i < count; i++) {
414 		linecard_type = &linecard->types[i];
415 		linecard->ops->types_get(linecard, linecard->priv, i,
416 					 &linecard_type->type,
417 					 &linecard_type->priv);
418 	}
419 	return 0;
420 }
421 
422 static void devlink_linecard_types_fini(struct devlink_linecard *linecard)
423 {
424 	kfree(linecard->types);
425 }
426 
427 /**
428  *	devl_linecard_create - Create devlink linecard
429  *
430  *	@devlink: devlink
431  *	@linecard_index: driver-specific numerical identifier of the linecard
432  *	@ops: linecards ops
433  *	@priv: user priv pointer
434  *
435  *	Create devlink linecard instance with provided linecard index.
436  *	Caller can use any indexing, even hw-related one.
437  *
438  *	Return: Line card structure or an ERR_PTR() encoded error code.
439  */
440 struct devlink_linecard *
441 devl_linecard_create(struct devlink *devlink, unsigned int linecard_index,
442 		     const struct devlink_linecard_ops *ops, void *priv)
443 {
444 	struct devlink_linecard *linecard;
445 	int err;
446 
447 	if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||
448 		    !ops->types_count || !ops->types_get))
449 		return ERR_PTR(-EINVAL);
450 
451 	if (devlink_linecard_index_exists(devlink, linecard_index))
452 		return ERR_PTR(-EEXIST);
453 
454 	linecard = kzalloc(sizeof(*linecard), GFP_KERNEL);
455 	if (!linecard)
456 		return ERR_PTR(-ENOMEM);
457 
458 	linecard->devlink = devlink;
459 	linecard->index = linecard_index;
460 	linecard->ops = ops;
461 	linecard->priv = priv;
462 	linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
463 	mutex_init(&linecard->state_lock);
464 
465 	err = devlink_linecard_types_init(linecard);
466 	if (err) {
467 		mutex_destroy(&linecard->state_lock);
468 		kfree(linecard);
469 		return ERR_PTR(err);
470 	}
471 
472 	list_add_tail(&linecard->list, &devlink->linecard_list);
473 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
474 	return linecard;
475 }
476 EXPORT_SYMBOL_GPL(devl_linecard_create);
477 
478 /**
479  *	devl_linecard_destroy - Destroy devlink linecard
480  *
481  *	@linecard: devlink linecard
482  */
483 void devl_linecard_destroy(struct devlink_linecard *linecard)
484 {
485 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
486 	list_del(&linecard->list);
487 	devlink_linecard_types_fini(linecard);
488 	mutex_destroy(&linecard->state_lock);
489 	kfree(linecard);
490 }
491 EXPORT_SYMBOL_GPL(devl_linecard_destroy);
492 
493 /**
494  *	devlink_linecard_provision_set - Set provisioning on linecard
495  *
496  *	@linecard: devlink linecard
497  *	@type: linecard type
498  *
499  *	This is either called directly from the provision() op call or
500  *	as a result of the provision() op call asynchronously.
501  */
502 void devlink_linecard_provision_set(struct devlink_linecard *linecard,
503 				    const char *type)
504 {
505 	mutex_lock(&linecard->state_lock);
506 	WARN_ON(linecard->type && strcmp(linecard->type, type));
507 	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
508 	linecard->type = type;
509 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
510 	mutex_unlock(&linecard->state_lock);
511 }
512 EXPORT_SYMBOL_GPL(devlink_linecard_provision_set);
513 
514 /**
515  *	devlink_linecard_provision_clear - Clear provisioning on linecard
516  *
517  *	@linecard: devlink linecard
518  *
519  *	This is either called directly from the unprovision() op call or
520  *	as a result of the unprovision() op call asynchronously.
521  */
522 void devlink_linecard_provision_clear(struct devlink_linecard *linecard)
523 {
524 	mutex_lock(&linecard->state_lock);
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 	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
544 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
545 	mutex_unlock(&linecard->state_lock);
546 }
547 EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
548 
549 /**
550  *	devlink_linecard_activate - Set linecard active
551  *
552  *	@linecard: devlink linecard
553  */
554 void devlink_linecard_activate(struct devlink_linecard *linecard)
555 {
556 	mutex_lock(&linecard->state_lock);
557 	WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED);
558 	linecard->state = DEVLINK_LINECARD_STATE_ACTIVE;
559 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
560 	mutex_unlock(&linecard->state_lock);
561 }
562 EXPORT_SYMBOL_GPL(devlink_linecard_activate);
563 
564 /**
565  *	devlink_linecard_deactivate - Set linecard inactive
566  *
567  *	@linecard: devlink linecard
568  */
569 void devlink_linecard_deactivate(struct devlink_linecard *linecard)
570 {
571 	mutex_lock(&linecard->state_lock);
572 	switch (linecard->state) {
573 	case DEVLINK_LINECARD_STATE_ACTIVE:
574 		linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
575 		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
576 		break;
577 	case DEVLINK_LINECARD_STATE_UNPROVISIONING:
578 		/* Line card is being deactivated as part
579 		 * of unprovisioning flow.
580 		 */
581 		break;
582 	default:
583 		WARN_ON(1);
584 		break;
585 	}
586 	mutex_unlock(&linecard->state_lock);
587 }
588 EXPORT_SYMBOL_GPL(devlink_linecard_deactivate);
589 
590 static void devlink_linecard_rel_notify_cb(struct devlink *devlink,
591 					   u32 linecard_index)
592 {
593 	struct devlink_linecard *linecard;
594 
595 	linecard = devlink_linecard_get_by_index(devlink, linecard_index);
596 	if (!linecard)
597 		return;
598 	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
599 }
600 
601 static void devlink_linecard_rel_cleanup_cb(struct devlink *devlink,
602 					    u32 linecard_index, u32 rel_index)
603 {
604 	struct devlink_linecard *linecard;
605 
606 	linecard = devlink_linecard_get_by_index(devlink, linecard_index);
607 	if (linecard && linecard->rel_index == rel_index)
608 		linecard->rel_index = 0;
609 }
610 
611 /**
612  *	devlink_linecard_nested_dl_set - Attach/detach nested devlink
613  *					 instance to linecard.
614  *
615  *	@linecard: devlink linecard
616  *	@nested_devlink: devlink instance to attach or NULL to detach
617  */
618 int devlink_linecard_nested_dl_set(struct devlink_linecard *linecard,
619 				   struct devlink *nested_devlink)
620 {
621 	return devlink_rel_nested_in_add(&linecard->rel_index,
622 					 linecard->devlink->index,
623 					 linecard->index,
624 					 devlink_linecard_rel_notify_cb,
625 					 devlink_linecard_rel_cleanup_cb,
626 					 nested_devlink);
627 }
628 EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);
629