xref: /kernel/linux/linux-6.6/net/devlink/linecard.c (revision 62306a36)
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
9static struct devlink_linecard *
10devlink_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
22static 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
28static struct devlink_linecard *
29devlink_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
43static struct devlink_linecard *
44devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info)
45{
46	return devlink_linecard_get_from_attrs(devlink, info->attrs);
47}
48
49static 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
62nla_put_failure:
63	nla_nest_cancel(msg, nested_attr);
64	return -EMSGSIZE;
65}
66
67struct devlink_linecard_type {
68	const char *type;
69	const void *priv;
70};
71
72static 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
121nla_put_failure:
122	genlmsg_cancel(msg, hdr);
123	return -EMSGSIZE;
124}
125
126static 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
154void 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
162void 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
170int 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
199static 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
231int 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
237static struct devlink_linecard_type *
238devlink_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
252static 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
313out:
314	mutex_unlock(&linecard->state_lock);
315	return err;
316}
317
318static 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
364out:
365	mutex_unlock(&linecard->state_lock);
366	return err;
367}
368
369int 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
399static 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
421static 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 */
439struct devlink_linecard *
440devl_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}
475EXPORT_SYMBOL_GPL(devl_linecard_create);
476
477/**
478 *	devl_linecard_destroy - Destroy devlink linecard
479 *
480 *	@linecard: devlink linecard
481 */
482void 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}
490EXPORT_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 */
501void 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}
511EXPORT_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 */
521void 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}
530EXPORT_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 */
540void 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}
548EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
549
550/**
551 *	devlink_linecard_activate - Set linecard active
552 *
553 *	@linecard: devlink linecard
554 */
555void 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}
563EXPORT_SYMBOL_GPL(devlink_linecard_activate);
564
565/**
566 *	devlink_linecard_deactivate - Set linecard inactive
567 *
568 *	@linecard: devlink linecard
569 */
570void 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}
589EXPORT_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 */
598void 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}
606EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);
607