Commit e9e90a70 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'resil-nhgroups-netdevsim-selftests'

Petr Machata says:

====================
net: Resilient NH groups: netdevsim, selftests

Support for resilient next-hop groups was added in a previous patch set.
Resilient next hop groups add a layer of indirection between the SKB hash
and the next hop. Thus the hash is used to reference a hash table bucket,
which is then used to reference a particular next hop. This allows the
system more flexibility when assigning SKB hash space to next hops.
Previously, each next hop had to be assigned a continuous range of SKB hash
space. With a hash table as an intermediate layer, it is possible to
reassign next hops with a hash table bucket granularity. In turn, this
mends issues with traffic flow redirection resulting from next hop removal
or adjustments in next-hop weights.

This patch set introduces mock offloading of resilient next hop groups by
the netdevsim driver, and a suite of selftests.

- Patch #1 adds a netdevsim-specific lock to protect next-hop hashtable.
  Previously, netdevsim relied on RTNL to maintain mutual exclusion.
  Patch #2 extracts a helper to make the following patches clearer.

- Patch #3 implements the support for offloading of resilient next-hop
  groups.

- Patch #4 introduces a new debugfs interface to set activity on a selected
  next-hop bucket. This simulates how HW can periodically report bucket
  activity, and buckets thus marked are expected to be exempt from
  migration to new next hops when the group changes.

- Patches #5 and #6 clean up the fib_nexthop selftests.

- Patches #7, #8 and #9 add tests for resilient next hop groups. Patch #7
  adds resilient-hashing counterparts to fib_nexthops.sh. Patch #8 adds a
  new traffic test for resilient next-hop groups. Patch #9 adds a new
  traffic test for tunneling.

- Patch #10 actually leverages the netdevsim offload to implement a suite
  of algorithmic tests that verify how and when buckets are migrated under
  various simulated workload scenarios.

The overall plan is to contribute approximately the following patchsets:

1) Nexthop policy refactoring (already pushed)
2) Preparations for resilient next hop groups (already pushed)
3) Implementation of resilient next hop group (already pushed)
4) Netdevsim offload plus a suite of selftests (this patchset)
5) Preparations for mlxsw offload of resilient next-hop groups
6) mlxsw offload including selftests

Interested parties can look at the complete code at [2].

[1] https://tools.ietf.org/html/rfc2992
[2] https://github.com/idosch/linux/commits/submit/res_integ_v1


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b202923d b8a07c4c
Loading
Loading
Loading
Loading
+133 −6
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
 */

#include <linux/bitmap.h>
#include <linux/in6.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -47,15 +48,18 @@ struct nsim_fib_data {
	struct nsim_fib_entry nexthops;
	struct rhashtable fib_rt_ht;
	struct list_head fib_rt_list;
	struct mutex fib_lock; /* Protects hashtable and list */
	struct mutex fib_lock; /* Protects FIB HT and list */
	struct notifier_block nexthop_nb;
	struct rhashtable nexthop_ht;
	struct devlink *devlink;
	struct work_struct fib_event_work;
	struct list_head fib_event_queue;
	spinlock_t fib_event_queue_lock; /* Protects fib event queue list */
	struct mutex nh_lock; /* Protects NH HT */
	struct dentry *ddir;
	bool fail_route_offload;
	bool fail_res_nexthop_group_replace;
	bool fail_nexthop_bucket_replace;
};

