diff mbox series

[v4,net-next,02/12] netlink: spec: add shaper YAML spec

Message ID dac4964232855be1444971d260dab0c106c86c26.1724165948.git.pabeni@redhat.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series net: introduce TX H/W shaping API | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; GEN HAS DIFF 2 files changed, 1304 insertions(+);
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 16 this patch: 16
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 1 maintainers not CCed: edumazet@google.com
netdev/build_clang success Errors and warnings before: 17 this patch: 17
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 22 this patch: 22
netdev/checkpatch warning WARNING: line length of 82 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Paolo Abeni Aug. 20, 2024, 3:12 p.m. UTC
Define the user-space visible interface to query, configure and delete
network shapers via yaml definition.

Add dummy implementations for the relevant NL callbacks.

set() and delete() operations touch a single shaper creating/updating or
deleting it.
The group() operation creates a shaper's group, nesting multiple input
shapers under the specified output shaper.

Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
v3 -> v4:
 - spec file rename
 - always use '@' for references
 - detached scope -> node scope
 - inputs/output -> leaves/root
 - deduplicate leaves/root policy
 - get/dump/group return ifindex, too
 - added some general introduction to the doc
RFC v1 -> RFC v2:
 - u64 -> uint
 - net_shapers -> net-shapers
 - documented all the attributes
 - dropped [ admin-perm ] for get() op
 - group op
 - set/delete touch a single shaper
---
 Documentation/netlink/specs/net_shaper.yaml | 289 ++++++++++++++++++++
 MAINTAINERS                                 |   1 +
 include/uapi/linux/net_shaper.h             |  73 +++++
 net/Kconfig                                 |   3 +
 net/Makefile                                |   1 +
 net/shaper/Makefile                         |   9 +
 net/shaper/shaper.c                         |  45 +++
 net/shaper/shaper_nl_gen.c                  | 125 +++++++++
 net/shaper/shaper_nl_gen.h                  |  33 +++
 9 files changed, 579 insertions(+)
 create mode 100644 Documentation/netlink/specs/net_shaper.yaml
 create mode 100644 include/uapi/linux/net_shaper.h
 create mode 100644 net/shaper/Makefile
 create mode 100644 net/shaper/shaper.c
 create mode 100644 net/shaper/shaper_nl_gen.c
 create mode 100644 net/shaper/shaper_nl_gen.h

Comments

Jakub Kicinski Aug. 23, 2024, 1:48 a.m. UTC | #1
On Tue, 20 Aug 2024 17:12:23 +0200 Paolo Abeni wrote:
> diff --git a/Documentation/netlink/specs/net_shaper.yaml b/Documentation/netlink/specs/net_shaper.yaml
> new file mode 100644
> index 000000000000..a2b7900646ae
> --- /dev/null
> +++ b/Documentation/netlink/specs/net_shaper.yaml
> @@ -0,0 +1,289 @@
> +# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
> +
> +name: net-shaper
> +
> +doc: |
> +  Network device HW rate limiting configuration.
> +
> +  This API allows configuring HR shapers available on the network

What's HR?

> +  device at different levels (queues, network device) and allows
> +  arbitrary manipulation of the scheduling tree of the involved
> +  shapers.
> +
> +  Each @shaper is identified within the given device, by an @handle,
> +  comprising both a @scope and an @id, and can be created via two
> +  different modifiers: the @set operation, to create and update single

s/different modifiers/operations/

> +  shaper, and the @group operation, to create and update a scheduling
> +  group.
> +
> +  Existing shapers can be deleted via the @delete operation.

deleted -> deleted / reset

> +  The user can query the running configuration via the @get operation.

The distinction between "scoped" nodes which can be "set"
and "detached" "node"s which can only be created via "group" (AFAIU)
needs clearer explanation.

> +definitions:
> +  -
> +    type: enum
> +    name: scope
> +    doc: The different scopes where a shaper can be attached.

Are they attached or are they the nodes themselves?
Maybe just say that scope defines the ID interpretation and that's it.

> +    render-max: true
> +    entries:
> +      - name: unspec
> +        doc: The scope is not specified.
> +      -
> +        name: netdev
> +        doc: The main shaper for the given network device.
> +      -
> +        name: queue
> +        doc: The shaper is attached to the given device queue.
> +      -
> +        name: node
> +        doc: |
> +             The shaper allows grouping of queues or others
> +             node shapers, is not attached to any user-visible

Saying it's not attached is confusing. Makes it sound like it exists
outside of the scope of a struct net_device.

> +             network device component, and can be nested to
> +             either @netdev shapers or other @node shapers.

> +attribute-sets:
> +  -
> +    name: net-shaper
> +    attributes:
> +      -
> +        name: handle
> +        type: nest
> +        nested-attributes: handle
> +        doc: Unique identifier for the given shaper inside the owning device.
> +      -
> +        name: info
> +        type: nest
> +        nested-attributes: info
> +        doc: Fully describes the shaper.
> +      -
> +        name: metric
> +        type: u32
> +        enum: metric
> +        doc: Metric used by the given shaper for bw-min, bw-max and burst.
> +      -
> +        name: bw-min
> +        type: uint
> +        doc: Minimum guaranteed B/W for the given shaper.

s/Minimum g/G/
Please spell out "bandwidth" in user-facing docs.

> +      -
> +        name: bw-max
> +        type: uint
> +        doc: Shaping B/W for the given shaper or 0 when unlimited.

s/Shaping/Maximum/

> +      -
> +        name: burst
> +        type: uint
> +        doc: Maximum burst-size for bw-min and bw-max.

How about:

