mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	devlink reset command can fail if a FIB resource limit is set to a value
lower than the current occupancy. Return a proper message indicating the
reason for the failure.
$ devlink resource sh netdevsim/netdevsim0
netdevsim/netdevsim0:
  name IPv4 size unlimited unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables none
    resources:
      name fib size unlimited occ 43 unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables none
      name fib-rules size unlimited occ 4 unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables none
  name IPv6 size unlimited unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables none
    resources:
      name fib size unlimited occ 54 unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables none
      name fib-rules size unlimited occ 3 unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables none
$ devlink resource set netdevsim/netdevsim0 path /IPv4/fib size 40
$ devlink dev  reload netdevsim/netdevsim0
Error: netdevsim: New size is less than current occupancy.
devlink answers: Invalid argument
Signed-off-by: David Ahern <dsahern@gmail.com>
Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
		
	
			
		
			
				
	
	
		
			266 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2018 Cumulus Networks. All rights reserved.
 | 
						|
 * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
 | 
						|
 *
 | 
						|
 * This software is licensed under the GNU General License Version 2,
 | 
						|
 * June 1991 as shown in the file COPYING in the top-level directory of this
 | 
						|
 * source tree.
 | 
						|
 *
 | 
						|
 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
 | 
						|
 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 | 
						|
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | 
						|
 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
 | 
						|
 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
 | 
						|
 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
 | 
						|
 */
 | 
						|
 | 
						|
#include <net/fib_notifier.h>
 | 
						|
#include <net/ip_fib.h>
 | 
						|
#include <net/ip6_fib.h>
 | 
						|
#include <net/fib_rules.h>
 | 
						|
#include <net/netns/generic.h>
 | 
						|
 | 
						|
#include "netdevsim.h"
 | 
						|
 | 
						|
struct nsim_fib_entry {
 | 
						|
	u64 max;
 | 
						|
	u64 num;
 | 
						|
};
 | 
						|
 | 
						|
struct nsim_per_fib_data {
 | 
						|
	struct nsim_fib_entry fib;
 | 
						|
	struct nsim_fib_entry rules;
 | 
						|
};
 | 
						|
 | 
						|
struct nsim_fib_data {
 | 
						|
	struct nsim_per_fib_data ipv4;
 | 
						|
	struct nsim_per_fib_data ipv6;
 | 
						|
};
 | 
						|
 | 
						|
static unsigned int nsim_fib_net_id;
 | 
						|
 | 
						|