struct nsim_fib_rt_key {
@@ -116,6 +120,7 @@ struct nsim_nexthop {
	struct rhash_head ht_node;
	u64 occ;
	u32 id;
	bool is_resilient;
};

static const struct rhashtable_params nsim_nexthop_ht_params = {
@@ -1114,6 +1119,10 @@ static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data,
		for (i = 0; i < info->nh_grp->num_nh; i++)
			occ += info->nh_grp->nh_entries[i].weight;
		break;
	case NH_NOTIFIER_INFO_TYPE_RES_TABLE:
		occ = info->nh_res_table->num_nh_buckets;
		nexthop->is_resilient = true;
		break;
	default:
		NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type");
		kfree(nexthop);
@@ -1156,6 +1165,21 @@ static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ,

}

static void nsim_nexthop_hw_flags_set(struct net *net,
				      const struct nsim_nexthop *nexthop,
				      bool trap)
{
	int i;

	nexthop_set_hw_flags(net, nexthop->id, false, trap);

	if (!nexthop->is_resilient)
		return;

	for (i = 0; i < nexthop->occ; i++)
		nexthop_bucket_set_hw_flags(net, nexthop->id, i, false, trap);
}

static int nsim_nexthop_add(struct nsim_fib_data *data,
			    struct nsim_nexthop *nexthop,
			    struct netlink_ext_ack *extack)
@@ -1174,7 +1198,7 @@ static int nsim_nexthop_add(struct nsim_fib_data *data,
		goto err_nexthop_dismiss;
	}

	nexthop_set_hw_flags(net, nexthop->id, false, true);
	nsim_nexthop_hw_flags_set(net, nexthop, true);

	return 0;

@@ -1203,7 +1227,7 @@ static int nsim_nexthop_replace(struct nsim_fib_data *data,
		goto err_nexthop_dismiss;
	}

	nexthop_set_hw_flags(net, nexthop->id, false, true);
	nsim_nexthop_hw_flags_set(net, nexthop, true);
	nsim_nexthop_account(data, nexthop_old->occ, false, extack);
	nsim_nexthop_destroy(nexthop_old);

@@ -1254,6 +1278,32 @@ static void nsim_nexthop_remove(struct nsim_fib_data *data,
	nsim_nexthop_destroy(nexthop);
}

static int nsim_nexthop_res_table_pre_replace(struct nsim_fib_data *data,
					      struct nh_notifier_info *info)
{
	if (data->fail_res_nexthop_group_replace) {
		NL_SET_ERR_MSG_MOD(info->extack, "Failed to replace a resilient nexthop group");
		return -EINVAL;
	}

	return 0;
}

static int nsim_nexthop_bucket_replace(struct nsim_fib_data *data,
				       struct nh_notifier_info *info)
{
	if (data->fail_nexthop_bucket_replace) {
		NL_SET_ERR_MSG_MOD(info->extack, "Failed to replace nexthop bucket");
		return -EINVAL;
	}

	nexthop_bucket_set_hw_flags(info->net, info->id,
				    info->nh_res_bucket->bucket_index,
				    false, true);

	return 0;
}

static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event,
				 void *ptr)
{
@@ -1262,8 +1312,7 @@ static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event,
	struct nh_notifier_info *info = ptr;
	int err = 0;

	ASSERT_RTNL();

	mutex_lock(&data->nh_lock);
	switch (event) {
	case NEXTHOP_EVENT_REPLACE:
		err = nsim_nexthop_insert(data, info);
@@ -1271,10 +1320,17 @@ static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event,
	case NEXTHOP_EVENT_DEL:
		nsim_nexthop_remove(data, info);
		break;
	case NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE:
		err = nsim_nexthop_res_table_pre_replace(data, info);
		break;
	case NEXTHOP_EVENT_BUCKET_REPLACE:
		err = nsim_nexthop_bucket_replace(data, info);
		break;
	default:
		break;
	}

	mutex_unlock(&data->nh_lock);
	return notifier_from_errno(err);
}

@@ -1285,11 +1341,68 @@ static void nsim_nexthop_free(void *ptr, void *arg)
	struct net *net;

	net = devlink_net(data->devlink);
	nexthop_set_hw_flags(net, nexthop->id, false, false);
	nsim_nexthop_hw_flags_set(net, nexthop, false);
	nsim_nexthop_account(data, nexthop->occ, false, NULL);
	nsim_nexthop_destroy(nexthop);
}