s/bw-min and bw-max/shaping. Should not be interpreted as a quantum./

?

> +      -
> +        name: priority
> +        type: u32
> +        doc: Scheduling priority for the given shaper.

Please clarify that that priority is only valid on children of 
a scheduling node, and the priority values are only compared
between siblings.

> +      -
> +        name: weight
> +        type: u32
> +        doc: |
> +          Weighted round robin weight for given shaper.

Relative weight of the input into a round robin node.

?

> +          The scheduling is applied to all the sibling
> +          shapers with the same priority.
> +      -
> +        name: scope
> +        type: u32
> +        enum: scope
> +        doc: The given shaper scope.

:)

> +      -
> +        name: id
> +        type: u32
> +        doc: |
> +          The given shaper id. 

"Numeric identifier of a shaper."

Do we ever use ID and scope directly in a nest with other attrs?
Or are they always wrapped in handle/parent ?
If they are always wrapped they can be a standalone attr set / space.

> The id semantic depends on the actual
> +          scope, e.g. for @queue scope it's the queue id, for
> +          @node scope it's the node identifier.
> +      -
> +        name: ifindex
> +        type: u32
> +        doc: Interface index owning the specified shaper.
> +      -
> +        name: parent
> +        type: nest
> +        nested-attributes: handle
> +        doc: |
> +          Identifier for the parent of the affected shaper,
> +          The parent is usually implied by the shaper handle itself,
> +          with the only exception of the root shaper in the @group operation.

Maybe just say that specifying the parent is only needed for group
operations? I think that's what you mean.

> +      -
> +        name: leaves
> +        type: nest
> +        multi-attr: true
> +        nested-attributes: info
> +        doc: |
> +           Describes a set of leaves shapers for a @group operation.
> +      -
> +        name: root
> +        type: nest
> +        nested-attributes: root-info
> +        doc: |
> +           Describes the root shaper for a @group operation

missing full stop

> +           Differently from @leaves and @shaper allow specifying
> +           the shaper parent handle, too.

Maybe this attr is better called "node", after all.

> +      -
> +        name: shaper
> +        type: nest
> +        nested-attributes: info
> +        doc: |
> +           Describes a single shaper for a @set operation.

Hm. How is this different than "info"?

$ git grep SHAPER_A_INFO
include/uapi/linux/net_shaper.h:        NET_SHAPER_A_INFO,
$

is "info" supposed to be used?

> +operations:
> +  list:
> +    -
> +      name: get
> +      doc: |
> +        Get / Dump information about a/all the shaper for a given device.

There's no need to "/ dump" and "/all".

> +      attribute-set: net-shaper

> +    -
> +      name: set
> +      doc: |
> +        Create or updates the specified shaper.

create or update

> +        On failure the extack is set accordingly.

it better be - no need to explain netlink basics

> +        Can't create @node scope shaper, use
> +        the @group operation instead.

"The set operation can't be used to create a @node scope shaper..."

> +      attribute-set: net-shaper
> +      flags: [ admin-perm ]
> +
> +      do:
> +        pre: net-shaper-nl-pre-doit
> +        post: net-shaper-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +            - shaper
> +
> +    -
> +      name: delete
> +      doc: |
> +        Clear (remove) the specified shaper. When deleting
> +        a @node shaper, relink all the node's leaves to the

relink -> reattach ?

> +        deleted node parent.

delete node's parent

> +        If, after the removal, the parent shaper has no more
> +        leaves and the parent shaper scope is @node, even
> +        the parent node is deleted, recursively.
> +        On failure the extack is set accordingly.
> +      attribute-set: net-shaper
> +      flags: [ admin-perm ]
> +
> +      do:
> +        pre: net-shaper-nl-pre-doit
> +        post: net-shaper-nl-post-doit
> +        request:
> +          attributes: *ns-binding
> +
> +    -
> +      name: group
> +      doc: |
> +        Creates or updates a scheduling group, adding the specified

Please use imperative mood, like in a commit message.

adding -> attach(ing)

> +++ b/net/shaper/Makefile
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Makefile for the Generic HANDSHAKE service
> +#
> +# Copyright (c) 2024, Red Hat, Inc.

Ironic that you added the copyright given the copy/paste 
fail in the contents... ;)
Jakub Kicinski Aug. 23, 2024, 1:56 a.m. UTC | #2
On Tue, 20 Aug 2024 17:12:23 +0200 Paolo Abeni wrote:
> +        Clear (remove) the specified shaper. When deleting
> +        a @node shaper, relink all the node's leaves to the
> +        deleted node parent.

I think you should explain the other case too.
When deleting a queue shaper the shaper disappears from the hierarchy
but the queue can still send traffic. It has an implicit node with
infinite bandwidth and it feeds an implicit RR node at the root of 
the hierarchy.
Paolo Abeni Aug. 23, 2024, 8:35 a.m. UTC | #3
On 8/23/24 03:48, Jakub Kicinski wrote:
> On Tue, 20 Aug 2024 17:12:23 +0200 Paolo Abeni wrote:
>> diff --git a/Documentation/netlink/specs/net_shaper.yaml b/Documentation/netlink/specs/net_shaper.yaml
>> new file mode 100644
>> index 000000000000..a2b7900646ae
>> --- /dev/null
>> +++ b/Documentation/netlink/specs/net_shaper.yaml
>> @@ -0,0 +1,289 @@
>> +# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
>> +
>> +name: net-shaper
>> +
>> +doc: |
>> +  Network device HW rate limiting configuration.
>> +
>> +  This API allows configuring HR shapers available on the network
> 
> What's HR?

Type: HW

