diff mbox series

[RFC,net-next,7/7] netdevsim: Add simple FIB resource controller via devlink

Message ID 20180322225757.10377-8-dsa@cumulusnetworks.com
State RFC, archived
Delegated to: David Miller
Headers show
Series net: Allow FIB notifiers to fail add and replace | expand

Commit Message

David Ahern March 22, 2018, 10:57 p.m. UTC
From: David Ahern <dsahern@gmail.com>

Add devlink support to netdevsim and use it to implement a simple,
profile based resource controller. Only one controller is needed
per namespace, so the first netdevsim netdevice in a namespace
registers with devlink. If that device is deleted, the resource
settings are deleted.

The resource controller allows a user to limit the number of IPv4 and
IPv6 FIB entries and FIB rules. The resource paths are:
    /IPv4
    /IPv4/fib
    /IPv4/fib-rules
    /IPv6
    /IPv6/fib
    /IPv6/fib-rules

The IPv4 and IPv6 top level resources are unlimited in size and can not
be changed. From there, the number of FIB entries and FIB rule entries
are unlimited by default. A user can specify a limit for the fib and
fib-rules resources:

    $ devlink resource set netdevsim/netdevsim0 path /IPv4/fib size 96
    $ devlink resource set netdevsim/netdevsim0 path /IPv4/fib-rules size 16
    $ devlink resource set netdevsim/netdevsim0 path /IPv6/fib size 64
    $ devlink resource set netdevsim/netdevsim0 path /IPv6/fib-rules size 16
    $ devlink dev reload netdevsim/netdevsim0

such that the number of rules or routes is limited:
    $ for n in $(seq 1 32); do ip ro add 10.99.$n.0/24 dev eth1; done
    Error: netdevsim: Exceeded number of supported fib entries.

    $ devlink resource show netdevsim/netdevsim0
    netdevsim/netdevsim0:
      name IPv4 size unlimited unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables non
        resources:
          name fib size 96 occ 96 unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables
    ...

The resource limits for one namespace have no bearing on another:
    $ ip netns add foobar
    $ ip -netns foobar link add bar type netdevsim
    $ ip netns exec foobar devlink resource show netdevsim/netdevsim1
    netdevsim/netdevsim1:
      name IPv4 size unlimited unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables non
        resources:
          name fib size unlimited occ 0 unit entry size_min 0 size_max unlimited size_gran 1 dpipe_t
          name fib-rules size unlimited occ 0 unit entry size_min 0 size_max unlimited size_gran 1 d
    ...

With this template in place for resource management, it is fairly trivial
to extend and shows one way to implement a simple counter based resource
controller typical of network profiles.

Signed-off-by: David Ahern <dsahern@gmail.com>
---
 drivers/net/netdevsim/Makefile    |   4 +
 drivers/net/netdevsim/devlink.c   | 281 ++++++++++++++++++++++++++++++++++++++
 drivers/net/netdevsim/fib.c       | 264 +++++++++++++++++++++++++++++++++++
 drivers/net/netdevsim/netdev.c    |  12 +-
 drivers/net/netdevsim/netdevsim.h |  42 ++++++
 5 files changed, 602 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/netdevsim/devlink.c
 create mode 100644 drivers/net/netdevsim/fib.c

Comments

Jiri Pirko March 23, 2018, 6:50 a.m. UTC | #1
Thu, Mar 22, 2018 at 11:57:57PM CET, dsa@cumulusnetworks.com wrote:
>From: David Ahern <dsahern@gmail.com>

[...]