static ssize_t nsim_nexthop_bucket_activity_write(struct file *file,
						  const char __user *user_buf,
						  size_t size, loff_t *ppos)
{
	struct nsim_fib_data *data = file->private_data;
	struct net *net = devlink_net(data->devlink);
	struct nsim_nexthop *nexthop;
	unsigned long *activity;
	loff_t pos = *ppos;
	u16 bucket_index;
	char buf[128];
	int err = 0;
	u32 nhid;

	if (pos != 0)
		return -EINVAL;
	if (size > sizeof(buf))
		return -EINVAL;
	if (copy_from_user(buf, user_buf, size))
		return -EFAULT;
	if (sscanf(buf, "%u %hu", &nhid, &bucket_index) != 2)
		return -EINVAL;

	rtnl_lock();

	nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &nhid,
					 nsim_nexthop_ht_params);
	if (!nexthop || !nexthop->is_resilient ||
	    bucket_index >= nexthop->occ) {
		err = -EINVAL;
		goto out;
	}

	activity = bitmap_zalloc(nexthop->occ, GFP_KERNEL);
	if (!activity) {
		err = -ENOMEM;
		goto out;
	}

	bitmap_set(activity, bucket_index, 1);
	nexthop_res_grp_activity_update(net, nhid, nexthop->occ, activity);
	bitmap_free(activity);

out:
	rtnl_unlock();

	*ppos = size;
	return err ?: size;
}

static const struct file_operations nsim_nexthop_bucket_activity_fops = {
	.open = simple_open,
	.write = nsim_nexthop_bucket_activity_write,
	.llseek = no_llseek,
	.owner = THIS_MODULE,
};

static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
{
	struct nsim_fib_data *data = priv;
@@ -1379,6 +1492,17 @@ nsim_fib_debugfs_init(struct nsim_fib_data *data, struct nsim_dev *nsim_dev)
	data->fail_route_offload = false;
	debugfs_create_bool("fail_route_offload", 0600, data->ddir,
			    &data->fail_route_offload);

	data->fail_res_nexthop_group_replace = false;
	debugfs_create_bool("fail_res_nexthop_group_replace", 0600, data->ddir,
			    &data->fail_res_nexthop_group_replace);

	data->fail_nexthop_bucket_replace = false;
	debugfs_create_bool("fail_nexthop_bucket_replace", 0600, data->ddir,
			    &data->fail_nexthop_bucket_replace);

	debugfs_create_file("nexthop_bucket_activity", 0200, data->ddir,
			    data, &nsim_nexthop_bucket_activity_fops);
	return 0;
}

@@ -1404,6 +1528,7 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
	if (err)
		goto err_data_free;

	mutex_init(&data->nh_lock);
	err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
	if (err)
		goto err_debugfs_exit;
@@ -1469,6 +1594,7 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
				    data);
	mutex_destroy(&data->fib_lock);
err_debugfs_exit:
	mutex_destroy(&data->nh_lock);
	nsim_fib_debugfs_exit(data);
err_data_free:
	kfree(data);
@@ -1497,6 +1623,7 @@ void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
	WARN_ON_ONCE(!list_empty(&data->fib_event_queue));
	WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
	mutex_destroy(&data->fib_lock);
	mutex_destroy(&data->nh_lock);
	nsim_fib_debugfs_exit(data);
	kfree(data);
}
+620 −0
Original line number Diff line number Diff line
@@ -11,14 +11,33 @@ ALL_TESTS="
	nexthop_single_add_err_test
	nexthop_group_add_test
	nexthop_group_add_err_test
	nexthop_res_group_add_test
	nexthop_res_group_add_err_test
	nexthop_group_replace_test
	nexthop_group_replace_err_test
	nexthop_res_group_replace_test
	nexthop_res_group_replace_err_test
	nexthop_res_group_idle_timer_test
	nexthop_res_group_idle_timer_del_test
	nexthop_res_group_increase_idle_timer_test
	nexthop_res_group_decrease_idle_timer_test
	nexthop_res_group_unbalanced_timer_test
	nexthop_res_group_unbalanced_timer_del_test
	nexthop_res_group_no_unbalanced_timer_test
	nexthop_res_group_short_unbalanced_timer_test
	nexthop_res_group_increase_unbalanced_timer_test
	nexthop_res_group_decrease_unbalanced_timer_test
	nexthop_res_group_force_migrate_busy_test
	nexthop_single_replace_test
	nexthop_single_replace_err_test
	nexthop_single_in_group_replace_test
	nexthop_single_in_group_replace_err_test
	nexthop_single_in_res_group_replace_test
	nexthop_single_in_res_group_replace_err_test
	nexthop_single_in_group_delete_test
	nexthop_single_in_group_delete_err_test
	nexthop_single_in_res_group_delete_test
	nexthop_single_in_res_group_delete_err_test
	nexthop_replay_test
	nexthop_replay_err_test