>> +  device at different levels (queues, network device) and allows
>> +  arbitrary manipulation of the scheduling tree of the involved
>> +  shapers.
>> +
>> +  Each @shaper is identified within the given device, by an @handle,
>> +  comprising both a @scope and an @id, and can be created via two
>> +  different modifiers: the @set operation, to create and update single
> 
> s/different modifiers/operations/
> 
>> +  shaper, and the @group operation, to create and update a scheduling
>> +  group.
>> +
>> +  Existing shapers can be deleted via the @delete operation.
> 
> deleted -> deleted / reset
> 
>> +  The user can query the running configuration via the @get operation.
> 
> The distinction between "scoped" nodes which can be "set"
> and "detached" "node"s which can only be created via "group" (AFAIU)
> needs clearer explanation.

How about re-phrasing the previous paragraph as:

   Each @shaper is identified within the given device, by an @handle,
   comprising both a @scope and an @id.

   Depending on the @scope value, the shapers are attached to specific
   HW objects (queues, devices) or, for @node scope, represent a
   scheduling group that can be placed in an arbitrary location of
   the scheduling tree.

   Shapers can be created with two different operations: the @set
   operation, to create and update single "attached" shaper, and
   the @group operation, to create and update a scheduling
   group. Only the @group operation can create @node scope shapers.


>> +definitions:
>> +  -
>> +    type: enum
>> +    name: scope
>> +    doc: The different scopes where a shaper can be attached.
> 
> Are they attached or are they the nodes themselves?
> Maybe just say that scope defines the ID interpretation and that's it.

Will do.

> 
>> +    render-max: true
>> +    entries:
>> +      - name: unspec
>> +        doc: The scope is not specified.
>> +      -
>> +        name: netdev
>> +        doc: The main shaper for the given network device.
>> +      -
>> +        name: queue
>> +        doc: The shaper is attached to the given device queue.
>> +      -
>> +        name: node
>> +        doc: |
>> +             The shaper allows grouping of queues or others
>> +             node shapers, is not attached to any user-visible
> 
> Saying it's not attached is confusing. Makes it sound like it exists
> outside of the scope of a struct net_device.

What about:

   Can be placed in any arbitrary location of
   the scheduling tree, except leaves and root.

> 
>> +             network device component, and can be nested to
>> +             either @netdev shapers or other @node shapers.
> 
>> +attribute-sets:
>> +  -
>> +    name: net-shaper
>> +    attributes:
>> +      -
>> +        name: handle
>> +        type: nest
>> +        nested-attributes: handle
>> +        doc: Unique identifier for the given shaper inside the owning device.
>> +      -
>> +        name: info
>> +        type: nest
>> +        nested-attributes: info
>> +        doc: Fully describes the shaper.
>> +      -
>> +        name: metric
>> +        type: u32
>> +        enum: metric
>> +        doc: Metric used by the given shaper for bw-min, bw-max and burst.
>> +      -
>> +        name: bw-min
>> +        type: uint
>> +        doc: Minimum guaranteed B/W for the given shaper.
> 
> s/Minimum g/G/
> Please spell out "bandwidth" in user-facing docs.
> 
>> +      -
>> +        name: bw-max
>> +        type: uint
>> +        doc: Shaping B/W for the given shaper or 0 when unlimited.
> 
> s/Shaping/Maximum/
> 
>> +      -
>> +        name: burst
>> +        type: uint
>> +        doc: Maximum burst-size for bw-min and bw-max.
> 
> How about:
> 
> s/bw-min and bw-max/shaping. Should not be interpreted as a quantum./
> 
> ? >
>> +      -
>> +        name: priority
>> +        type: u32
>> +        doc: Scheduling priority for the given shaper.
> 
> Please clarify that that priority is only valid on children of
> a scheduling node, and the priority values are only compared
> between siblings.
> 
>> +      -
>> +        name: weight
>> +        type: u32
>> +        doc: |
>> +          Weighted round robin weight for given shaper.
> 
> Relative weight of the input into a round robin node.

I would avoid mentioning 'input' unless we rolls back to the previous 
naming scheme.

> ?
> 
>> +          The scheduling is applied to all the sibling
>> +          shapers with the same priority.
>> +      -
>> +        name: scope
>> +        type: u32
>> +        enum: scope
>> +        doc: The given shaper scope.
> 
> :)
> 
>> +      -
>> +        name: id
>> +        type: u32
>> +        doc: |
>> +          The given shaper id.
> 
> "Numeric identifier of a shaper."
> 
> Do we ever use ID and scope directly in a nest with other attrs?
> Or are they always wrapped in handle/parent ?
> If they are always wrapped they can be a standalone attr set / space.

Will do in the next revision.

>> +      -
>> +        name: leaves
>> +        type: nest
>> +        multi-attr: true
>> +        nested-attributes: info
>> +        doc: |
>> +           Describes a set of leaves shapers for a @group operation.
>> +      -
>> +        name: root
>> +        type: nest
>> +        nested-attributes: root-info
>> +        doc: |
>> +           Describes the root shaper for a @group operation
> 
> missing full stop
> 
>> +           Differently from @leaves and @shaper allow specifying
>> +           the shaper parent handle, too.
> 
> Maybe this attr is better called "node", after all.

Fine by me, but would that cause some confusion with the alias scope 
value?

>> +      -
>> +        name: shaper
>> +        type: nest
>> +        nested-attributes: info
>> +        doc: |
>> +           Describes a single shaper for a @set operation.
> 
> Hm. How is this different than "info"?
> 
> $ git grep SHAPER_A_INFO
> include/uapi/linux/net_shaper.h:        NET_SHAPER_A_INFO,
> $
> 
> is "info" supposed to be used?

