1860 lines · 47.0 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 <trace/events/devlink.h> #include "devl_internal.h" struct devlink_stats {	u64_stats_t rx_bytes;	u64_stats_t rx_packets;	struct u64_stats_sync syncp;}; /** * struct devlink_trap_policer_item - Packet trap policer attributes. * @policer: Immutable packet trap policer attributes. * @rate: Rate in packets / sec. * @burst: Burst size in packets. * @list: trap_policer_list member. * * Describes packet trap policer attributes. Created by devlink during trap * policer registration. */struct devlink_trap_policer_item {	const struct devlink_trap_policer *policer;	u64 rate;	u64 burst;	struct list_head list;}; /** * struct devlink_trap_group_item - Packet trap group attributes. * @group: Immutable packet trap group attributes. * @policer_item: Associated policer item. Can be NULL. * @list: trap_group_list member. * @stats: Trap group statistics. * * Describes packet trap group attributes. Created by devlink during trap * group registration. */struct devlink_trap_group_item {	const struct devlink_trap_group *group;	struct devlink_trap_policer_item *policer_item;	struct list_head list;	struct devlink_stats __percpu *stats;}; /** * struct devlink_trap_item - Packet trap attributes. * @trap: Immutable packet trap attributes. * @group_item: Associated group item. * @list: trap_list member. * @action: Trap action. * @stats: Trap statistics. * @priv: Driver private information. * * Describes both mutable and immutable packet trap attributes. Created by * devlink during trap registration and used for all trap related operations. */struct devlink_trap_item {	const struct devlink_trap *trap;	struct devlink_trap_group_item *group_item;	struct list_head list;	enum devlink_trap_action action;	struct devlink_stats __percpu *stats;	void *priv;}; static struct devlink_trap_policer_item *devlink_trap_policer_item_lookup(struct devlink *devlink, u32 id){	struct devlink_trap_policer_item *policer_item; 	list_for_each_entry(policer_item, &devlink->trap_policer_list, list) {		if (policer_item->policer->id == id)			return policer_item;	} 	return NULL;} static struct devlink_trap_item *devlink_trap_item_lookup(struct devlink *devlink, const char *name){	struct devlink_trap_item *trap_item; 	list_for_each_entry(trap_item, &devlink->trap_list, list) {		if (!strcmp(trap_item->trap->name, name))			return trap_item;	} 	return NULL;} static struct devlink_trap_item *devlink_trap_item_get_from_info(struct devlink *devlink,				struct genl_info *info){	struct nlattr *attr; 	if (!info->attrs[DEVLINK_ATTR_TRAP_NAME])		return NULL;	attr = info->attrs[DEVLINK_ATTR_TRAP_NAME]; 	return devlink_trap_item_lookup(devlink, nla_data(attr));} static intdevlink_trap_action_get_from_info(struct genl_info *info,				  enum devlink_trap_action *p_trap_action){	u8 val; 	val = nla_get_u8(info->attrs[DEVLINK_ATTR_TRAP_ACTION]);	switch (val) {	case DEVLINK_TRAP_ACTION_DROP:	case DEVLINK_TRAP_ACTION_TRAP:	case DEVLINK_TRAP_ACTION_MIRROR:		*p_trap_action = val;		break;	default:		return -EINVAL;	} 	return 0;} static int devlink_trap_metadata_put(struct sk_buff *msg,				     const struct devlink_trap *trap){	struct nlattr *attr; 	attr = nla_nest_start(msg, DEVLINK_ATTR_TRAP_METADATA);	if (!attr)		return -EMSGSIZE; 	if ((trap->metadata_cap & DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT) &&	    nla_put_flag(msg, DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT))		goto nla_put_failure;	if ((trap->metadata_cap & DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE) &&	    nla_put_flag(msg, DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE))		goto nla_put_failure; 	nla_nest_end(msg, attr); 	return 0; nla_put_failure:	nla_nest_cancel(msg, attr);	return -EMSGSIZE;} static void devlink_trap_stats_read(struct devlink_stats __percpu *trap_stats,				    struct devlink_stats *stats){	int i; 	memset(stats, 0, sizeof(*stats));	for_each_possible_cpu(i) {		struct devlink_stats *cpu_stats;		u64 rx_packets, rx_bytes;		unsigned int start; 		cpu_stats = per_cpu_ptr(trap_stats, i);		do {			start = u64_stats_fetch_begin(&cpu_stats->syncp);			rx_packets = u64_stats_read(&cpu_stats->rx_packets);			rx_bytes = u64_stats_read(&cpu_stats->rx_bytes);		} while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); 		u64_stats_add(&stats->rx_packets, rx_packets);		u64_stats_add(&stats->rx_bytes, rx_bytes);	}} static intdevlink_trap_group_stats_put(struct sk_buff *msg,			     struct devlink_stats __percpu *trap_stats){	struct devlink_stats stats;	struct nlattr *attr; 	devlink_trap_stats_read(trap_stats, &stats); 	attr = nla_nest_start(msg, DEVLINK_ATTR_STATS);	if (!attr)		return -EMSGSIZE; 	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_PACKETS,			      u64_stats_read(&stats.rx_packets),			      DEVLINK_ATTR_PAD))		goto nla_put_failure; 	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_BYTES,			      u64_stats_read(&stats.rx_bytes),			      DEVLINK_ATTR_PAD))		goto nla_put_failure; 	nla_nest_end(msg, attr); 	return 0; nla_put_failure:	nla_nest_cancel(msg, attr);	return -EMSGSIZE;} static int devlink_trap_stats_put(struct sk_buff *msg, struct devlink *devlink,				  const struct devlink_trap_item *trap_item){	struct devlink_stats stats;	struct nlattr *attr;	u64 drops = 0;	int err; 	if (devlink->ops->trap_drop_counter_get) {		err = devlink->ops->trap_drop_counter_get(devlink,							  trap_item->trap,							  &drops);		if (err)			return err;	} 	devlink_trap_stats_read(trap_item->stats, &stats); 	attr = nla_nest_start(msg, DEVLINK_ATTR_STATS);	if (!attr)		return -EMSGSIZE; 	if (devlink->ops->trap_drop_counter_get &&	    nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_DROPPED, drops,			      DEVLINK_ATTR_PAD))		goto nla_put_failure; 	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_PACKETS,			      u64_stats_read(&stats.rx_packets),			      DEVLINK_ATTR_PAD))		goto nla_put_failure; 	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_BYTES,			      u64_stats_read(&stats.rx_bytes),			      DEVLINK_ATTR_PAD))		goto nla_put_failure; 	nla_nest_end(msg, attr); 	return 0; nla_put_failure:	nla_nest_cancel(msg, attr);	return -EMSGSIZE;} static int devlink_nl_trap_fill(struct sk_buff *msg, struct devlink *devlink,				const struct devlink_trap_item *trap_item,				enum devlink_command cmd, u32 portid, u32 seq,				int flags){	struct devlink_trap_group_item *group_item = trap_item->group_item;	void *hdr;	int err; 	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_string(msg, DEVLINK_ATTR_TRAP_GROUP_NAME,			   group_item->group->name))		goto nla_put_failure; 	if (nla_put_string(msg, DEVLINK_ATTR_TRAP_NAME, trap_item->trap->name))		goto nla_put_failure; 	if (nla_put_u8(msg, DEVLINK_ATTR_TRAP_TYPE, trap_item->trap->type))		goto nla_put_failure; 	if (trap_item->trap->generic &&	    nla_put_flag(msg, DEVLINK_ATTR_TRAP_GENERIC))		goto nla_put_failure; 	if (nla_put_u8(msg, DEVLINK_ATTR_TRAP_ACTION, trap_item->action))		goto nla_put_failure; 	err = devlink_trap_metadata_put(msg, trap_item->trap);	if (err)		goto nla_put_failure; 	err = devlink_trap_stats_put(msg, devlink, trap_item);	if (err)		goto nla_put_failure; 	genlmsg_end(msg, hdr); 	return 0; nla_put_failure:	genlmsg_cancel(msg, hdr);	return -EMSGSIZE;} int devlink_nl_trap_get_doit(struct sk_buff *skb, struct genl_info *info){	struct netlink_ext_ack *extack = info->extack;	struct devlink *devlink = info->user_ptr[0];	struct devlink_trap_item *trap_item;	struct sk_buff *msg;	int err; 	if (list_empty(&devlink->trap_list))		return -EOPNOTSUPP; 	trap_item = devlink_trap_item_get_from_info(devlink, info);	if (!trap_item) {		NL_SET_ERR_MSG(extack, "Device did not register this trap");		return -ENOENT;	} 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return -ENOMEM; 	err = devlink_nl_trap_fill(msg, devlink, trap_item,				   DEVLINK_CMD_TRAP_NEW, info->snd_portid,				   info->snd_seq, 0);	if (err)		goto err_trap_fill; 	return genlmsg_reply(msg, info); err_trap_fill:	nlmsg_free(msg);	return err;} static int devlink_nl_trap_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_trap_item *trap_item;	int idx = 0;	int err = 0; 	list_for_each_entry(trap_item, &devlink->trap_list, list) {		if (idx < state->idx) {			idx++;			continue;		}		err = devlink_nl_trap_fill(msg, devlink, trap_item,					   DEVLINK_CMD_TRAP_NEW,					   NETLINK_CB(cb->skb).portid,					   cb->nlh->nlmsg_seq, flags);		if (err) {			state->idx = idx;			break;		}		idx++;	} 	return err;} int devlink_nl_trap_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb){	return devlink_nl_dumpit(skb, cb, devlink_nl_trap_get_dump_one);} static int __devlink_trap_action_set(struct devlink *devlink,				     struct devlink_trap_item *trap_item,				     enum devlink_trap_action trap_action,				     struct netlink_ext_ack *extack){	int err; 	if (trap_item->action != trap_action &&	    trap_item->trap->type != DEVLINK_TRAP_TYPE_DROP) {		NL_SET_ERR_MSG(extack, "Cannot change action of non-drop traps. Skipping");		return 0;	} 	err = devlink->ops->trap_action_set(devlink, trap_item->trap,					    trap_action, extack);	if (err)		return err; 	trap_item->action = trap_action; 	return 0;} static int devlink_trap_action_set(struct devlink *devlink,				   struct devlink_trap_item *trap_item,				   struct genl_info *info){	enum devlink_trap_action trap_action;	int err; 	if (!info->attrs[DEVLINK_ATTR_TRAP_ACTION])		return 0; 	err = devlink_trap_action_get_from_info(info, &trap_action);	if (err) {		NL_SET_ERR_MSG(info->extack, "Invalid trap action");		return -EINVAL;	} 	return __devlink_trap_action_set(devlink, trap_item, trap_action,					 info->extack);} int devlink_nl_trap_set_doit(struct sk_buff *skb, struct genl_info *info){	struct netlink_ext_ack *extack = info->extack;	struct devlink *devlink = info->user_ptr[0];	struct devlink_trap_item *trap_item; 	if (list_empty(&devlink->trap_list))		return -EOPNOTSUPP; 	trap_item = devlink_trap_item_get_from_info(devlink, info);	if (!trap_item) {		NL_SET_ERR_MSG(extack, "Device did not register this trap");		return -ENOENT;	} 	return devlink_trap_action_set(devlink, trap_item, info);} static struct devlink_trap_group_item *devlink_trap_group_item_lookup(struct devlink *devlink, const char *name){	struct devlink_trap_group_item *group_item; 	list_for_each_entry(group_item, &devlink->trap_group_list, list) {		if (!strcmp(group_item->group->name, name))			return group_item;	} 	return NULL;} static struct devlink_trap_group_item *devlink_trap_group_item_lookup_by_id(struct devlink *devlink, u16 id){	struct devlink_trap_group_item *group_item; 	list_for_each_entry(group_item, &devlink->trap_group_list, list) {		if (group_item->group->id == id)			return group_item;	} 	return NULL;} static struct devlink_trap_group_item *devlink_trap_group_item_get_from_info(struct devlink *devlink,				      struct genl_info *info){	char *name; 	if (!info->attrs[DEVLINK_ATTR_TRAP_GROUP_NAME])		return NULL;	name = nla_data(info->attrs[DEVLINK_ATTR_TRAP_GROUP_NAME]); 	return devlink_trap_group_item_lookup(devlink, name);} static intdevlink_nl_trap_group_fill(struct sk_buff *msg, struct devlink *devlink,			   const struct devlink_trap_group_item *group_item,			   enum devlink_command cmd, u32 portid, u32 seq,			   int flags){	void *hdr;	int err; 	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_string(msg, DEVLINK_ATTR_TRAP_GROUP_NAME,			   group_item->group->name))		goto nla_put_failure; 	if (group_item->group->generic &&	    nla_put_flag(msg, DEVLINK_ATTR_TRAP_GENERIC))		goto nla_put_failure; 	if (group_item->policer_item &&	    nla_put_u32(msg, DEVLINK_ATTR_TRAP_POLICER_ID,			group_item->policer_item->policer->id))

Lines 1–500 of 1860.