"
@@ -27,6 +46,7 @@ DEV_ADDR=1337
DEV=netdevsim${DEV_ADDR}
DEVLINK_DEV=netdevsim/${DEV}
SYSFS_NET_DIR=/sys/bus/netdevsim/devices/$DEV/net/
DEBUGFS_NET_DIR=/sys/kernel/debug/netdevsim/$DEV/
NUM_NETIFS=0
source $lib_dir/lib.sh
source $lib_dir/devlink_lib.sh
@@ -44,6 +64,28 @@ nexthop_check()
	return 0
}

nexthop_bucket_nhid_count_check()
{
	local group_id=$1; shift
	local expected
	local count
	local nhid
	local ret

	while (($# > 0)); do
		nhid=$1; shift
		expected=$1; shift

		count=$($IP nexthop bucket show id $group_id nhid $nhid |
			grep "trap" | wc -l)
		if ((expected != count)); then
			return 1
		fi
	done

	return 0
}

nexthop_resource_check()
{
	local expected_occ=$1; shift
@@ -159,6 +201,71 @@ nexthop_group_add_err_test()
	nexthop_resource_set 9999
}

nexthop_res_group_add_test()
{
	RET=0

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1

	$IP nexthop add id 10 group 1/2 type resilient buckets 4
	nexthop_check "id 10" "id 10 group 1/2 type resilient buckets 4 idle_timer 120 unbalanced_timer 0 unbalanced_time 0 trap"
	check_err $? "Unexpected nexthop group entry"

	nexthop_bucket_nhid_count_check 10 1 2
	check_err $? "Wrong nexthop buckets count"
	nexthop_bucket_nhid_count_check 10 2 2
	check_err $? "Wrong nexthop buckets count"

	nexthop_resource_check 6
	check_err $? "Wrong nexthop occupancy"

	$IP nexthop del id 10
	nexthop_resource_check 2
	check_err $? "Wrong nexthop occupancy after delete"

	$IP nexthop add id 10 group 1,3/2,2 type resilient buckets 5
	nexthop_check "id 10" "id 10 group 1,3/2,2 type resilient buckets 5 idle_timer 120 unbalanced_timer 0 unbalanced_time 0 trap"
	check_err $? "Unexpected weighted nexthop group entry"

	nexthop_bucket_nhid_count_check 10 1 3
	check_err $? "Wrong nexthop buckets count"
	nexthop_bucket_nhid_count_check 10 2 2
	check_err $? "Wrong nexthop buckets count"

	nexthop_resource_check 7
	check_err $? "Wrong weighted nexthop occupancy"

	$IP nexthop del id 10
	nexthop_resource_check 2
	check_err $? "Wrong nexthop occupancy after delete"

	log_test "Resilient nexthop group add and delete"

	$IP nexthop flush &> /dev/null
}

nexthop_res_group_add_err_test()
{
	RET=0

	nexthop_resource_set 2

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1

	$IP nexthop add id 10 group 1/2 type resilient buckets 4 &> /dev/null
	check_fail $? "Nexthop group addition succeeded when should fail"

	nexthop_resource_check 2
	check_err $? "Wrong nexthop occupancy"

	log_test "Resilient nexthop group add failure"

	$IP nexthop flush &> /dev/null
	nexthop_resource_set 9999
}

nexthop_group_replace_test()
{
	RET=0
@@ -206,6 +313,411 @@ nexthop_group_replace_err_test()
	nexthop_resource_set 9999
}

nexthop_res_group_replace_test()
{
	RET=0

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1
	$IP nexthop add id 3 via 192.0.2.4 dev dummy1
	$IP nexthop add id 10 group 1/2 type resilient buckets 6

	$IP nexthop replace id 10 group 1/2/3 type resilient
	nexthop_check "id 10" "id 10 group 1/2/3 type resilient buckets 6 idle_timer 120 unbalanced_timer 0 unbalanced_time 0 trap"
	check_err $? "Unexpected nexthop group entry"

	nexthop_bucket_nhid_count_check 10 1 2
	check_err $? "Wrong nexthop buckets count"
	nexthop_bucket_nhid_count_check 10 2 2
	check_err $? "Wrong nexthop buckets count"
	nexthop_bucket_nhid_count_check 10 3 2
	check_err $? "Wrong nexthop buckets count"

	nexthop_resource_check 9
	check_err $? "Wrong nexthop occupancy"

	log_test "Resilient nexthop group replace"

	$IP nexthop flush &> /dev/null
}

nexthop_res_group_replace_err_test()
{
	RET=0

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1
	$IP nexthop add id 3 via 192.0.2.4 dev dummy1
	$IP nexthop add id 10 group 1/2 type resilient buckets 6

	ip netns exec testns1 \
		echo 1 > $DEBUGFS_NET_DIR/fib/fail_res_nexthop_group_replace
	$IP nexthop replace id 10 group 1/2/3 type resilient &> /dev/null
	check_fail $? "Nexthop group replacement succeeded when should fail"

	nexthop_check "id 10" "id 10 group 1/2 type resilient buckets 6 idle_timer 120 unbalanced_timer 0 unbalanced_time 0 trap"
	check_err $? "Unexpected nexthop group entry after failure"

	nexthop_bucket_nhid_count_check 10 1 3
	check_err $? "Wrong nexthop buckets count"
	nexthop_bucket_nhid_count_check 10 2 3
	check_err $? "Wrong nexthop buckets count"

	nexthop_resource_check 9
	check_err $? "Wrong nexthop occupancy after failure"

	log_test "Resilient nexthop group replace failure"

	$IP nexthop flush &> /dev/null
	ip netns exec testns1 \
		echo 0 > $DEBUGFS_NET_DIR/fib/fail_res_nexthop_group_replace
}

nexthop_res_mark_buckets_busy()
{
	local group_id=$1; shift
	local nhid=$1; shift
	local count=$1; shift
	local index

	for index in $($IP -j nexthop bucket show id $group_id nhid $nhid |
		       jq '.[].bucket.index' | head -n ${count:--0})
	do
		echo $group_id $index \
			> $DEBUGFS_NET_DIR/fib/nexthop_bucket_activity
	done
}

nexthop_res_num_nhid_buckets()
{
	local group_id=$1; shift
	local nhid=$1; shift

	$IP -j nexthop bucket show id $group_id nhid $nhid | jq length
}

nexthop_res_group_idle_timer_test()
{
	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1/2 type resilient buckets 8 idle_timer 4
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1/2,3 type resilient

	nexthop_bucket_nhid_count_check 10  1 4  2 4
	check_err $? "Group expected to be unbalanced"

	sleep 6

	nexthop_bucket_nhid_count_check 10  1 2  2 6
	check_err $? "Group expected to be balanced"

	log_test "Bucket migration after idle timer"

	$IP nexthop flush &> /dev/null
}

nexthop_res_group_idle_timer_del_test()
{
	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1
	$IP nexthop add id 3 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1,50/2,50/3,1 \
	    type resilient buckets 8 idle_timer 6
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1,50/2,150/3,1 type resilient

	nexthop_bucket_nhid_count_check 10  1 4  2 4  3 0
	check_err $? "Group expected to be unbalanced"

	sleep 4

	# Deletion prompts group replacement. Check that the bucket timers
	# are kept.
	$IP nexthop delete id 3

	nexthop_bucket_nhid_count_check 10  1 4  2 4
	check_err $? "Group expected to still be unbalanced"

	sleep 4

	nexthop_bucket_nhid_count_check 10  1 2  2 6
	check_err $? "Group expected to be balanced"

	log_test "Bucket migration after idle timer (with delete)"

	$IP nexthop flush &> /dev/null
}

__nexthop_res_group_increase_timer_test()
{
	local timer=$1; shift

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1/2 type resilient buckets 8 $timer 4
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1/2,3 type resilient

	nexthop_bucket_nhid_count_check 10 2 6
	check_fail $? "Group expected to be unbalanced"

	sleep 2
	$IP nexthop replace id 10 group 1/2,3 type resilient $timer 8
	sleep 4

	# 6 seconds, past the original timer.
	nexthop_bucket_nhid_count_check 10 2 6
	check_fail $? "Group still expected to be unbalanced"

	sleep 4

	# 10 seconds, past the new timer.
	nexthop_bucket_nhid_count_check 10 2 6
	check_err $? "Group expected to be balanced"

	log_test "Bucket migration after $timer increase"

	$IP nexthop flush &> /dev/null
}

__nexthop_res_group_decrease_timer_test()
{
	local timer=$1; shift

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1/2 type resilient buckets 8 $timer 8
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1/2,3 type resilient

	nexthop_bucket_nhid_count_check 10 2 6
	check_fail $? "Group expected to be unbalanced"

	sleep 2
	$IP nexthop replace id 10 group 1/2,3 type resilient $timer 4
	sleep 4

	# 6 seconds, past the new timer, before the old timer.
	nexthop_bucket_nhid_count_check 10 2 6
	check_err $? "Group expected to be balanced"

	log_test "Bucket migration after $timer decrease"

	$IP nexthop flush &> /dev/null
}

__nexthop_res_group_increase_timer_del_test()
{
	local timer=$1; shift

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1
	$IP nexthop add id 3 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1,100/2,100/3,1 \
	    type resilient buckets 8 $timer 4
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1,100/2,300/3,1 type resilient

	nexthop_bucket_nhid_count_check 10 2 6
	check_fail $? "Group expected to be unbalanced"

	sleep 2
	$IP nexthop replace id 10 group 1/2,3 type resilient $timer 8
	sleep 4

	# 6 seconds, past the original timer.
	nexthop_bucket_nhid_count_check 10 2 6
	check_fail $? "Group still expected to be unbalanced"

	sleep 4

	# 10 seconds, past the new timer.
	nexthop_bucket_nhid_count_check 10 2 6
	check_err $? "Group expected to be balanced"

	log_test "Bucket migration after $timer increase"

	$IP nexthop flush &> /dev/null
}

nexthop_res_group_increase_idle_timer_test()
{
	__nexthop_res_group_increase_timer_test idle_timer
}

nexthop_res_group_decrease_idle_timer_test()
{
	__nexthop_res_group_decrease_timer_test idle_timer
}

nexthop_res_group_unbalanced_timer_test()
{
	local i

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1/2 type resilient \
	    buckets 8 idle_timer 6 unbalanced_timer 10
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1/2,3 type resilient

	for i in 1 2; do
		sleep 4
		nexthop_bucket_nhid_count_check 10  1 4  2 4
		check_err $? "$i: Group expected to be unbalanced"
		nexthop_res_mark_buckets_busy 10 1
	done

	# 3 x sleep 4 > unbalanced timer 10
	sleep 4
	nexthop_bucket_nhid_count_check 10  1 2  2 6
	check_err $? "Group expected to be balanced"

	log_test "Bucket migration after unbalanced timer"

	$IP nexthop flush &> /dev/null
}

nexthop_res_group_unbalanced_timer_del_test()
{
	local i

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1
	$IP nexthop add id 3 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1,50/2,50/3,1 type resilient \
	    buckets 8 idle_timer 6 unbalanced_timer 10
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1,50/2,150/3,1 type resilient

	# Check that NH delete does not reset unbalanced time.
	sleep 4
	$IP nexthop delete id 3
	nexthop_bucket_nhid_count_check 10  1 4  2 4
	check_err $? "1: Group expected to be unbalanced"
	nexthop_res_mark_buckets_busy 10 1

	sleep 4
	nexthop_bucket_nhid_count_check 10  1 4  2 4
	check_err $? "2: Group expected to be unbalanced"
	nexthop_res_mark_buckets_busy 10 1

	# 3 x sleep 4 > unbalanced timer 10
	sleep 4
	nexthop_bucket_nhid_count_check 10  1 2  2 6
	check_err $? "Group expected to be balanced"

	log_test "Bucket migration after unbalanced timer (with delete)"

	$IP nexthop flush &> /dev/null
}

nexthop_res_group_no_unbalanced_timer_test()
{
	local i

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1/2 type resilient buckets 8
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1/2,3 type resilient

	for i in $(seq 3); do
		sleep 60
		nexthop_bucket_nhid_count_check 10 2 6
		check_fail $? "$i: Group expected to be unbalanced"
		nexthop_res_mark_buckets_busy 10 1
	done

	log_test "Buckets never force-migrated without unbalanced timer"

	$IP nexthop flush &> /dev/null
}

nexthop_res_group_short_unbalanced_timer_test()
{
	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1/2 type resilient \
	    buckets 8 idle_timer 120 unbalanced_timer 4
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1/2,3 type resilient

	nexthop_bucket_nhid_count_check 10 2 6
	check_fail $? "Group expected to be unbalanced"

	sleep 5

	nexthop_bucket_nhid_count_check 10 2 6
	check_err $? "Group expected to be balanced"

	log_test "Bucket migration after unbalanced < idle timer"

	$IP nexthop flush &> /dev/null
}

nexthop_res_group_increase_unbalanced_timer_test()
{
	__nexthop_res_group_increase_timer_test unbalanced_timer
}

nexthop_res_group_decrease_unbalanced_timer_test()
{
	__nexthop_res_group_decrease_timer_test unbalanced_timer
}

nexthop_res_group_force_migrate_busy_test()
{
	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1

	RET=0

	$IP nexthop add id 10 group 1/2 type resilient \
	    buckets 8 idle_timer 120
	nexthop_res_mark_buckets_busy 10 1
	$IP nexthop replace id 10 group 1/2,3 type resilient

	nexthop_bucket_nhid_count_check 10 2 6
	check_fail $? "Group expected to be unbalanced"

	$IP nexthop replace id 10 group 2 type resilient
	nexthop_bucket_nhid_count_check 10 2 8
	check_err $? "All buckets expected to have migrated"

	log_test "Busy buckets force-migrated when NH removed"

	$IP nexthop flush &> /dev/null
}

nexthop_single_replace_test()
{
	RET=0
@@ -299,6 +811,63 @@ nexthop_single_in_group_replace_err_test()
	nexthop_resource_set 9999
}

nexthop_single_in_res_group_replace_test()
{
	RET=0

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1
	$IP nexthop add id 10 group 1/2 type resilient buckets 4

	$IP nexthop replace id 1 via 192.0.2.4 dev dummy1
	check_err $? "Failed to replace nexthop when should not"

	nexthop_check "id 10" "id 10 group 1/2 type resilient buckets 4 idle_timer 120 unbalanced_timer 0 unbalanced_time 0 trap"
	check_err $? "Unexpected nexthop group entry"

	nexthop_bucket_nhid_count_check 10  1 2  2 2
	check_err $? "Wrong nexthop buckets count"

	nexthop_resource_check 6
	check_err $? "Wrong nexthop occupancy"

	log_test "Single nexthop replace while in resilient group"

	$IP nexthop flush &> /dev/null
}

nexthop_single_in_res_group_replace_err_test()
{
	RET=0

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1
	$IP nexthop add id 10 group 1/2 type resilient buckets 4

	ip netns exec testns1 \
		echo 1 > $DEBUGFS_NET_DIR/fib/fail_nexthop_bucket_replace
	$IP nexthop replace id 1 via 192.0.2.4 dev dummy1 &> /dev/null
	check_fail $? "Nexthop replacement succeeded when should fail"

	nexthop_check "id 1" "id 1 via 192.0.2.2 dev dummy1 scope link trap"
	check_err $? "Unexpected nexthop entry after failure"

	nexthop_check "id 10" "id 10 group 1/2 type resilient buckets 4 idle_timer 120 unbalanced_timer 0 unbalanced_time 0 trap"
	check_err $? "Unexpected nexthop group entry after failure"

	nexthop_bucket_nhid_count_check 10  1 2  2 2
	check_err $? "Wrong nexthop buckets count"

	nexthop_resource_check 6
	check_err $? "Wrong nexthop occupancy"

	log_test "Single nexthop replace while in resilient group failure"

	$IP nexthop flush &> /dev/null
	ip netns exec testns1 \
		echo 0 > $DEBUGFS_NET_DIR/fib/fail_nexthop_bucket_replace
}

nexthop_single_in_group_delete_test()
{
	RET=0
@@ -346,6 +915,57 @@ nexthop_single_in_group_delete_err_test()
	nexthop_resource_set 9999
}

nexthop_single_in_res_group_delete_test()
{
	RET=0

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1
	$IP nexthop add id 10 group 1/2 type resilient buckets 4

	$IP nexthop del id 1
	nexthop_check "id 10" "id 10 group 2 type resilient buckets 4 idle_timer 120 unbalanced_timer 0 unbalanced_time 0 trap"
	check_err $? "Unexpected nexthop group entry"

	nexthop_bucket_nhid_count_check 10 2 4
	check_err $? "Wrong nexthop buckets count"

	nexthop_resource_check 5
	check_err $? "Wrong nexthop occupancy"

	log_test "Single nexthop delete while in resilient group"

	$IP nexthop flush &> /dev/null
}

nexthop_single_in_res_group_delete_err_test()
{
	RET=0

	$IP nexthop add id 1 via 192.0.2.2 dev dummy1
	$IP nexthop add id 2 via 192.0.2.3 dev dummy1
	$IP nexthop add id 3 via 192.0.2.4 dev dummy1
	$IP nexthop add id 10 group 1/2/3 type resilient buckets 6

	ip netns exec testns1 \
		echo 1 > $DEBUGFS_NET_DIR/fib/fail_nexthop_bucket_replace
	$IP nexthop del id 1

	# We failed to replace the two nexthop buckets that were originally
	# assigned to nhid 1.
	nexthop_bucket_nhid_count_check 10  2 2  3 2
	check_err $? "Wrong nexthop buckets count"

	nexthop_resource_check 8
	check_err $? "Wrong nexthop occupancy"

	log_test "Single nexthop delete while in resilient group failure"

	$IP nexthop flush &> /dev/null
	ip netns exec testns1 \
		echo 0 > $DEBUGFS_NET_DIR/fib/fail_nexthop_bucket_replace
}

nexthop_replay_test()
{
	RET=0
+545 −4

File changed.

Preview size limit exceeded, changes collapsed.

+361 −0

File added.

Preview size limit exceeded, changes collapsed.

+400 −0

File added.

Preview size limit exceeded, changes collapsed.