left over from the previous revision, I will drop it.

>> +++ b/net/shaper/Makefile
>> @@ -0,0 +1,9 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +#
>> +# Makefile for the Generic HANDSHAKE service
>> +#
>> +# Copyright (c) 2024, Red Hat, Inc.
> 
> Ironic that you added the copyright given the copy/paste
> fail in the contents... ;)

Strictly speaking, since it was not intentional, it is more careless or 
stupid on my side.

Thanks,

Paolo
Paolo Abeni Aug. 23, 2024, 9:04 a.m. UTC | #4
On 8/23/24 10:35, Paolo Abeni wrote:
> On 8/23/24 03:48, Jakub Kicinski wrote:
>> On Tue, 20 Aug 2024 17:12:23 +0200 Paolo Abeni wrote:
>>> +    render-max: true
>>> +    entries:
>>> +      - name: unspec
>>> +        doc: The scope is not specified.
>>> +      -
>>> +        name: netdev
>>> +        doc: The main shaper for the given network device.
>>> +      -
>>> +        name: queue
>>> +        doc: The shaper is attached to the given device queue.
>>> +      -
>>> +        name: node
>>> +        doc: |
>>> +             The shaper allows grouping of queues or others
>>> +             node shapers, is not attached to any user-visible
>>
>> Saying it's not attached is confusing. Makes it sound like it exists
>> outside of the scope of a struct net_device.
> 
> What about:
> 
>     Can be placed in any arbitrary location of
>     the scheduling tree, except leaves and root.

To rephrase the whole doc:

	     The shaper allows grouping of queues or others
              node shapers; can be nested to either @netdev
              shapers or other @node shapers, allowing placement
              in any arbitrary location of the scheduling tree,
              except leaves and root.

/P
Jakub Kicinski Aug. 27, 2024, 1:50 a.m. UTC | #5
On Fri, 23 Aug 2024 10:35:05 +0200 Paolo Abeni wrote:

> > deleted -> deleted / reset
> >   
> >> +  The user can query the running configuration via the @get operation.  
> > 
> > The distinction between "scoped" nodes which can be "set"
> > and "detached" "node"s which can only be created via "group" (AFAIU)
> > needs clearer explanation.  
> 
> How about re-phrasing the previous paragraph as:
> 
>    Each @shaper is identified within the given device, by an @handle,
>    comprising both a @scope and an @id.
> 
>    Depending on the @scope value, the shapers are attached to specific
>    HW objects (queues, devices) or, for @node scope, represent a
>    scheduling group that can be placed in an arbitrary location of
>    the scheduling tree.

s/that can be placed in an arbitrary location of/which is an inner node/

So:

    Depending on the @scope value, the shapers are attached to specific
    HW objects (queues, devices) or, for @node scope, represent a
    scheduling group which is an inner node the scheduling tree with 
    multiple inputs.

>    Shapers can be created with two different operations: the @set
>    operation, to create and update single "attached" shaper, and
>    the @group operation, to create and update a scheduling
>    group. Only the @group operation can create @node scope shapers.

> >> +    render-max: true
> >> +    entries:
> >> +      - name: unspec
> >> +        doc: The scope is not specified.
> >> +      -
> >> +        name: netdev
> >> +        doc: The main shaper for the given network device.
> >> +      -
> >> +        name: queue
> >> +        doc: The shaper is attached to the given device queue.
> >> +      -
> >> +        name: node
> >> +        doc: |
> >> +             The shaper allows grouping of queues or others
> >> +             node shapers, is not attached to any user-visible  
> > 
> > Saying it's not attached is confusing. Makes it sound like it exists
> > outside of the scope of a struct net_device.  
> 
> What about:
> 
>    Can be placed in any arbitrary location of
>    the scheduling tree, except leaves and root.

Oh, I was thinking along the same lines above.
Whether "except leaves or root" or "inner node" is clearer is up to you.

> >> +      -
> >> +        name: weight
> >> +        type: u32
> >> +        doc: |
> >> +          Weighted round robin weight for given shaper.  
> > 
> > Relative weight of the input into a round robin node.  
> 
> I would avoid mentioning 'input' unless we rolls back to the previous 
> naming scheme.

Okay, how about:

	Relative weight used by a parent round robin node.

> >> +           Differently from @leaves and @shaper allow specifying
> >> +           the shaper parent handle, too.  
> > 
> > Maybe this attr is better called "node", after all.  
> 
> Fine by me, but would that cause some confusion with the alias scope 
> value?

But to be clear, the "root" describes the node we're creating, right?
Alternatively maybe we should remove the level of nesting and let 
the attributes live at the command level?
Paolo Abeni Aug. 27, 2024, 7:41 a.m. UTC | #6
On 8/27/24 03:50, Jakub Kicinski wrote:
> On Fri, 23 Aug 2024 10:35:05 +0200 Paolo Abeni wrote:
>>>> +        name: node
>>>> +        doc: |
>>>> +             The shaper allows grouping of queues or others
>>>> +             node shapers, is not attached to any user-visible
>>>
>>> Saying it's not attached is confusing. Makes it sound like it exists
>>> outside of the scope of a struct net_device.
>>
>> What about:
>>
>>     Can be placed in any arbitrary location of
>>     the scheduling tree, except leaves and root.
> 
> Oh, I was thinking along the same lines above.
> Whether "except leaves or root" or "inner node" is clearer is up to you.

I agree "inner node" should be clear.