>+void nsim_devlink_teardown(struct netdevsim *ns)
>+{
>+	if (ns->devlink) {
>+		struct net *net = dev_net(ns->netdev);
>+		bool *reg_devlink = net_generic(net, nsim_devlink_id);
>+
>+		devlink_unregister(ns->devlink);
>+		devlink_free(ns->devlink);
>+		ns->devlink = NULL;
>+
>+		nsim_devlink_net_reset(net);
>+		*reg_devlink = true;
>+	}
>+}
>+
>+void nsim_devlink_setup(struct netdevsim *ns)
>+{
>+	struct net *net = dev_net(ns->netdev);
>+	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>+	struct devlink *devlink;
>+	int err = -ENOMEM;
>+
>+	/* only one device per namespace controls devlink */
>+	if (!*reg_devlink) {
>+		ns->devlink = NULL;
>+		return;
>+	}
>+
>+	devlink = devlink_alloc(&nsim_devlink_ops, 0);
>+	if (!devlink)
>+		return;
>+
>+	devlink_net_set(devlink, net);
>+	err = devlink_register(devlink, &ns->dev);

This reg_devlink construct looks odd. Why don't you leave the devlink
instance in init_ns?



>+	if (err)
>+		goto err_devlink_free;
>+
>+	err = devlink_resources_register(devlink);
>+	if (err)
>+		goto err_dl_unregister;
>+
>+	ns->devlink = devlink;
>+
>+	*reg_devlink = false;
>+
>+	return;
>+
>+err_dl_unregister:
>+	devlink_unregister(devlink);
>+err_devlink_free:
>+	devlink_free(devlink);
>+}
>+
>+/* Initialize per network namespace state */
>+static int __net_init nsim_devlink_netns_init(struct net *net)
>+{
>+	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>+
>+	*reg_devlink = true;
>+
>+	return 0;
>+}
>+
>+static struct pernet_operations nsim_devlink_net_ops __net_initdata = {
>+	.init = nsim_devlink_netns_init,
>+	.id   = &nsim_devlink_id,
>+	.size = sizeof(bool),
>+	.async = true,
>+};
David Ahern March 23, 2018, 2:31 p.m. UTC | #2
On 3/23/18 12:50 AM, Jiri Pirko wrote:
>> +void nsim_devlink_setup(struct netdevsim *ns)
>> +{
>> +	struct net *net = dev_net(ns->netdev);
>> +	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>> +	struct devlink *devlink;
>> +	int err = -ENOMEM;
>> +
>> +	/* only one device per namespace controls devlink */
>> +	if (!*reg_devlink) {
>> +		ns->devlink = NULL;
>> +		return;
>> +	}
>> +
>> +	devlink = devlink_alloc(&nsim_devlink_ops, 0);
>> +	if (!devlink)
>> +		return;
>> +
>> +	devlink_net_set(devlink, net);
>> +	err = devlink_register(devlink, &ns->dev);
> 
> This reg_devlink construct looks odd. Why don't you leave the devlink
> instance in init_ns?

It is a per-network namespace resource controller. Since struct devlink
has a net entry, the simplest design is to put it into the namespace of
the controller. Without it, controlling resource sizes in namespace
'foobar' has to be done from init_net, which is just wrong.
Jiri Pirko March 23, 2018, 3:01 p.m. UTC | #3
Fri, Mar 23, 2018 at 03:31:02PM CET, dsa@cumulusnetworks.com wrote:
>On 3/23/18 12:50 AM, Jiri Pirko wrote:
>>> +void nsim_devlink_setup(struct netdevsim *ns)
>>> +{
>>> +	struct net *net = dev_net(ns->netdev);
>>> +	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>>> +	struct devlink *devlink;
>>> +	int err = -ENOMEM;
>>> +
>>> +	/* only one device per namespace controls devlink */
>>> +	if (!*reg_devlink) {
>>> +		ns->devlink = NULL;
>>> +		return;
>>> +	}
>>> +
>>> +	devlink = devlink_alloc(&nsim_devlink_ops, 0);
>>> +	if (!devlink)
>>> +		return;
>>> +
>>> +	devlink_net_set(devlink, net);
>>> +	err = devlink_register(devlink, &ns->dev);
>> 
>> This reg_devlink construct looks odd. Why don't you leave the devlink
>> instance in init_ns?
>
>It is a per-network namespace resource controller. Since struct devlink

Wait a second. What do you mean by "per-network namespace"? Devlink
instance is always associated with one physical device. Like an ASIC.


>has a net entry, the simplest design is to put it into the namespace of
>the controller. Without it, controlling resource sizes in namespace
>'foobar' has to be done from init_net, which is just wrong.
David Ahern March 23, 2018, 3:03 p.m. UTC | #4
On 3/23/18 9:01 AM, Jiri Pirko wrote:
> Fri, Mar 23, 2018 at 03:31:02PM CET, dsa@cumulusnetworks.com wrote:
>> On 3/23/18 12:50 AM, Jiri Pirko wrote:
>>>> +void nsim_devlink_setup(struct netdevsim *ns)
>>>> +{
>>>> +	struct net *net = dev_net(ns->netdev);
>>>> +	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>>>> +	struct devlink *devlink;
>>>> +	int err = -ENOMEM;
>>>> +
>>>> +	/* only one device per namespace controls devlink */
>>>> +	if (!*reg_devlink) {
>>>> +		ns->devlink = NULL;
>>>> +		return;
>>>> +	}
>>>> +
>>>> +	devlink = devlink_alloc(&nsim_devlink_ops, 0);
>>>> +	if (!devlink)
>>>> +		return;
>>>> +
>>>> +	devlink_net_set(devlink, net);
>>>> +	err = devlink_register(devlink, &ns->dev);
>>>
>>> This reg_devlink construct looks odd. Why don't you leave the devlink
>>> instance in init_ns?
>>
>> It is a per-network namespace resource controller. Since struct devlink
> 
> Wait a second. What do you mean by "per-network namespace"? Devlink
> instance is always associated with one physical device. Like an ASIC.
> 
> 
>> has a net entry, the simplest design is to put it into the namespace of
>> the controller. Without it, controlling resource sizes in namespace
>> 'foobar' has to be done from init_net, which is just wrong.

you need to look at how netdevsim creates a device per netdevice.
Jiri Pirko March 23, 2018, 3:05 p.m. UTC | #5
Fri, Mar 23, 2018 at 04:03:40PM CET, dsa@cumulusnetworks.com wrote:
>On 3/23/18 9:01 AM, Jiri Pirko wrote:
>> Fri, Mar 23, 2018 at 03:31:02PM CET, dsa@cumulusnetworks.com wrote:
>>> On 3/23/18 12:50 AM, Jiri Pirko wrote:
>>>>> +void nsim_devlink_setup(struct netdevsim *ns)
>>>>> +{
>>>>> +	struct net *net = dev_net(ns->netdev);
>>>>> +	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>>>>> +	struct devlink *devlink;
>>>>> +	int err = -ENOMEM;
>>>>> +
>>>>> +	/* only one device per namespace controls devlink */
>>>>> +	if (!*reg_devlink) {
>>>>> +		ns->devlink = NULL;
>>>>> +		return;
>>>>> +	}
>>>>> +
>>>>> +	devlink = devlink_alloc(&nsim_devlink_ops, 0);
>>>>> +	if (!devlink)
>>>>> +		return;
>>>>> +
>>>>> +	devlink_net_set(devlink, net);
>>>>> +	err = devlink_register(devlink, &ns->dev);
>>>>
>>>> This reg_devlink construct looks odd. Why don't you leave the devlink
>>>> instance in init_ns?
>>>
>>> It is a per-network namespace resource controller. Since struct devlink
>> 
>> Wait a second. What do you mean by "per-network namespace"? Devlink
>> instance is always associated with one physical device. Like an ASIC.
>> 
>> 
>>> has a net entry, the simplest design is to put it into the namespace of
>>> the controller. Without it, controlling resource sizes in namespace
>>> 'foobar' has to be done from init_net, which is just wrong.
>
>you need to look at how netdevsim creates a device per netdevice.

That means one devlink instance for each netdevsim device, doesn't it?
David Ahern March 23, 2018, 3:13 p.m. UTC | #6
On 3/23/18 9:05 AM, Jiri Pirko wrote:
> Fri, Mar 23, 2018 at 04:03:40PM CET, dsa@cumulusnetworks.com wrote:
>> On 3/23/18 9:01 AM, Jiri Pirko wrote:
>>> Fri, Mar 23, 2018 at 03:31:02PM CET, dsa@cumulusnetworks.com wrote:
>>>> On 3/23/18 12:50 AM, Jiri Pirko wrote:
>>>>>> +void nsim_devlink_setup(struct netdevsim *ns)
>>>>>> +{
>>>>>> +	struct net *net = dev_net(ns->netdev);
>>>>>> +	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>>>>>> +	struct devlink *devlink;
>>>>>> +	int err = -ENOMEM;
>>>>>> +
>>>>>> +	/* only one device per namespace controls devlink */
>>>>>> +	if (!*reg_devlink) {
>>>>>> +		ns->devlink = NULL;
>>>>>> +		return;
>>>>>> +	}
>>>>>> +
>>>>>> +	devlink = devlink_alloc(&nsim_devlink_ops, 0);
>>>>>> +	if (!devlink)
>>>>>> +		return;
>>>>>> +
>>>>>> +	devlink_net_set(devlink, net);
>>>>>> +	err = devlink_register(devlink, &ns->dev);
>>>>>
>>>>> This reg_devlink construct looks odd. Why don't you leave the devlink
>>>>> instance in init_ns?
>>>>
>>>> It is a per-network namespace resource controller. Since struct devlink
>>>
>>> Wait a second. What do you mean by "per-network namespace"? Devlink
>>> instance is always associated with one physical device. Like an ASIC.
>>>
>>>
>>>> has a net entry, the simplest design is to put it into the namespace of
>>>> the controller. Without it, controlling resource sizes in namespace
>>>> 'foobar' has to be done from init_net, which is just wrong.
>>
>> you need to look at how netdevsim creates a device per netdevice.
> 
> That means one devlink instance for each netdevsim device, doesn't it?
> 

yes.
Jakub Kicinski March 24, 2018, 3:47 a.m. UTC | #7
On Thu, 22 Mar 2018 15:57:57 -0700, David Ahern wrote:
> From: David Ahern <dsahern@gmail.com>
> 
> Add devlink support to netdevsim and use it to implement a simple,
> profile based resource controller. Only one controller is needed
> per namespace, so the first netdevsim netdevice in a namespace
> registers with devlink. If that device is deleted, the resource
> settings are deleted.

FWIW some nits from me blow.

> diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile
> index 09388c06171d..449b2a1a1800 100644
> --- a/drivers/net/netdevsim/Makefile
> +++ b/drivers/net/netdevsim/Makefile
> @@ -9,3 +9,7 @@ ifeq ($(CONFIG_BPF_SYSCALL),y)
>  netdevsim-objs += \
>  	bpf.o
>  endif
> +
> +ifneq ($(CONFIG_NET_DEVLINK),)

Hm.  Don't you need MAY_USE_DEVLINK dependency perhaps?

> +netdevsim-objs += devlink.o fib.o
> +endif
> diff --git a/drivers/net/netdevsim/devlink.c b/drivers/net/netdevsim/devlink.c
> new file mode 100644
> index 000000000000..d10558e1b022
> --- /dev/null
> +++ b/drivers/net/netdevsim/devlink.c
 
> +static int devlink_resources_register(struct devlink *devlink)
> +{
> +	struct devlink_resource_size_params params = {
> +		.size_max = (u64)-1,
> +		.size_granularity = 1,
> +		.unit = DEVLINK_RESOURCE_UNIT_ENTRY
> +	};
> +	struct net *net = devlink_net(devlink);
> +	int err;
> +	u64 n;
> +
> +	/* Resources for IPv4 */
> +	err = devlink_resource_register(devlink, "IPv4", (u64)-1,
> +					NSIM_RESOURCE_IPV4,
> +					DEVLINK_RESOURCE_ID_PARENT_TOP,
> +					&params, NULL);
> +	if (err) {
> +		pr_err("Failed to register IPv4 top resource\n");
> +		goto out;

nit: why goto out here and return err everywhere else?  If I was to
     choose I'd rather see returns.  goto X; X: return; is less
     obviously correct IMHO.  Besides labels should be called by the
     action they perform/undo, so goto err_return? :S

> +	}
> +
> +	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true);
> +	err = devlink_resource_register(devlink, "fib", n,
> +					NSIM_RESOURCE_IPV4_FIB,
> +					NSIM_RESOURCE_IPV4,
> +					&params, &nsim_ipv4_fib_res_ops);
> +	if (err) {
> +		pr_err("Failed to register IPv4 FIB resource\n");
> +		return err;
> +	}
> +
> +	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true);
> +	err = devlink_resource_register(devlink, "fib-rules", n,
> +					NSIM_RESOURCE_IPV4_FIB_RULES,
> +					NSIM_RESOURCE_IPV4,
> +					&params, &nsim_ipv4_fib_rules_res_ops);
> +	if (err) {
> +		pr_err("Failed to register IPv4 FIB rules resource\n");
> +		return err;
> +	}
> +
> +	/* Resources for IPv6 */
> +	err = devlink_resource_register(devlink, "IPv6", (u64)-1,
> +					NSIM_RESOURCE_IPV6,
> +					DEVLINK_RESOURCE_ID_PARENT_TOP,
> +					&params, NULL);
> +	if (err) {
> +		pr_err("Failed to register IPv6 top resource\n");
> +		goto out;
> +	}
> +
> +	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true);
> +	err = devlink_resource_register(devlink, "fib", n,
> +					NSIM_RESOURCE_IPV6_FIB,
> +					NSIM_RESOURCE_IPV6,
> +					&params, &nsim_ipv6_fib_res_ops);
> +	if (err) {
> +		pr_err("Failed to register IPv6 FIB resource\n");
> +		return err;
> +	}
> +
> +	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true);
> +	err = devlink_resource_register(devlink, "fib-rules", n,
> +					NSIM_RESOURCE_IPV6_FIB_RULES,
> +					NSIM_RESOURCE_IPV6,
> +					&params, &nsim_ipv6_fib_rules_res_ops);
> +	if (err) {
> +		pr_err("Failed to register IPv6 FIB rules resource\n");
> +		return err;
> +	}
> +out:
> +	return err;
> +}

> +void nsim_devlink_teardown(struct netdevsim *ns)
> +{
> +	if (ns->devlink) {
> +		struct net *net = dev_net(ns->netdev);
> +		bool *reg_devlink = net_generic(net, nsim_devlink_id);

nit: reverse xmas tree

> +		devlink_unregister(ns->devlink);
> +		devlink_free(ns->devlink);
> +		ns->devlink = NULL;
> +
> +		nsim_devlink_net_reset(net);
> +		*reg_devlink = true;
> +	}
> +}

> diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c
> new file mode 100644
> index 000000000000..b77dcafc7158
> --- /dev/null
> +++ b/drivers/net/netdevsim/fib.c

> +static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
> +			     void *ptr)
> +{
> +	struct fib_notifier_info *info = ptr;
> +	int err;
> +
> +	switch (event) {
> +	case FIB_EVENT_RULE_ADD: /* fall through */

nit: I don't think fall through comment is needed for back-to-back
     cases.

> +	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);
> +}
Jiri Pirko March 24, 2018, 7:26 a.m. UTC | #8
Fri, Mar 23, 2018 at 04:13:14PM CET, dsa@cumulusnetworks.com wrote:
>On 3/23/18 9:05 AM, Jiri Pirko wrote:
>> Fri, Mar 23, 2018 at 04:03:40PM CET, dsa@cumulusnetworks.com wrote:
>>> On 3/23/18 9:01 AM, Jiri Pirko wrote:
>>>> Fri, Mar 23, 2018 at 03:31:02PM CET, dsa@cumulusnetworks.com wrote:
>>>>> On 3/23/18 12:50 AM, Jiri Pirko wrote:
>>>>>>> +void nsim_devlink_setup(struct netdevsim *ns)
>>>>>>> +{
>>>>>>> +	struct net *net = dev_net(ns->netdev);
>>>>>>> +	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>>>>>>> +	struct devlink *devlink;
>>>>>>> +	int err = -ENOMEM;
>>>>>>> +
>>>>>>> +	/* only one device per namespace controls devlink */
>>>>>>> +	if (!*reg_devlink) {
>>>>>>> +		ns->devlink = NULL;
>>>>>>> +		return;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	devlink = devlink_alloc(&nsim_devlink_ops, 0);
>>>>>>> +	if (!devlink)
>>>>>>> +		return;
>>>>>>> +
>>>>>>> +	devlink_net_set(devlink, net);
>>>>>>> +	err = devlink_register(devlink, &ns->dev);
>>>>>>
>>>>>> This reg_devlink construct looks odd. Why don't you leave the devlink
>>>>>> instance in init_ns?
>>>>>
>>>>> It is a per-network namespace resource controller. Since struct devlink
>>>>
>>>> Wait a second. What do you mean by "per-network namespace"? Devlink
>>>> instance is always associated with one physical device. Like an ASIC.
>>>>
>>>>
>>>>> has a net entry, the simplest design is to put it into the namespace of
>>>>> the controller. Without it, controlling resource sizes in namespace
>>>>> 'foobar' has to be done from init_net, which is just wrong.
>>>
>>> you need to look at how netdevsim creates a device per netdevice.
>> 
>> That means one devlink instance for each netdevsim device, doesn't it?
>> 
>
>yes.

Still not sure how to handle namespaces in devlink. Originally, I
thought it would be okay to leave all devlink instances in init_ns.
Because what happens if you move netdev to another namespace? Should the
devlink move as well? What if you have multiple ports, each in different
namespace. Can user move devlink instance to another namespace? Etc.
David Ahern March 24, 2018, 3:02 p.m. UTC | #9
On 3/23/18 9:47 PM, Jakub Kicinski wrote:
> On Thu, 22 Mar 2018 15:57:57 -0700, David Ahern wrote:
>> From: David Ahern <dsahern@gmail.com>
>>
>> Add devlink support to netdevsim and use it to implement a simple,
>> profile based resource controller. Only one controller is needed
>> per namespace, so the first netdevsim netdevice in a namespace
>> registers with devlink. If that device is deleted, the resource
>> settings are deleted.
> 
> FWIW some nits from me blow.
> 
>> diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile
>> index 09388c06171d..449b2a1a1800 100644
>> --- a/drivers/net/netdevsim/Makefile
>> +++ b/drivers/net/netdevsim/Makefile
>> @@ -9,3 +9,7 @@ ifeq ($(CONFIG_BPF_SYSCALL),y)
>>  netdevsim-objs += \
>>  	bpf.o
>>  endif
>> +
>> +ifneq ($(CONFIG_NET_DEVLINK),)
> 
> Hm.  Don't you need MAY_USE_DEVLINK dependency perhaps?

mlxsw uses CONFIG_NET_DEVLINK in its Makefile.

MAY_USE_DEVLINK seems to only be used in Kconfig files. Not clear to me
why it is needed at all.

> 
>> +netdevsim-objs += devlink.o fib.o
>> +endif
>> diff --git a/drivers/net/netdevsim/devlink.c b/drivers/net/netdevsim/devlink.c
>> new file mode 100644
>> index 000000000000..d10558e1b022
>> --- /dev/null
>> +++ b/drivers/net/netdevsim/devlink.c
>  
>> +static int devlink_resources_register(struct devlink *devlink)
>> +{
>> +	struct devlink_resource_size_params params = {
>> +		.size_max = (u64)-1,
>> +		.size_granularity = 1,
>> +		.unit = DEVLINK_RESOURCE_UNIT_ENTRY
>> +	};
>> +	struct net *net = devlink_net(devlink);
>> +	int err;
>> +	u64 n;
>> +
>> +	/* Resources for IPv4 */
>> +	err = devlink_resource_register(devlink, "IPv4", (u64)-1,
>> +					NSIM_RESOURCE_IPV4,
>> +					DEVLINK_RESOURCE_ID_PARENT_TOP,
>> +					&params, NULL);
>> +	if (err) {
>> +		pr_err("Failed to register IPv4 top resource\n");
>> +		goto out;
> 
> nit: why goto out here and return err everywhere else?  If I was to
>      choose I'd rather see returns.  goto X; X: return; is less
>      obviously correct IMHO.  Besides labels should be called by the
>      action they perform/undo, so goto err_return? :S

Will fix. Just got lost in the many iterations leading up to the RFC.

> 
>> +	}
>> +
>> +	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true);
>> +	err = devlink_resource_register(devlink, "fib", n,
>> +					NSIM_RESOURCE_IPV4_FIB,
>> +					NSIM_RESOURCE_IPV4,
>> +					&params, &nsim_ipv4_fib_res_ops);
>> +	if (err) {
>> +		pr_err("Failed to register IPv4 FIB resource\n");
>> +		return err;
>> +	}
>> +
>> +	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true);
>> +	err = devlink_resource_register(devlink, "fib-rules", n,
>> +					NSIM_RESOURCE_IPV4_FIB_RULES,
>> +					NSIM_RESOURCE_IPV4,
>> +					&params, &nsim_ipv4_fib_rules_res_ops);
>> +	if (err) {
>> +		pr_err("Failed to register IPv4 FIB rules resource\n");
>> +		return err;
>> +	}
>> +
>> +	/* Resources for IPv6 */
>> +	err = devlink_resource_register(devlink, "IPv6", (u64)-1,
>> +					NSIM_RESOURCE_IPV6,
>> +					DEVLINK_RESOURCE_ID_PARENT_TOP,
>> +					&params, NULL);
>> +	if (err) {
>> +		pr_err("Failed to register IPv6 top resource\n");
>> +		goto out;
>> +	}
>> +
>> +	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true);
>> +	err = devlink_resource_register(devlink, "fib", n,
>> +					NSIM_RESOURCE_IPV6_FIB,
>> +					NSIM_RESOURCE_IPV6,
>> +					&params, &nsim_ipv6_fib_res_ops);
>> +	if (err) {
>> +		pr_err("Failed to register IPv6 FIB resource\n");
>> +		return err;
>> +	}
>> +
>> +	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true);
>> +	err = devlink_resource_register(devlink, "fib-rules", n,
>> +					NSIM_RESOURCE_IPV6_FIB_RULES,
>> +					NSIM_RESOURCE_IPV6,
>> +					&params, &nsim_ipv6_fib_rules_res_ops);
>> +	if (err) {
>> +		pr_err("Failed to register IPv6 FIB rules resource\n");
>> +		return err;
>> +	}
>> +out:
>> +	return err;
>> +}
> 
>> +void nsim_devlink_teardown(struct netdevsim *ns)
>> +{
>> +	if (ns->devlink) {
>> +		struct net *net = dev_net(ns->netdev);
>> +		bool *reg_devlink = net_generic(net, nsim_devlink_id);
> 
> nit: reverse xmas tree

reg_devlink uses net, so the order can not be changed

> 
>> +		devlink_unregister(ns->devlink);
>> +		devlink_free(ns->devlink);
>> +		ns->devlink = NULL;
>> +
>> +		nsim_devlink_net_reset(net);
>> +		*reg_devlink = true;
>> +	}
>> +}
> 
>> diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c
>> new file mode 100644
>> index 000000000000..b77dcafc7158
>> --- /dev/null
>> +++ b/drivers/net/netdevsim/fib.c
> 
>> +static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
>> +			     void *ptr)
>> +{
>> +	struct fib_notifier_info *info = ptr;
>> +	int err;
>> +
>> +	switch (event) {
>> +	case FIB_EVENT_RULE_ADD: /* fall through */
> 
> nit: I don't think fall through comment is needed for back-to-back
>      cases.

ok.

Thanks for the review.

> 
>> +	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);
>> +}
David Ahern March 24, 2018, 3:05 p.m. UTC | #10
On 3/24/18 1:26 AM, Jiri Pirko wrote:
> Fri, Mar 23, 2018 at 04:13:14PM CET, dsa@cumulusnetworks.com wrote:
>> On 3/23/18 9:05 AM, Jiri Pirko wrote:
>>> Fri, Mar 23, 2018 at 04:03:40PM CET, dsa@cumulusnetworks.com wrote:
>>>> On 3/23/18 9:01 AM, Jiri Pirko wrote:
>>>>> Fri, Mar 23, 2018 at 03:31:02PM CET, dsa@cumulusnetworks.com wrote:
>>>>>> On 3/23/18 12:50 AM, Jiri Pirko wrote:
>>>>>>>> +void nsim_devlink_setup(struct netdevsim *ns)
>>>>>>>> +{
>>>>>>>> +	struct net *net = dev_net(ns->netdev);
>>>>>>>> +	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>>>>>>>> +	struct devlink *devlink;
>>>>>>>> +	int err = -ENOMEM;
>>>>>>>> +
>>>>>>>> +	/* only one device per namespace controls devlink */
>>>>>>>> +	if (!*reg_devlink) {
>>>>>>>> +		ns->devlink = NULL;
>>>>>>>> +		return;
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	devlink = devlink_alloc(&nsim_devlink_ops, 0);
>>>>>>>> +	if (!devlink)
>>>>>>>> +		return;
>>>>>>>> +
>>>>>>>> +	devlink_net_set(devlink, net);
>>>>>>>> +	err = devlink_register(devlink, &ns->dev);
>>>>>>>
>>>>>>> This reg_devlink construct looks odd. Why don't you leave the devlink
>>>>>>> instance in init_ns?
>>>>>>
>>>>>> It is a per-network namespace resource controller. Since struct devlink
>>>>>
>>>>> Wait a second. What do you mean by "per-network namespace"? Devlink
>>>>> instance is always associated with one physical device. Like an ASIC.
>>>>>
>>>>>
>>>>>> has a net entry, the simplest design is to put it into the namespace of
>>>>>> the controller. Without it, controlling resource sizes in namespace
>>>>>> 'foobar' has to be done from init_net, which is just wrong.
>>>>
>>>> you need to look at how netdevsim creates a device per netdevice.
>>>
>>> That means one devlink instance for each netdevsim device, doesn't it?
>>>
>>
>> yes.
> 
> Still not sure how to handle namespaces in devlink. Originally, I
> thought it would be okay to leave all devlink instances in init_ns.
> Because what happens if you move netdev to another namespace? Should the
> devlink move as well? What if you have multiple ports, each in different
> namespace. Can user move devlink instance to another namespace? Etc.
> 

The devlink instance is associated with a 'struct device' and those do
not change namespaces AFAIK.
Jiri Pirko March 24, 2018, 4:02 p.m. UTC | #11
Sat, Mar 24, 2018 at 04:05:38PM CET, dsa@cumulusnetworks.com wrote:
>On 3/24/18 1:26 AM, Jiri Pirko wrote:
>> Fri, Mar 23, 2018 at 04:13:14PM CET, dsa@cumulusnetworks.com wrote:
>>> On 3/23/18 9:05 AM, Jiri Pirko wrote:
>>>> Fri, Mar 23, 2018 at 04:03:40PM CET, dsa@cumulusnetworks.com wrote:
>>>>> On 3/23/18 9:01 AM, Jiri Pirko wrote:
>>>>>> Fri, Mar 23, 2018 at 03:31:02PM CET, dsa@cumulusnetworks.com wrote:
>>>>>>> On 3/23/18 12:50 AM, Jiri Pirko wrote:
>>>>>>>>> +void nsim_devlink_setup(struct netdevsim *ns)
>>>>>>>>> +{
>>>>>>>>> +	struct net *net = dev_net(ns->netdev);
>>>>>>>>> +	bool *reg_devlink = net_generic(net, nsim_devlink_id);
>>>>>>>>> +	struct devlink *devlink;
>>>>>>>>> +	int err = -ENOMEM;
>>>>>>>>> +
>>>>>>>>> +	/* only one device per namespace controls devlink */
>>>>>>>>> +	if (!*reg_devlink) {
>>>>>>>>> +		ns->devlink = NULL;
>>>>>>>>> +		return;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	devlink = devlink_alloc(&nsim_devlink_ops, 0);
>>>>>>>>> +	if (!devlink)
>>>>>>>>> +		return;
>>>>>>>>> +
>>>>>>>>> +	devlink_net_set(devlink, net);
>>>>>>>>> +	err = devlink_register(devlink, &ns->dev);
>>>>>>>>
>>>>>>>> This reg_devlink construct looks odd. Why don't you leave the devlink
>>>>>>>> instance in init_ns?
>>>>>>>
>>>>>>> It is a per-network namespace resource controller. Since struct devlink
>>>>>>
>>>>>> Wait a second. What do you mean by "per-network namespace"? Devlink
>>>>>> instance is always associated with one physical device. Like an ASIC.
>>>>>>
>>>>>>
>>>>>>> has a net entry, the simplest design is to put it into the namespace of
>>>>>>> the controller. Without it, controlling resource sizes in namespace
>>>>>>> 'foobar' has to be done from init_net, which is just wrong.
>>>>>
>>>>> you need to look at how netdevsim creates a device per netdevice.
>>>>
>>>> That means one devlink instance for each netdevsim device, doesn't it?
>>>>
>>>
>>> yes.
>> 
>> Still not sure how to handle namespaces in devlink. Originally, I
>> thought it would be okay to leave all devlink instances in init_ns.
>> Because what happens if you move netdev to another namespace? Should the
>> devlink move as well? What if you have multiple ports, each in different
>> namespace. Can user move devlink instance to another namespace? Etc.
>> 
>
>The devlink instance is associated with a 'struct device' and those do
>not change namespaces AFAIK.

Yeah. But you put devlink instance into namespace according to struct
net_device. That is mismatch.
Jakub Kicinski March 25, 2018, 6:35 a.m. UTC | #12
On Sat, 24 Mar 2018 09:02:45 -0600, David Ahern wrote:
> >> diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile
> >> index 09388c06171d..449b2a1a1800 100644
> >> --- a/drivers/net/netdevsim/Makefile
> >> +++ b/drivers/net/netdevsim/Makefile
> >> @@ -9,3 +9,7 @@ ifeq ($(CONFIG_BPF_SYSCALL),y)
> >>  netdevsim-objs += \
> >>  	bpf.o
> >>  endif
> >> +
> >> +ifneq ($(CONFIG_NET_DEVLINK),)  
> > 
> > Hm.  Don't you need MAY_USE_DEVLINK dependency perhaps?  
> 
> mlxsw uses CONFIG_NET_DEVLINK in its Makefile.
> 
> MAY_USE_DEVLINK seems to only be used in Kconfig files. Not clear to me
> why it is needed at all.

NETDEVSIM=y && DEVLINK=m
David Ahern March 25, 2018, 2:24 p.m. UTC | #13
On 3/24/18 10:02 AM, Jiri Pirko wrote:
>>>>>>>
>>>>>>> Wait a second. What do you mean by "per-network namespace"? Devlink
>>>>>>> instance is always associated with one physical device. Like an ASIC.
>>>>>>>
>>>>>>>
>>>>>>>> has a net entry, the simplest design is to put it into the namespace of
>>>>>>>> the controller. Without it, controlling resource sizes in namespace
>>>>>>>> 'foobar' has to be done from init_net, which is just wrong.
>>>>>>
>>>>>> you need to look at how netdevsim creates a device per netdevice.
>>>>>
>>>>> That means one devlink instance for each netdevsim device, doesn't it?
>>>>>
>>>>
>>>> yes.
>>>
>>> Still not sure how to handle namespaces in devlink. Originally, I
>>> thought it would be okay to leave all devlink instances in init_ns.
>>> Because what happens if you move netdev to another namespace? Should the
>>> devlink move as well? What if you have multiple ports, each in different
>>> namespace. Can user move devlink instance to another namespace? Etc.
>>>
>>
>> The devlink instance is associated with a 'struct device' and those do
>> not change namespaces AFAIK.
> 
> Yeah. But you put devlink instance into namespace according to struct
> net_device. That is mismatch.
> 

New netdevsim netdevice creates a new 'struct device' which creates a
new devlink instance. The namespace the netdev is created in is then
passed to the devlink instance. Yes, the netdev could change namespaces,
but that is something we can easily prevent if it has a devlink instance.

But really, we are way down a tangent with respect to the intent of this
patch set. I am fine with limiting the example resource controller to
just the init_net; this patch set is really aimed at the bigger idea of
allowing a notifier handler to fail route and rule adds.

We can revisit devlink and namespaces another time, along with moving
switch ports to namespaces - a key part of implementing a feature
equivalent to what Cisco calls a VDC [1].

[1]
https://www.cisco.com/c/en/us/products/collateral/switches/nexus-7000-10-slot-switch/White_Paper_Tech_Overview_Virtual_Device_Contexts.html
David Ahern March 25, 2018, 2:27 p.m. UTC | #14
On 3/25/18 12:35 AM, Jakub Kicinski wrote:
> On Sat, 24 Mar 2018 09:02:45 -0600, David Ahern wrote:
>>>> diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile
>>>> index 09388c06171d..449b2a1a1800 100644
>>>> --- a/drivers/net/netdevsim/Makefile
>>>> +++ b/drivers/net/netdevsim/Makefile
>>>> @@ -9,3 +9,7 @@ ifeq ($(CONFIG_BPF_SYSCALL),y)
>>>>  netdevsim-objs += \
>>>>  	bpf.o
>>>>  endif
>>>> +
>>>> +ifneq ($(CONFIG_NET_DEVLINK),)  
>>>
>>> Hm.  Don't you need MAY_USE_DEVLINK dependency perhaps?  
>>
>> mlxsw uses CONFIG_NET_DEVLINK in its Makefile.
>>
>> MAY_USE_DEVLINK seems to only be used in Kconfig files. Not clear to me
>> why it is needed at all.
> 
> NETDEVSIM=y && DEVLINK=m
> 

ok. I purposely did not add DEVLINK as a dependency to netdevsim to make
the resource controller truly optional. Can add it if you prefer.
Jakub Kicinski March 25, 2018, 7:53 p.m. UTC | #15
On Sun, 25 Mar 2018 08:27:42 -0600, David Ahern wrote:
> On 3/25/18 12:35 AM, Jakub Kicinski wrote:
> > On Sat, 24 Mar 2018 09:02:45 -0600, David Ahern wrote:  
> >>>> diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile
> >>>> index 09388c06171d..449b2a1a1800 100644
> >>>> --- a/drivers/net/netdevsim/Makefile
> >>>> +++ b/drivers/net/netdevsim/Makefile
> >>>> @@ -9,3 +9,7 @@ ifeq ($(CONFIG_BPF_SYSCALL),y)
> >>>>  netdevsim-objs += \
> >>>>  	bpf.o
> >>>>  endif
> >>>> +
> >>>> +ifneq ($(CONFIG_NET_DEVLINK),)    
> >>>
> >>> Hm.  Don't you need MAY_USE_DEVLINK dependency perhaps?    
> >>
> >> mlxsw uses CONFIG_NET_DEVLINK in its Makefile.
> >>
> >> MAY_USE_DEVLINK seems to only be used in Kconfig files. Not clear to me
> >> why it is needed at all.  
> > 
> > NETDEVSIM=y && DEVLINK=m
> >   
> 
> ok. I purposely did not add DEVLINK as a dependency to netdevsim to make
> the resource controller truly optional. Can add it if you prefer.

Oh, no, I don't mind.  I just thought NETDEVSIM=y DEVLINK=m case will
break the build, but I haven't tested.  If it works perfect, let's not
add unnecessary dependencies :)

(FWIW the MAY_USE_DEVLINK dep is basically depend on DEVLINK ||
DEVLINK=n, so one can still build without devlink but if devlink is a
module netdevsim will also have to be.)
Jiri Pirko March 26, 2018, 2:33 p.m. UTC | #16
Sun, Mar 25, 2018 at 04:24:11PM CEST, dsa@cumulusnetworks.com wrote:
>On 3/24/18 10:02 AM, Jiri Pirko wrote:
>>>>>>>>
>>>>>>>> Wait a second. What do you mean by "per-network namespace"? Devlink
>>>>>>>> instance is always associated with one physical device. Like an ASIC.
>>>>>>>>
>>>>>>>>
>>>>>>>>> has a net entry, the simplest design is to put it into the namespace of
>>>>>>>>> the controller. Without it, controlling resource sizes in namespace
>>>>>>>>> 'foobar' has to be done from init_net, which is just wrong.
>>>>>>>
>>>>>>> you need to look at how netdevsim creates a device per netdevice.
>>>>>>
>>>>>> That means one devlink instance for each netdevsim device, doesn't it?
>>>>>>
>>>>>
>>>>> yes.
>>>>
>>>> Still not sure how to handle namespaces in devlink. Originally, I
>>>> thought it would be okay to leave all devlink instances in init_ns.
>>>> Because what happens if you move netdev to another namespace? Should the
>>>> devlink move as well? What if you have multiple ports, each in different
>>>> namespace. Can user move devlink instance to another namespace? Etc.
>>>>
>>>
>>> The devlink instance is associated with a 'struct device' and those do
>>> not change namespaces AFAIK.
>> 
>> Yeah. But you put devlink instance into namespace according to struct
>> net_device. That is mismatch.
>> 
>
>New netdevsim netdevice creates a new 'struct device' which creates a
>new devlink instance. The namespace the netdev is created in is then
>passed to the devlink instance. Yes, the netdev could change namespaces,
>but that is something we can easily prevent if it has a devlink instance.
>
>But really, we are way down a tangent with respect to the intent of this
>patch set. I am fine with limiting the example resource controller to

I know. That is just something that I spotted :)
diff mbox series

Patch

diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile
index 09388c06171d..449b2a1a1800 100644
--- a/drivers/net/netdevsim/Makefile
+++ b/drivers/net/netdevsim/Makefile
@@ -9,3 +9,7 @@  ifeq ($(CONFIG_BPF_SYSCALL),y)
 netdevsim-objs += \
 	bpf.o
 endif
+
+ifneq ($(CONFIG_NET_DEVLINK),)
+netdevsim-objs += devlink.o fib.o
+endif
diff --git a/drivers/net/netdevsim/devlink.c b/drivers/net/netdevsim/devlink.c
new file mode 100644
index 000000000000..d10558e1b022
--- /dev/null
+++ b/drivers/net/netdevsim/devlink.c
@@ -0,0 +1,281 @@ 
+/*
+ * 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 <linux/device.h>
+#include <net/devlink.h>
+#include <net/netns/generic.h>
+
+#include "netdevsim.h"
+
+static unsigned int nsim_devlink_id;
+
+/* IPv4
+ */
+static u64 nsim_ipv4_fib_resource_occ_get(struct devlink *devlink)
+{
+	struct net *net = devlink_net(devlink);
+
+	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false);
+}
+
+static struct devlink_resource_ops nsim_ipv4_fib_res_ops = {
+	.occ_get = nsim_ipv4_fib_resource_occ_get,
+};
+
+static u64 nsim_ipv4_fib_rules_res_occ_get(struct devlink *devlink)
+{
+	struct net *net = devlink_net(devlink);
+
+	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false);
+}
+
+static struct devlink_resource_ops nsim_ipv4_fib_rules_res_ops = {
+	.occ_get = nsim_ipv4_fib_rules_res_occ_get,
+};
+
+/* IPv6
+ */
+static u64 nsim_ipv6_fib_resource_occ_get(struct devlink *devlink)
+{
+	struct net *net = devlink_net(devlink);
+
+	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false);
+}
+
+static struct devlink_resource_ops nsim_ipv6_fib_res_ops = {
+	.occ_get = nsim_ipv6_fib_resource_occ_get,
+};
+
+static u64 nsim_ipv6_fib_rules_res_occ_get(struct devlink *devlink)
+{
+	struct net *net = devlink_net(devlink);
+
+	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false);
+}
+
+static struct devlink_resource_ops nsim_ipv6_fib_rules_res_ops = {
+	.occ_get = nsim_ipv6_fib_rules_res_occ_get,
+};
+
+static int devlink_resources_register(struct devlink *devlink)
+{
+	struct devlink_resource_size_params params = {
+		.size_max = (u64)-1,
+		.size_granularity = 1,
+		.unit = DEVLINK_RESOURCE_UNIT_ENTRY
+	};
+	struct net *net = devlink_net(devlink);
+	int err;
+	u64 n;
+
+	/* Resources for IPv4 */
+	err = devlink_resource_register(devlink, "IPv4", (u64)-1,
+					NSIM_RESOURCE_IPV4,
+					DEVLINK_RESOURCE_ID_PARENT_TOP,
+					&params, NULL);
+	if (err) {
+		pr_err("Failed to register IPv4 top resource\n");
+		goto out;
+	}
+
+	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true);
+	err = devlink_resource_register(devlink, "fib", n,
+					NSIM_RESOURCE_IPV4_FIB,
+					NSIM_RESOURCE_IPV4,
+					&params, &nsim_ipv4_fib_res_ops);
+	if (err) {
+		pr_err("Failed to register IPv4 FIB resource\n");
+		return err;
+	}
+
+	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true);
+	err = devlink_resource_register(devlink, "fib-rules", n,
+					NSIM_RESOURCE_IPV4_FIB_RULES,
+					NSIM_RESOURCE_IPV4,
+					&params, &nsim_ipv4_fib_rules_res_ops);
+	if (err) {
+		pr_err("Failed to register IPv4 FIB rules resource\n");
+		return err;
+	}
+
+	/* Resources for IPv6 */
+	err = devlink_resource_register(devlink, "IPv6", (u64)-1,
+					NSIM_RESOURCE_IPV6,
+					DEVLINK_RESOURCE_ID_PARENT_TOP,
+					&params, NULL);
+	if (err) {
+		pr_err("Failed to register IPv6 top resource\n");
+		goto out;
+	}
+
+	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true);
+	err = devlink_resource_register(devlink, "fib", n,
+					NSIM_RESOURCE_IPV6_FIB,
+					NSIM_RESOURCE_IPV6,
+					&params, &nsim_ipv6_fib_res_ops);
+	if (err) {
+		pr_err("Failed to register IPv6 FIB resource\n");
+		return err;
+	}
+
+	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true);
+	err = devlink_resource_register(devlink, "fib-rules", n,
+					NSIM_RESOURCE_IPV6_FIB_RULES,
+					NSIM_RESOURCE_IPV6,
+					&params, &nsim_ipv6_fib_rules_res_ops);
+	if (err) {
+		pr_err("Failed to register IPv6 FIB rules resource\n");
+		return err;
+	}
+out:
+	return err;
+}
+
+static int nsim_devlink_reload(struct devlink *devlink)
+{
+	enum nsim_resource_id res_ids[] = {
+		NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
+		NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
+	};
+	struct net *net = devlink_net(devlink);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
+		int err;
+		u64 val;
+
+		err = devlink_resource_size_get(devlink, res_ids[i], &val);
+		if (!err) {
+			err = nsim_fib_set_max(net, res_ids[i], val);
+			if (err)
+				return err;
+		}
+	}
+
+	return 0;
+}
+
+static void nsim_devlink_net_reset(struct net *net)
+{
+	enum nsim_resource_id res_ids[] = {
+		NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
+		NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
+		if (nsim_fib_set_max(net, res_ids[i], (u64)-1)) {
+			pr_err("Failed to reset limit for resource %u\n",
+			       res_ids[i]);
+		}
+	}
+}
+
+static const struct devlink_ops nsim_devlink_ops = {
+	.reload = nsim_devlink_reload,
+};
+
+void nsim_devlink_teardown(struct netdevsim *ns)
+{
+	if (ns->devlink) {
+		struct net *net = dev_net(ns->netdev);
+		bool *reg_devlink = net_generic(net, nsim_devlink_id);
+
+		devlink_unregister(ns->devlink);
+		devlink_free(ns->devlink);
+		ns->devlink = NULL;
+
+		nsim_devlink_net_reset(net);
+		*reg_devlink = true;
+	}
+}
+
+void nsim_devlink_setup(struct netdevsim *ns)
+{
+	struct net *net = dev_net(ns->netdev);
+	bool *reg_devlink = net_generic(net, nsim_devlink_id);
+	struct devlink *devlink;
+	int err = -ENOMEM;
+
+	/* only one device per namespace controls devlink */
+	if (!*reg_devlink) {
+		ns->devlink = NULL;
+		return;
+	}
+
+	devlink = devlink_alloc(&nsim_devlink_ops, 0);
+	if (!devlink)
+		return;
+
+	devlink_net_set(devlink, net);
+	err = devlink_register(devlink, &ns->dev);
+	if (err)
+		goto err_devlink_free;
+
+	err = devlink_resources_register(devlink);
+	if (err)
+		goto err_dl_unregister;
+
+	ns->devlink = devlink;
+
+	*reg_devlink = false;
+
+	return;
+
+err_dl_unregister:
+	devlink_unregister(devlink);
+err_devlink_free:
+	devlink_free(devlink);
+}
+
+/* Initialize per network namespace state */
+static int __net_init nsim_devlink_netns_init(struct net *net)
+{
+	bool *reg_devlink = net_generic(net, nsim_devlink_id);
+
+	*reg_devlink = true;
+
+	return 0;
+}
+
+static struct pernet_operations nsim_devlink_net_ops __net_initdata = {
+	.init = nsim_devlink_netns_init,
+	.id   = &nsim_devlink_id,
+	.size = sizeof(bool),
+	.async = true,
+};
+
+void nsim_devlink_exit(void)
+{
+	unregister_pernet_subsys(&nsim_devlink_net_ops);
+	nsim_fib_exit();
+}
+
+int nsim_devlink_init(void)
+{
+	int err;
+
+	err = nsim_fib_init();
+	if (err)
+		goto err_out;
+
+	err = register_pernet_subsys(&nsim_devlink_net_ops);
+	if (err)
+		nsim_fib_exit();
+
+err_out:
+	return err;
+}
diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c
new file mode 100644
index 000000000000..b77dcafc7158
--- /dev/null
+++ b/drivers/net/netdevsim/fib.c
@@ -0,0 +1,264 @@ 
+/*
+ * 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 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)
+		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;
+
+	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 __net_initdata = {
+	.init = nsim_fib_netns_init,
+	.id   = &nsim_fib_net_id,
+	.size = sizeof(struct nsim_fib_data),
+	.async = true,
+};
+
+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;
+}
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 3fd567928f3d..8b30ab3ea2c2 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -167,6 +167,8 @@  static int nsim_init(struct net_device *dev)
 
 	SET_NETDEV_DEV(dev, &ns->dev);
 
+	nsim_devlink_setup(ns);
+
 	return 0;
 
 err_bpf_uninit:
@@ -180,6 +182,7 @@  static void nsim_uninit(struct net_device *dev)
 {
 	struct netdevsim *ns = netdev_priv(dev);
 
+	nsim_devlink_teardown(ns);
 	debugfs_remove_recursive(ns->ddir);
 	nsim_bpf_uninit(ns);
 }
@@ -478,12 +481,18 @@  static int __init nsim_module_init(void)
 	if (err)
 		goto err_debugfs_destroy;
 
-	err = rtnl_link_register(&nsim_link_ops);
+	err = nsim_devlink_init();
 	if (err)
 		goto err_unreg_bus;
 
+	err = rtnl_link_register(&nsim_link_ops);
+	if (err)
+		goto err_dl_fini;
+
 	return 0;
 
+err_dl_fini:
+	nsim_devlink_exit();
 err_unreg_bus:
 	bus_unregister(&nsim_bus);
 err_debugfs_destroy:
@@ -494,6 +503,7 @@  static int __init nsim_module_init(void)
 static void __exit nsim_module_exit(void)
 {
 	rtnl_link_unregister(&nsim_link_ops);
+	nsim_devlink_exit();
 	bus_unregister(&nsim_bus);
 	debugfs_remove_recursive(nsim_ddir);
 }
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index ea081c10efb8..37dd5f312913 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -64,6 +64,9 @@  struct netdevsim {
 
 	bool bpf_map_accept;
 	struct list_head bpf_bound_maps;
+#if IS_ENABLED(CONFIG_NET_DEVLINK)
+	struct devlink *devlink;
+#endif
 };
 
 extern struct dentry *nsim_ddir;
@@ -103,6 +106,45 @@  nsim_bpf_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
 }
 #endif
 
+#if IS_ENABLED(CONFIG_NET_DEVLINK)
+enum nsim_resource_id {
+	NSIM_RESOURCE_IPV4,
+	NSIM_RESOURCE_IPV4_FIB,
+	NSIM_RESOURCE_IPV4_FIB_RULES,
+	NSIM_RESOURCE_IPV6,
+	NSIM_RESOURCE_IPV6_FIB,
+	NSIM_RESOURCE_IPV6_FIB_RULES,
+};
+
+void nsim_devlink_setup(struct netdevsim *ns);
+void nsim_devlink_teardown(struct netdevsim *ns);
+
+int nsim_devlink_init(void);
+void nsim_devlink_exit(void);
+
+int nsim_fib_init(void);
+void nsim_fib_exit(void);
+u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max);
+int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val);
+#else
+static inline void nsim_devlink_setup(struct netdevsim *ns)
+{
+}
+
+static inline void nsim_devlink_teardown(struct netdevsim *ns)
+{
+}
+
+static inline int nsim_devlink_init(void)
+{
+	return 0;
+}
+
+static inline void nsim_devlink_exit(void)
+{
+}
+#endif
+
 static inline struct netdevsim *to_nsim(struct device *ptr)
 {
 	return container_of(ptr, struct netdevsim, dev);