u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
 | 
						|
{
 | 
						|
	struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
 | 
						|
	struct nsim_fib_entry *entry;
 | 
						|
 | 
						|
	switch (res_id) {
 | 
						|
	case NSIM_RESOURCE_IPV4_FIB:
 | 
						|
		entry = &fib_data->ipv4.fib;
 | 
						|
		break;
 | 
						|
	case NSIM_RESOURCE_IPV4_FIB_RULES:
 | 
						|
		entry = &fib_data->ipv4.rules;
 | 
						|
		break;
 | 
						|
	case NSIM_RESOURCE_IPV6_FIB:
 | 
						|
		entry = &fib_data->ipv6.fib;
 | 
						|
		break;
 | 
						|
	case NSIM_RESOURCE_IPV6_FIB_RULES:
 | 
						|
		entry = &fib_data->ipv6.rules;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return max ? entry->max : entry->num;
 | 
						|
}
 | 
						|
 | 
						|
int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,
 | 
						|
		     struct netlink_ext_ack *extack)
 | 
						|
{
 | 
						|
	struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
 | 
						|
	struct nsim_fib_entry *entry;
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	switch (res_id) {
 | 
						|
	case NSIM_RESOURCE_IPV4_FIB:
 | 
						|
		entry = &fib_data->ipv4.fib;
 | 
						|
		break;
 | 
						|
	case NSIM_RESOURCE_IPV4_FIB_RULES:
 | 
						|
		entry = &fib_data->ipv4.rules;
 | 
						|
		break;
 | 
						|
	case NSIM_RESOURCE_IPV6_FIB:
 | 
						|
		entry = &fib_data->ipv6.fib;
 | 
						|
		break;
 | 
						|
	case NSIM_RESOURCE_IPV6_FIB_RULES:
 | 
						|
		entry = &fib_data->ipv6.rules;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* not allowing a new max to be less than curren occupancy
 | 
						|
	 * --> no means of evicting entries
 | 
						|
	 */
 | 
						|
	if (val < entry->num) {
 | 
						|
		NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
 | 
						|
		err = -EINVAL;
 | 
						|
	} else {
 | 
						|
		entry->max = val;
 | 
						|
	}
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
 | 
						|
				 struct netlink_ext_ack *extack)
 | 
						|
{
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	if (add) {
 | 
						|
		if (entry->num < entry->max) {
 | 
						|
			entry->num++;
 | 
						|
		} else {
 | 
						|
			err = -ENOSPC;
 | 
						|
			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		entry->num--;
 | 
						|
	}
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
 | 
						|
{
 | 
						|
	struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
 | 
						|
	struct netlink_ext_ack *extack = info->extack;
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	switch (info->family) {
 | 
						|
	case AF_INET:
 | 
						|
		err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
 | 
						|
		break;
 | 
						|
	case AF_INET6:
 | 
						|
		err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
 | 
						|
			    struct netlink_ext_ack *extack)
 | 
						|
{
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	if (add) {
 | 
						|
		if (entry->num < entry->max) {
 | 
						|
			entry->num++;
 | 
						|
		} else {
 | 
						|
			err = -ENOSPC;
 | 
						|
			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		entry->num--;
 | 
						|
	}
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int nsim_fib_event(struct fib_notifier_info *info, bool add)
 | 
						|
{
 | 
						|
	struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
 | 
						|
	struct netlink_ext_ack *extack = info->extack;
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	switch (info->family) {
 | 
						|
	case AF_INET:
 | 
						|
		err = nsim_fib_account(&data->ipv4.fib, add, extack);
 | 
						|
		break;
 | 
						|
	case AF_INET6:
 | 
						|
		err = nsim_fib_account(&data->ipv6.fib, add, extack);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
 | 
						|
			     void *ptr)
 | 
						|
{
 | 
						|
	struct fib_notifier_info *info = ptr;
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	switch (event) {
 | 
						|
	case FIB_EVENT_RULE_ADD: /* fall through */
 | 
						|
	case FIB_EVENT_RULE_DEL:
 | 
						|
		err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIB_EVENT_ENTRY_ADD:  /* fall through */
 | 
						|
	case FIB_EVENT_ENTRY_DEL:
 | 
						|
		err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return notifier_from_errno(err);
 | 
						|
}
 | 
						|
 | 
						|
/* inconsistent dump, trying again */
 | 
						|
static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
 | 
						|
{
 | 
						|
	struct nsim_fib_data *data;
 | 
						|
	struct net *net;
 | 
						|
 | 
						|
	rcu_read_lock();
 | 
						|
	for_each_net_rcu(net) {
 | 
						|
		data = net_generic(net, nsim_fib_net_id);
 | 
						|
 | 
						|
		data->ipv4.fib.num = 0ULL;
 | 
						|
		data->ipv4.rules.num = 0ULL;
 | 
						|
 | 
						|
		data->ipv6.fib.num = 0ULL;
 | 
						|
		data->ipv6.rules.num = 0ULL;
 | 
						|
	}
 | 
						|
	rcu_read_unlock();
 | 
						|
}
 | 
						|
 | 
						|
static struct notifier_block nsim_fib_nb = {
 | 
						|
	.notifier_call = nsim_fib_event_nb,
 | 
						|
};
 | 
						|
 | 
						|
/* Initialize per network namespace state */
 | 
						|
static int __net_init nsim_fib_netns_init(struct net *net)
 | 
						|
{
 | 
						|
	struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
 | 
						|
 | 
						|
	data->ipv4.fib.max = (u64)-1;
 | 
						|
	data->ipv4.rules.max = (u64)-1;
 | 
						|
 | 
						|
	data->ipv6.fib.max = (u64)-1;
 | 
						|
	data->ipv6.rules.max = (u64)-1;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static struct pernet_operations nsim_fib_net_ops = {
 | 
						|
	.init = nsim_fib_netns_init,
 | 
						|
	.id   = &nsim_fib_net_id,
 | 
						|
	.size = sizeof(struct nsim_fib_data),
 | 
						|
};
 | 
						|
 | 
						|
void nsim_fib_exit(void)
 | 
						|
{
 | 
						|
	unregister_pernet_subsys(&nsim_fib_net_ops);
 | 
						|
	unregister_fib_notifier(&nsim_fib_nb);
 | 
						|
}
 | 
						|
 | 
						|
int nsim_fib_init(void)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = register_pernet_subsys(&nsim_fib_net_ops);
 | 
						|
	if (err < 0) {
 | 
						|
		pr_err("Failed to register pernet subsystem\n");
 | 
						|
		goto err_out;
 | 
						|
	}
 | 
						|
 | 
						|
	err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
 | 
						|
	if (err < 0) {
 | 
						|
		pr_err("Failed to register fib notifier\n");
 | 
						|
		goto err_out;
 | 
						|
	}
 | 
						|
 | 
						|
err_out:
 | 
						|
	return err;
 | 
						|
}
 |