>>>> +      -
>>>> +        name: weight
>>>> +        type: u32
>>>> +        doc: |
>>>> +          Weighted round robin weight for given shaper.
>>>
>>> Relative weight of the input into a round robin node.
>>
>> I would avoid mentioning 'input' unless we rolls back to the previous
>> naming scheme.
> 
> Okay, how about:
> 
> 	Relative weight used by a parent round robin node.

Fine by me.

>>>> +           Differently from @leaves and @shaper allow specifying
>>>> +           the shaper parent handle, too.
>>>
>>> Maybe this attr is better called "node", after all.
>>
>> Fine by me, but would that cause some confusion with the alias scope
>> value?
> 
> But to be clear, the "root" describes the node we're creating, right?

Yes. I guess the possible confusion I mentioned will not be so 
confusing, after all. I'll go with this option.

Thanks,

Paolo
diff mbox series

Patch

diff --git a/Documentation/netlink/specs/net_shaper.yaml b/Documentation/netlink/specs/net_shaper.yaml
new file mode 100644
index 000000000000..a2b7900646ae
--- /dev/null
+++ b/Documentation/netlink/specs/net_shaper.yaml
@@ -0,0 +1,289 @@ 
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+
+name: net-shaper
+
+doc: |
+  Network device HW rate limiting configuration.
+
+  This API allows configuring HR shapers available on the network
+  device at different levels (queues, network device) and allows
+  arbitrary manipulation of the scheduling tree of the involved
+  shapers.
+
+  Each @shaper is identified within the given device, by an @handle,
+  comprising both a @scope and an @id, and can be created via two
+  different modifiers: the @set operation, to create and update single
+  shaper, and the @group operation, to create and update a scheduling
+  group.
+
+  Existing shapers can be deleted via the @delete operation.
+
+  The user can query the running configuration via the @get operation.
+
+definitions:
+  -
+    type: enum
+    name: scope
+    doc: The different scopes where a shaper can be attached.
+    render-max: true
+    entries:
+      - name: unspec
+        doc: The scope is not specified.
+      -
+        name: netdev
+        doc: The main shaper for the given network device.
+      -
+        name: queue
+        doc: The shaper is attached to the given device queue.
+      -
+        name: node
+        doc: |
+             The shaper allows grouping of queues or others
+             node shapers, is not attached to any user-visible
+             network device component, and can be nested to
+             either @netdev shapers or other @node shapers.
+  -
+    type: enum
+    name: metric
+    doc: Different metric each shaper can support.
+    entries:
+      -
+        name: bps
+        doc: Shaper operates on a bits per second basis.
+      -
+        name: pps
+        doc: Shaper operates on a packets per second basis.
+
+attribute-sets:
+  -
+    name: net-shaper
+    attributes:
+      -
+        name: handle
+        type: nest
+        nested-attributes: handle
+        doc: Unique identifier for the given shaper inside the owning device.
+      -
+        name: info
+        type: nest
+        nested-attributes: info
+        doc: Fully describes the shaper.
+      -
+        name: metric
+        type: u32
+        enum: metric
+        doc: Metric used by the given shaper for bw-min, bw-max and burst.
+      -
+        name: bw-min
+        type: uint
+        doc: Minimum guaranteed B/W for the given shaper.
+      -
+        name: bw-max
+        type: uint
+        doc: Shaping B/W for the given shaper or 0 when unlimited.
+      -
+        name: burst
+        type: uint
+        doc: Maximum burst-size for bw-min and bw-max.
+      -
+        name: priority
+        type: u32
+        doc: Scheduling priority for the given shaper.
+      -
+        name: weight
+        type: u32
+        doc: |
+          Weighted round robin weight for given shaper.
+          The scheduling is applied to all the sibling
+          shapers with the same priority.
+      -
+        name: scope
+        type: u32
+        enum: scope
+        doc: The given shaper scope.
+      -
+        name: id
+        type: u32
+        doc: |
+          The given shaper id. The id semantic depends on the actual
+          scope, e.g. for @queue scope it's the queue id, for
+          @node scope it's the node identifier.
+      -
+        name: ifindex
+        type: u32
+        doc: Interface index owning the specified shaper.
+      -
+        name: parent
+        type: nest
+        nested-attributes: handle
+        doc: |
+          Identifier for the parent of the affected shaper,
+          The parent is usually implied by the shaper handle itself,
+          with the only exception of the root shaper in the @group operation.
+      -
+        name: leaves
+        type: nest
+        multi-attr: true
+        nested-attributes: info
+        doc: |
+           Describes a set of leaves shapers for a @group operation.
+      -
+        name: root
+        type: nest
+        nested-attributes: root-info
+        doc: |
+           Describes the root shaper for a @group operation
+           Differently from @leaves and @shaper allow specifying
+           the shaper parent handle, too.
+      -
+        name: shaper
+        type: nest
+        nested-attributes: info
+        doc: |
+           Describes a single shaper for a @set operation.
+  -
+    name: handle
+    subset-of: net-shaper
+    attributes:
+      -
+        name: scope
+      -
+        name: id
+  -
+    name: info
+    subset-of: net-shaper
+    attributes:
+      -
+        name: handle
+      -
+        name: metric
+      -
+        name: bw-min
+      -
+        name: bw-max
+      -
+        name: burst
+      -
+        name: priority
+      -
+        name: weight
+  -
+    name: root-info
+    subset-of: net-shaper
+    attributes:
+      -
+        name: parent
+      -
+        name: handle
+      -
+        name: metric
+      -
+        name: bw-min
+      -
+        name: bw-max
+      -
+        name: burst
+      -
+        name: priority
+      -
+        name: weight
+
+operations:
+  list:
+    -
+      name: get
+      doc: |
+        Get / Dump information about a/all the shaper for a given device.
+      attribute-set: net-shaper
+
+      do:
+        pre: net-shaper-nl-pre-doit
+        post: net-shaper-nl-post-doit
+        request:
+          attributes: &ns-binding
+            - ifindex
+            - handle
+        reply:
+          attributes: &ns-attrs
+            - ifindex
+            - parent
+            - handle
+            - metric
+            - bw-min
+            - bw-max
+            - burst
+            - priority
+            - weight
+
+      dump:
+        request:
+          attributes:
+            - ifindex
+        reply:
+          attributes: *ns-attrs
+    -
+      name: set
+      doc: |
+        Create or updates the specified shaper.
+        On failure the extack is set accordingly.
+        Can't create @node scope shaper, use
+        the @group operation instead.
+      attribute-set: net-shaper
+      flags: [ admin-perm ]
+
+      do:
+        pre: net-shaper-nl-pre-doit
+        post: net-shaper-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - shaper
+
+    -
+      name: delete
+      doc: |
+        Clear (remove) the specified shaper. When deleting
+        a @node shaper, relink all the node's leaves to the
+        deleted node parent.
+        If, after the removal, the parent shaper has no more
+        leaves and the parent shaper scope is @node, even
+        the parent node is deleted, recursively.
+        On failure the extack is set accordingly.
+      attribute-set: net-shaper
+      flags: [ admin-perm ]
+
+      do:
+        pre: net-shaper-nl-pre-doit
+        post: net-shaper-nl-post-doit
+        request:
+          attributes: *ns-binding
+
+    -
+      name: group
+      doc: |
+        Creates or updates a scheduling group, adding the specified
+        @leaves shapers under the specified @root, eventually creating
+        the latter, if needed.
+        The @leaves shapers scope must be @queue or @node scope and
+        the @root shaper scope must be either @node or @netdev.
+        When using a root @node scope shaper, if the
+        @handle @id is not specified, a new shaper of such scope
+        is created, otherwise the specified root shaper
+        must already exist.
+        The operation is atomic, on failure the extack is set
+        accordingly and no change is applied to the device
+        shaping configuration, otherwise the root shaper
+        binding (ifindex and handle) is provided as the reply.
+      attribute-set: net-shaper
+      flags: [ admin-perm ]
+
+      do:
+        pre: net-shaper-nl-pre-doit
+        post: net-shaper-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - leaves
+            - root
+        reply:
+          attributes: *ns-binding
diff --git a/MAINTAINERS b/MAINTAINERS
index 5dbf23cf11c8..553ca2635bcc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15885,6 +15885,7 @@  F:	include/linux/inetdevice.h
 F:	include/linux/netdevice.h
 F:	include/uapi/linux/cn_proc.h
 F:	include/uapi/linux/if_*
