Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.14.15.
  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
  9struct 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
 23unsigned int devlink_linecard_index(struct devlink_linecard *linecard)
 24{
 25	return linecard->index;
 26}
 27
 28static struct devlink_linecard *
 29devlink_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
 41static 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
 47static struct devlink_linecard *
 48devlink_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
 62static struct devlink_linecard *
 63devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info)
 64{
 65	return devlink_linecard_get_from_attrs(devlink, info->attrs);
 66}
 67
 68struct devlink_linecard_type {
 69	const char *type;
 70	const void *priv;
 71};
 72
 73static 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
124nla_put_failure:
125	genlmsg_cancel(msg, hdr);
126	return -EMSGSIZE;
127}
128
129static 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
156void 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
164void 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
172int 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
201static 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
233int 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
239static struct devlink_linecard_type *
240devlink_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
254static 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
315out:
316	mutex_unlock(&linecard->state_lock);
317	return err;
318}
319
320static 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
366out:
367	mutex_unlock(&linecard->state_lock);
368	return err;
369}
370
371int 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
400static 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
422static 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 */
440struct devlink_linecard *
441devl_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}
476EXPORT_SYMBOL_GPL(devl_linecard_create);
477
478/**
479 *	devl_linecard_destroy - Destroy devlink linecard
480 *
481 *	@linecard: devlink linecard
482 */
483void 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}
491EXPORT_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 */
502void 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}
512EXPORT_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 */
522void 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}
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	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
544	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
545	mutex_unlock(&linecard->state_lock);
546}
547EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
548
549/**
550 *	devlink_linecard_activate - Set linecard active
551 *
552 *	@linecard: devlink linecard
553 */
554void 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}
562EXPORT_SYMBOL_GPL(devlink_linecard_activate);
563
564/**
565 *	devlink_linecard_deactivate - Set linecard inactive
566 *
567 *	@linecard: devlink linecard
568 */
569void 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}
588EXPORT_SYMBOL_GPL(devlink_linecard_deactivate);
589
590static 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
601static 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 */
618int 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}
628EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);