627 lines · 16.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> */ #include "devl_internal.h" struct devlink_linecard {	struct list_head list;	struct devlink *devlink;	unsigned int index;	const struct devlink_linecard_ops *ops;	void *priv;	enum devlink_linecard_state state;	struct mutex state_lock; /* Protects state */	const char *type;	struct devlink_linecard_type *types;	unsigned int types_count;	u32 rel_index;}; unsigned int devlink_linecard_index(struct devlink_linecard *linecard){	return linecard->index;} static struct devlink_linecard *devlink_linecard_get_by_index(struct devlink *devlink,			      unsigned int linecard_index){	struct devlink_linecard *devlink_linecard; 	list_for_each_entry(devlink_linecard, &devlink->linecard_list, list) {		if (devlink_linecard->index == linecard_index)			return devlink_linecard;	}	return NULL;} static bool devlink_linecard_index_exists(struct devlink *devlink,					  unsigned int linecard_index){	return devlink_linecard_get_by_index(devlink, linecard_index);} static struct devlink_linecard *devlink_linecard_get_from_attrs(struct devlink *devlink, struct nlattr **attrs){	if (attrs[DEVLINK_ATTR_LINECARD_INDEX]) {		u32 linecard_index = nla_get_u32(attrs[DEVLINK_ATTR_LINECARD_INDEX]);		struct devlink_linecard *linecard; 		linecard = devlink_linecard_get_by_index(devlink, linecard_index);		if (!linecard)			return ERR_PTR(-ENODEV);		return linecard;	}	return ERR_PTR(-EINVAL);} static struct devlink_linecard *devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info){	return devlink_linecard_get_from_attrs(devlink, info->attrs);} struct devlink_linecard_type {	const char *type;	const void *priv;}; static int devlink_nl_linecard_fill(struct sk_buff *msg,				    struct devlink *devlink,				    struct devlink_linecard *linecard,				    enum devlink_command cmd, u32 portid,				    u32 seq, int flags,				    struct netlink_ext_ack *extack){	struct devlink_linecard_type *linecard_type;	struct nlattr *attr;	void *hdr;	int i; 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);	if (!hdr)		return -EMSGSIZE; 	if (devlink_nl_put_handle(msg, devlink))		goto nla_put_failure;	if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index))		goto nla_put_failure;	if (nla_put_u8(msg, DEVLINK_ATTR_LINECARD_STATE, linecard->state))		goto nla_put_failure;	if (linecard->type &&	    nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, linecard->type))		goto nla_put_failure; 	if (linecard->types_count) {		attr = nla_nest_start(msg,				      DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES);		if (!attr)			goto nla_put_failure;		for (i = 0; i < linecard->types_count; i++) {			linecard_type = &linecard->types[i];			if (nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE,					   linecard_type->type)) {				nla_nest_cancel(msg, attr);				goto nla_put_failure;			}		}		nla_nest_end(msg, attr);	} 	if (devlink_rel_devlink_handle_put(msg, devlink,					   linecard->rel_index,					   DEVLINK_ATTR_NESTED_DEVLINK,					   NULL))		goto nla_put_failure; 	genlmsg_end(msg, hdr);	return 0; nla_put_failure:	genlmsg_cancel(msg, hdr);	return -EMSGSIZE;} static void devlink_linecard_notify(struct devlink_linecard *linecard,				    enum devlink_command cmd){	struct devlink *devlink = linecard->devlink;	struct sk_buff *msg;	int err; 	WARN_ON(cmd != DEVLINK_CMD_LINECARD_NEW &&		cmd != DEVLINK_CMD_LINECARD_DEL); 	if (!__devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))		return; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return; 	err = devlink_nl_linecard_fill(msg, devlink, linecard, cmd, 0, 0, 0,				       NULL);	if (err) {		nlmsg_free(msg);		return;	} 	devlink_nl_notify_send(devlink, msg);} void devlink_linecards_notify_register(struct devlink *devlink){	struct devlink_linecard *linecard; 	list_for_each_entry(linecard, &devlink->linecard_list, list)		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);} void devlink_linecards_notify_unregister(struct devlink *devlink){	struct devlink_linecard *linecard; 	list_for_each_entry_reverse(linecard, &devlink->linecard_list, list)		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);} int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info){	struct devlink *devlink = devlink_nl_ctx(info)->devlink;	struct devlink_linecard *linecard;	struct sk_buff *msg;	int err; 	linecard = devlink_linecard_get_from_info(devlink, info);	if (IS_ERR(linecard))		return PTR_ERR(linecard); 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return -ENOMEM; 	mutex_lock(&linecard->state_lock);	err = devlink_nl_linecard_fill(msg, devlink, linecard,				       DEVLINK_CMD_LINECARD_NEW,				       info->snd_portid, info->snd_seq, 0,				       info->extack);	mutex_unlock(&linecard->state_lock);	if (err) {		nlmsg_free(msg);		return err;	} 	return genlmsg_reply(msg, info);} static int devlink_nl_linecard_get_dump_one(struct sk_buff *msg,					    struct devlink *devlink,					    struct netlink_callback *cb,					    int flags){	struct devlink_nl_dump_state *state = devlink_dump_state(cb);	struct devlink_linecard *linecard;	int idx = 0;	int err = 0; 	list_for_each_entry(linecard, &devlink->linecard_list, list) {		if (idx < state->idx) {			idx++;			continue;		}		mutex_lock(&linecard->state_lock);		err = devlink_nl_linecard_fill(msg, devlink, linecard,					       DEVLINK_CMD_LINECARD_NEW,					       NETLINK_CB(cb->skb).portid,					       cb->nlh->nlmsg_seq, flags,					       cb->extack);		mutex_unlock(&linecard->state_lock);		if (err) {			state->idx = idx;			break;		}		idx++;	} 	return err;} int devlink_nl_linecard_get_dumpit(struct sk_buff *skb,				   struct netlink_callback *cb){	return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one);} static struct devlink_linecard_type *devlink_linecard_type_lookup(struct devlink_linecard *linecard,			     const char *type){	struct devlink_linecard_type *linecard_type;	int i; 	for (i = 0; i < linecard->types_count; i++) {		linecard_type = &linecard->types[i];		if (!strcmp(type, linecard_type->type))			return linecard_type;	}	return NULL;} static int devlink_linecard_type_set(struct devlink_linecard *linecard,				     const char *type,				     struct netlink_ext_ack *extack){	const struct devlink_linecard_ops *ops = linecard->ops;	struct devlink_linecard_type *linecard_type;	int err; 	mutex_lock(&linecard->state_lock);	if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {		NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");		err = -EBUSY;		goto out;	}	if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {		NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");		err = -EBUSY;		goto out;	} 	linecard_type = devlink_linecard_type_lookup(linecard, type);	if (!linecard_type) {		NL_SET_ERR_MSG(extack, "Unsupported line card type provided");		err = -EINVAL;		goto out;	} 	if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED &&	    linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {		NL_SET_ERR_MSG(extack, "Line card already provisioned");		err = -EBUSY;		/* Check if the line card is provisioned in the same		 * way the user asks. In case it is, make the operation		 * to return success.		 */		if (ops->same_provision &&		    ops->same_provision(linecard, linecard->priv,					linecard_type->type,					linecard_type->priv))			err = 0;		goto out;	} 	linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING;	linecard->type = linecard_type->type;	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);	mutex_unlock(&linecard->state_lock);	err = ops->provision(linecard, linecard->priv, linecard_type->type,			     linecard_type->priv, extack);	if (err) {		/* Provisioning failed. Assume the linecard is unprovisioned		 * for future operations.		 */		mutex_lock(&linecard->state_lock);		linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;		linecard->type = NULL;		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);		mutex_unlock(&linecard->state_lock);	}	return err; out:	mutex_unlock(&linecard->state_lock);	return err;} static int devlink_linecard_type_unset(struct devlink_linecard *linecard,				       struct netlink_ext_ack *extack){	int err; 	mutex_lock(&linecard->state_lock);	if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {		NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");		err = -EBUSY;		goto out;	}	if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {		NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");		err = -EBUSY;		goto out;	}	if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {		linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;		linecard->type = NULL;		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);		err = 0;		goto out;	} 	if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) {		NL_SET_ERR_MSG(extack, "Line card is not provisioned");		err = 0;		goto out;	}	linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING;	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);	mutex_unlock(&linecard->state_lock);	err = linecard->ops->unprovision(linecard, linecard->priv,					 extack);	if (err) {		/* Unprovisioning failed. Assume the linecard is unprovisioned		 * for future operations.		 */		mutex_lock(&linecard->state_lock);		linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;		linecard->type = NULL;		devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);		mutex_unlock(&linecard->state_lock);	}	return err; out:	mutex_unlock(&linecard->state_lock);	return err;} int devlink_nl_linecard_set_doit(struct sk_buff *skb, struct genl_info *info){	struct netlink_ext_ack *extack = info->extack;	struct devlink *devlink = devlink_nl_ctx(info)->devlink;	struct devlink_linecard *linecard;	int err; 	linecard = devlink_linecard_get_from_info(devlink, info);	if (IS_ERR(linecard))		return PTR_ERR(linecard); 	if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {		const char *type; 		type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);		if (strcmp(type, "")) {			err = devlink_linecard_type_set(linecard, type, extack);			if (err)				return err;		} else {			err = devlink_linecard_type_unset(linecard, extack);			if (err)				return err;		}	} 	return 0;} static int devlink_linecard_types_init(struct devlink_linecard *linecard){	struct devlink_linecard_type *linecard_type;	unsigned int count;	int i; 	count = linecard->ops->types_count(linecard, linecard->priv);	linecard->types = kmalloc_objs(*linecard_type, count);	if (!linecard->types)		return -ENOMEM;	linecard->types_count = count; 	for (i = 0; i < count; i++) {		linecard_type = &linecard->types[i];		linecard->ops->types_get(linecard, linecard->priv, i,					 &linecard_type->type,					 &linecard_type->priv);	}	return 0;} static void devlink_linecard_types_fini(struct devlink_linecard *linecard){	kfree(linecard->types);} /** *	devl_linecard_create - Create devlink linecard * *	@devlink: devlink *	@linecard_index: driver-specific numerical identifier of the linecard *	@ops: linecards ops *	@priv: user priv pointer * *	Create devlink linecard instance with provided linecard index. *	Caller can use any indexing, even hw-related one. * *	Return: Line card structure or an ERR_PTR() encoded error code. */struct devlink_linecard *devl_linecard_create(struct devlink *devlink, unsigned int linecard_index,		     const struct devlink_linecard_ops *ops, void *priv){	struct devlink_linecard *linecard;	int err; 	if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||		    !ops->types_count || !ops->types_get))		return ERR_PTR(-EINVAL); 	if (devlink_linecard_index_exists(devlink, linecard_index))		return ERR_PTR(-EEXIST); 	linecard = kzalloc_obj(*linecard);	if (!linecard)		return ERR_PTR(-ENOMEM); 	linecard->devlink = devlink;	linecard->index = linecard_index;	linecard->ops = ops;	linecard->priv = priv;	linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;	mutex_init(&linecard->state_lock); 	err = devlink_linecard_types_init(linecard);	if (err) {		mutex_destroy(&linecard->state_lock);		kfree(linecard);		return ERR_PTR(err);	} 	list_add_tail(&linecard->list, &devlink->linecard_list);	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);	return linecard;}EXPORT_SYMBOL_GPL(devl_linecard_create); /** *	devl_linecard_destroy - Destroy devlink linecard * *	@linecard: devlink linecard */void devl_linecard_destroy(struct devlink_linecard *linecard){	devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);	list_del(&linecard->list);	devlink_linecard_types_fini(linecard);	mutex_destroy(&linecard->state_lock);	kfree(linecard);}EXPORT_SYMBOL_GPL(devl_linecard_destroy); /** *	devlink_linecard_provision_set - Set provisioning on linecard * *	@linecard: devlink linecard *	@type: linecard type * *	This is either called directly from the provision() op call or *	as a result of the provision() op call asynchronously. */

Lines 1–500 of 627.