+F:	include/uapi/linux/net_shaper.h
 F:	include/uapi/linux/netdevice.h
 X:	drivers/net/wireless/
 
diff --git a/include/uapi/linux/net_shaper.h b/include/uapi/linux/net_shaper.h
new file mode 100644
index 000000000000..05917f10b021
--- /dev/null
+++ b/include/uapi/linux/net_shaper.h
@@ -0,0 +1,73 @@ 
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/net_shaper.yaml */
+/* YNL-GEN uapi header */
+
+#ifndef _UAPI_LINUX_NET_SHAPER_H
+#define _UAPI_LINUX_NET_SHAPER_H
+
+#define NET_SHAPER_FAMILY_NAME		"net-shaper"
+#define NET_SHAPER_FAMILY_VERSION	1
+
+/**
+ * enum net_shaper_scope - The different scopes where a shaper can be attached.
+ * @NET_SHAPER_SCOPE_UNSPEC: The scope is not specified.
+ * @NET_SHAPER_SCOPE_NETDEV: The main shaper for the given network device.
+ * @NET_SHAPER_SCOPE_QUEUE: The shaper is attached to the given device queue.
+ * @NET_SHAPER_SCOPE_NODE: The shaper allows grouping of queues or others node
+ *   shapers, is not attached to any user-visible network device component, and
+ *   can be nested to either @netdev shapers or other @node shapers.
+ */
+enum net_shaper_scope {
+	NET_SHAPER_SCOPE_UNSPEC,
+	NET_SHAPER_SCOPE_NETDEV,
+	NET_SHAPER_SCOPE_QUEUE,
+	NET_SHAPER_SCOPE_NODE,
+
+	/* private: */
+	__NET_SHAPER_SCOPE_MAX,
+	NET_SHAPER_SCOPE_MAX = (__NET_SHAPER_SCOPE_MAX - 1)
+};
+
+/**
+ * enum net_shaper_metric - Different metric each shaper can support.
+ * @NET_SHAPER_METRIC_BPS: Shaper operates on a bits per second basis.
+ * @NET_SHAPER_METRIC_PPS: Shaper operates on a packets per second basis.
+ */
+enum net_shaper_metric {
+	NET_SHAPER_METRIC_BPS,
+	NET_SHAPER_METRIC_PPS,
+};
+
+enum {
+	NET_SHAPER_A_HANDLE = 1,
+	NET_SHAPER_A_INFO,
+	NET_SHAPER_A_METRIC,
+	NET_SHAPER_A_BW_MIN,
+	NET_SHAPER_A_BW_MAX,
+	NET_SHAPER_A_BURST,
+	NET_SHAPER_A_PRIORITY,
+	NET_SHAPER_A_WEIGHT,
+	NET_SHAPER_A_SCOPE,
+	NET_SHAPER_A_ID,
+	NET_SHAPER_A_IFINDEX,
+	NET_SHAPER_A_PARENT,
+	NET_SHAPER_A_LEAVES,
+	NET_SHAPER_A_ROOT,
+	NET_SHAPER_A_SHAPER,
+
+	__NET_SHAPER_A_MAX,
+	NET_SHAPER_A_MAX = (__NET_SHAPER_A_MAX - 1)
+};
+
+enum {
+	NET_SHAPER_CMD_GET = 1,
+	NET_SHAPER_CMD_SET,
+	NET_SHAPER_CMD_DELETE,
+	NET_SHAPER_CMD_GROUP,
+
+	__NET_SHAPER_CMD_MAX,
+	NET_SHAPER_CMD_MAX = (__NET_SHAPER_CMD_MAX - 1)
+};
+
+#endif /* _UAPI_LINUX_NET_SHAPER_H */
diff --git a/net/Kconfig b/net/Kconfig
index d27d0deac0bf..31fccfed04f7 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -66,6 +66,9 @@  config SKB_DECRYPTED
 config SKB_EXTENSIONS
 	bool
 
+config NET_SHAPER
+	bool
+
 menu "Networking options"
 
 source "net/packet/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index 65bb8c72a35e..60ed5190eda8 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -79,3 +79,4 @@  obj-$(CONFIG_XDP_SOCKETS)	+= xdp/
 obj-$(CONFIG_MPTCP)		+= mptcp/
 obj-$(CONFIG_MCTP)		+= mctp/
 obj-$(CONFIG_NET_HANDSHAKE)	+= handshake/
+obj-$(CONFIG_NET_SHAPER)	+= shaper/
diff --git a/net/shaper/Makefile b/net/shaper/Makefile
new file mode 100644
index 000000000000..13375884d60e
--- /dev/null
+++ b/net/shaper/Makefile
@@ -0,0 +1,9 @@ 
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Generic HANDSHAKE service
+#
+# Copyright (c) 2024, Red Hat, Inc.
+#
+
+obj-y += shaper.o shaper_nl_gen.o
+
diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c
new file mode 100644
index 000000000000..6518c7a96e86
--- /dev/null
+++ b/net/shaper/shaper.c
@@ -0,0 +1,45 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+
+#include "shaper_nl_gen.h"
+
+int net_shaper_nl_pre_doit(const struct genl_split_ops *ops,
+			   struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+void net_shaper_nl_post_doit(const struct genl_split_ops *ops,
+			     struct sk_buff *skb, struct genl_info *info)
+{
+}
+
+int net_shaper_nl_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int net_shaper_nl_get_dumpit(struct sk_buff *skb,
+			     struct netlink_callback *cb)
+{
+	return -EOPNOTSUPP;
+}
+
+int net_shaper_nl_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int net_shaper_nl_delete_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+static int __init shaper_init(void)
+{
+	return genl_register_family(&net_shaper_nl_family);
+}
+
+subsys_initcall(shaper_init);
diff --git a/net/shaper/shaper_nl_gen.c b/net/shaper/shaper_nl_gen.c
new file mode 100644
index 000000000000..b0a4bdf1f00a
--- /dev/null
+++ b/net/shaper/shaper_nl_gen.c
@@ -0,0 +1,125 @@ 
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/net_shaper.yaml */
+/* YNL-GEN kernel source */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "shaper_nl_gen.h"
+
+#include <uapi/linux/net_shaper.h>
+
+/* Common nested types */
+const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_ID + 1] = {
+	[NET_SHAPER_A_SCOPE] = NLA_POLICY_MAX(NLA_U32, 3),
+	[NET_SHAPER_A_ID] = { .type = NLA_U32, },
+};
+
+const struct nla_policy net_shaper_info_nl_policy[NET_SHAPER_A_WEIGHT + 1] = {
+	[NET_SHAPER_A_HANDLE] = NLA_POLICY_NESTED(net_shaper_handle_nl_policy),
+	[NET_SHAPER_A_METRIC] = NLA_POLICY_MAX(NLA_U32, 1),
+	[NET_SHAPER_A_BW_MIN] = { .type = NLA_UINT, },
+	[NET_SHAPER_A_BW_MAX] = { .type = NLA_UINT, },
+	[NET_SHAPER_A_BURST] = { .type = NLA_UINT, },
+	[NET_SHAPER_A_PRIORITY] = { .type = NLA_U32, },
+	[NET_SHAPER_A_WEIGHT] = { .type = NLA_U32, },
+};
+
+const struct nla_policy net_shaper_root_info_nl_policy[NET_SHAPER_A_PARENT + 1] = {
+	[NET_SHAPER_A_PARENT] = NLA_POLICY_NESTED(net_shaper_handle_nl_policy),
+	[NET_SHAPER_A_HANDLE] = NLA_POLICY_NESTED(net_shaper_handle_nl_policy),
+	[NET_SHAPER_A_METRIC] = NLA_POLICY_MAX(NLA_U32, 1),
+	[NET_SHAPER_A_BW_MIN] = { .type = NLA_UINT, },
+	[NET_SHAPER_A_BW_MAX] = { .type = NLA_UINT, },
+	[NET_SHAPER_A_BURST] = { .type = NLA_UINT, },
+	[NET_SHAPER_A_PRIORITY] = { .type = NLA_U32, },
+	[NET_SHAPER_A_WEIGHT] = { .type = NLA_U32, },
+};
+
+/* NET_SHAPER_CMD_GET - do */
+static const struct nla_policy net_shaper_get_do_nl_policy[NET_SHAPER_A_IFINDEX + 1] = {
+	[NET_SHAPER_A_IFINDEX] = { .type = NLA_U32, },
+	[NET_SHAPER_A_HANDLE] = NLA_POLICY_NESTED(net_shaper_handle_nl_policy),
+};
+
+/* NET_SHAPER_CMD_GET - dump */
+static const struct nla_policy net_shaper_get_dump_nl_policy[NET_SHAPER_A_IFINDEX + 1] = {
+	[NET_SHAPER_A_IFINDEX] = { .type = NLA_U32, },
+};
+
+/* NET_SHAPER_CMD_SET - do */
+static const struct nla_policy net_shaper_set_nl_policy[NET_SHAPER_A_SHAPER + 1] = {
+	[NET_SHAPER_A_IFINDEX] = { .type = NLA_U32, },
+	[NET_SHAPER_A_SHAPER] = NLA_POLICY_NESTED(net_shaper_info_nl_policy),
+};
+
+/* NET_SHAPER_CMD_DELETE - do */
+static const struct nla_policy net_shaper_delete_nl_policy[NET_SHAPER_A_IFINDEX + 1] = {
+	[NET_SHAPER_A_IFINDEX] = { .type = NLA_U32, },
+	[NET_SHAPER_A_HANDLE] = NLA_POLICY_NESTED(net_shaper_handle_nl_policy),
+};
+
+/* NET_SHAPER_CMD_GROUP - do */
+static const struct nla_policy net_shaper_group_nl_policy[NET_SHAPER_A_ROOT + 1] = {
+	[NET_SHAPER_A_IFINDEX] = { .type = NLA_U32, },
+	[NET_SHAPER_A_LEAVES] = NLA_POLICY_NESTED(net_shaper_info_nl_policy),
+	[NET_SHAPER_A_ROOT] = NLA_POLICY_NESTED(net_shaper_root_info_nl_policy),
+};
+
+/* Ops table for net_shaper */
+static const struct genl_split_ops net_shaper_nl_ops[] = {
+	{
+		.cmd		= NET_SHAPER_CMD_GET,
+		.pre_doit	= net_shaper_nl_pre_doit,
+		.doit		= net_shaper_nl_get_doit,
+		.post_doit	= net_shaper_nl_post_doit,
+		.policy		= net_shaper_get_do_nl_policy,
+		.maxattr	= NET_SHAPER_A_IFINDEX,
+		.flags		= GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= NET_SHAPER_CMD_GET,
+		.dumpit		= net_shaper_nl_get_dumpit,
+		.policy		= net_shaper_get_dump_nl_policy,
+		.maxattr	= NET_SHAPER_A_IFINDEX,
+		.flags		= GENL_CMD_CAP_DUMP,
+	},
+	{
+		.cmd		= NET_SHAPER_CMD_SET,
+		.pre_doit	= net_shaper_nl_pre_doit,
+		.doit		= net_shaper_nl_set_doit,
+		.post_doit	= net_shaper_nl_post_doit,
+		.policy		= net_shaper_set_nl_policy,
+		.maxattr	= NET_SHAPER_A_SHAPER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= NET_SHAPER_CMD_DELETE,
+		.pre_doit	= net_shaper_nl_pre_doit,
+		.doit		= net_shaper_nl_delete_doit,
+		.post_doit	= net_shaper_nl_post_doit,
+		.policy		= net_shaper_delete_nl_policy,
+		.maxattr	= NET_SHAPER_A_IFINDEX,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= NET_SHAPER_CMD_GROUP,
+		.pre_doit	= net_shaper_nl_pre_doit,
+		.doit		= net_shaper_nl_group_doit,
+		.post_doit	= net_shaper_nl_post_doit,
+		.policy		= net_shaper_group_nl_policy,
+		.maxattr	= NET_SHAPER_A_ROOT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+};
+
+struct genl_family net_shaper_nl_family __ro_after_init = {
+	.name		= NET_SHAPER_FAMILY_NAME,
+	.version	= NET_SHAPER_FAMILY_VERSION,
+	.netnsok	= true,
+	.parallel_ops	= true,
+	.module		= THIS_MODULE,
+	.split_ops	= net_shaper_nl_ops,
+	.n_split_ops	= ARRAY_SIZE(net_shaper_nl_ops),
+};
diff --git a/net/shaper/shaper_nl_gen.h b/net/shaper/shaper_nl_gen.h
new file mode 100644
index 000000000000..9b0682c83a07
--- /dev/null
+++ b/net/shaper/shaper_nl_gen.h
@@ -0,0 +1,33 @@ 
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/net_shaper.yaml */
+/* YNL-GEN kernel header */
+
+#ifndef _LINUX_NET_SHAPER_GEN_H
+#define _LINUX_NET_SHAPER_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/net_shaper.h>
+
+/* Common nested types */
+extern const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_ID + 1];
+extern const struct nla_policy net_shaper_info_nl_policy[NET_SHAPER_A_WEIGHT + 1];
+extern const struct nla_policy net_shaper_root_info_nl_policy[NET_SHAPER_A_PARENT + 1];
+
+int net_shaper_nl_pre_doit(const struct genl_split_ops *ops,
+			   struct sk_buff *skb, struct genl_info *info);
+void
+net_shaper_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+			struct genl_info *info);
+
+int net_shaper_nl_get_doit(struct sk_buff *skb, struct genl_info *info);
+int net_shaper_nl_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int net_shaper_nl_set_doit(struct sk_buff *skb, struct genl_info *info);
+int net_shaper_nl_delete_doit(struct sk_buff *skb, struct genl_info *info);
+int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info);
+
+extern struct genl_family net_shaper_nl_family;
+
+#endif /* _LINUX_NET_SHAPER_GEN_H */