[v3,19/20] net/sfc: support flow action COUNT in transfer rules

Message ID 20210618134032.1922012-20-andrew.rybchenko@oktetlabs.ru (mailing list archive)
State Superseded, archived
Delegated to: David Marchand
Headers
Series net/sfc: support flow API COUNT action |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Andrew Rybchenko June 18, 2021, 1:40 p.m. UTC
  From: Igor Romanov <igor.romanov@oktetlabs.ru>

For now, a rule may have only one dedicated counter, shared counters
are not supported.

HW delivers (or "streams") counter readings using special packets.
The driver creates a dedicated Rx queue to receive such packets
and requests that HW start "streaming" the readings to it.

The counter queue is polled periodically, and the first available
service core is used for that. Hence, the user has to specify at least
one service core for counters to work. Such a core is shared by all
MAE-capable devices managed by sfc driver.

Signed-off-by: Igor Romanov <igor.romanov@oktetlabs.ru>
Signed-off-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Reviewed-by: Andy Moreton <amoreton@xilinx.com>
Reviewed-by: Ivan Malov <ivan.malov@oktetlabs.ru>
---
 doc/guides/nics/sfc_efx.rst            |   2 +
 doc/guides/rel_notes/release_21_08.rst |   6 +
 drivers/net/sfc/meson.build            |  10 +
 drivers/net/sfc/sfc_flow.c             |   7 +
 drivers/net/sfc/sfc_mae.c              | 231 +++++++++-
 drivers/net/sfc/sfc_mae.h              |  60 +++
 drivers/net/sfc/sfc_mae_counter.c      | 578 +++++++++++++++++++++++++
 drivers/net/sfc/sfc_mae_counter.h      |  11 +
 drivers/net/sfc/sfc_stats.h            |  80 ++++
 drivers/net/sfc/sfc_tweak.h            |   9 +
 10 files changed, 989 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/sfc/sfc_stats.h
  

Comments

David Marchand June 21, 2021, 8:28 a.m. UTC | #1
On Fri, Jun 18, 2021 at 3:41 PM Andrew Rybchenko
<andrew.rybchenko@oktetlabs.ru> wrote:
> diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
> index f8880f740a..32b58e3d76 100644
> --- a/drivers/net/sfc/meson.build
> +++ b/drivers/net/sfc/meson.build
> @@ -39,6 +39,16 @@ foreach flag: extra_flags
>      endif
>  endforeach
>
> +# for clang 32-bit compiles we need libatomic for 64-bit atomic ops
> +if cc.get_id() == 'clang' and dpdk_conf.get('RTE_ARCH_64') == false
> +    ext_deps += cc.find_library('atomic')
> +endif

I don't think this block is needed.
The atomic library is globally required in config/meson.build for the
clang + 32bits case.


> +
> +# for gcc compiles we need -latomic for 128-bit atomic ops
> +if cc.get_id() == 'gcc'
> +    ext_deps += cc.find_library('atomic')
> +endif
> +

This patch breaks compilation on rhel/fedora (most failures in UNH for
this series are linked to this issue) when the libatomic rpm is not
installed.
ninja: Entering directory `/home/dmarchan/builds/build-gcc-static'
[1/18] Linking target drivers/librte_net_sfc.so.21.3
FAILED: drivers/librte_net_sfc.so.21.3
gcc  -o drivers/librte_net_sfc.so.21.3
drivers/librte_net_sfc.so.21.3.p/meson-generated_.._rte_net_sfc.pmd.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_ethdev.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_kvargs.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_mcdi.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_sriov.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_intr.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_ev.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_port.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_rx.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_tx.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_tso.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_filter.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_switch.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_mae.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_mae_counter.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_flow.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_dp.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_ef10_rx.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_ef10_essb_rx.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_ef10_tx.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_ef100_rx.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_ef100_tx.c.o
drivers/libtmp_rte_net_sfc.a.p/net_sfc_sfc_service.c.o
-I/home/dmarchan/intel-ipsec-mb/install/include
-L/home/dmarchan/intel-ipsec-mb/install/lib -Wl,--as-needed
-Wl,--no-undefined -shared -fPIC -Wl,--start-group
-Wl,-soname,librte_net_sfc.so.21 -Wl,--no-as-needed -pthread -lm -ldl
-lnuma -lfdt lib/librte_ethdev.so.21.3 lib/librte_eal.so.21.3
lib/librte_kvargs.so.21.3 lib/librte_telemetry.so.21.3
lib/librte_net.so.21.3 lib/librte_mbuf.so.21.3
lib/librte_mempool.so.21.3 lib/librte_ring.so.21.3
lib/librte_meter.so.21.3 drivers/librte_bus_pci.so.21.3
lib/librte_pci.so.21.3 drivers/librte_bus_vdev.so.21.3
drivers/librte_common_sfc_efx.so.21.3
-Wl,--version-script=/home/dmarchan/dpdk/drivers/net/sfc/version.map
/usr/lib/gcc/x86_64-redhat-linux/10/libatomic.so /usr/lib64/libbsd.so
-Wl,--end-group '-Wl,-rpath,$ORIGIN/../lib:$ORIGIN/'
-Wl,-rpath-link,/home/dmarchan/builds/build-gcc-static/lib
-Wl,-rpath-link,/home/dmarchan/builds/build-gcc-static/drivers
/usr/bin/ld: cannot find /usr/lib64/libatomic.so.1.2.0
collect2: error: ld returned 1 exit status


It seems meson related.
I do see:
Library atomic found: YES
Message: drivers/net/sfc: Defining dependency "net_sfc"


But looking at /home/dmarchan/build/build-gcc-static/meson-logs/meson-log.txt:
"""
Running compile:
Working directory:
/home/dmarchan/builds/build-gcc-static/meson-private/tmpdu27j15z
Command line:  gcc -L/home/dmarchan/intel-ipsec-mb/install/lib
-I/home/dmarchan/intel-ipsec-mb/install/include
/home/dmarchan/builds/build-gcc-static/meson-private/tmpdu27j15z/testfile.c
-o /home/dmarchan/builds/build-gcc-static/meson-private/tmpdu27j15z/output.exe
-pipe -D_FILE_OFFSET_BITS=64 -O0 -Wl,--start-group -latomic
-Wl,--end-group -Wl,--allow-shlib-undefined

Code:
 int main(void) { return 0; }

Compiler stdout:

Compiler stderr:
 /usr/bin/ld: cannot find /usr/lib64/libatomic.so.1.2.0
collect2: error: ld returned 1 exit status

Library atomic found: YES
"""


And:

[dmarchan@wsfd-netdev66 dpdk]$ cat
/usr/lib/gcc/x86_64-redhat-linux/10/libatomic.so
INPUT ( /usr/lib64/libatomic.so.1.2.0 )
[dmarchan@wsfd-netdev66 dpdk]$ file /usr/lib64/libatomic.so.1.2.0
/usr/lib64/libatomic.so.1.2.0: cannot open
`/usr/lib64/libatomic.so.1.2.0' (No such file or directory)

[dmarchan@wsfd-netdev66 dpdk]$ meson --version
0.55.3
  
Thomas Monjalon June 21, 2021, 9:30 a.m. UTC | #2
21/06/2021 10:28, David Marchand:
> On Fri, Jun 18, 2021 at 3:41 PM Andrew Rybchenko
> > +# for gcc compiles we need -latomic for 128-bit atomic ops
> > +if cc.get_id() == 'gcc'
> > +    ext_deps += cc.find_library('atomic')
> > +endif
> 
> This patch breaks compilation on rhel/fedora (most failures in UNH for
> this series are linked to this issue) when the libatomic rpm is not
> installed.
[...]
> """
> Running compile:
> Working directory:
> /home/dmarchan/builds/build-gcc-static/meson-private/tmpdu27j15z
> Command line:  gcc -L/home/dmarchan/intel-ipsec-mb/install/lib
> -I/home/dmarchan/intel-ipsec-mb/install/include
> /home/dmarchan/builds/build-gcc-static/meson-private/tmpdu27j15z/testfile.c
> -o /home/dmarchan/builds/build-gcc-static/meson-private/tmpdu27j15z/output.exe
> -pipe -D_FILE_OFFSET_BITS=64 -O0 -Wl,--start-group -latomic
> -Wl,--end-group -Wl,--allow-shlib-undefined
> 
> Code:
>  int main(void) { return 0; }
> 
> Compiler stdout:
> 
> Compiler stderr:
>  /usr/bin/ld: cannot find /usr/lib64/libatomic.so.1.2.0
> collect2: error: ld returned 1 exit status
> 
> Library atomic found: YES
> """

Indeed it looks like a bug in meson.

How does it behave with clang 32-bit?

For reference, in config/meson.build:
"""
# for clang 32-bit compiles we need libatomic for 64-bit atomic ops
if cc.get_id() == 'clang' and dpdk_conf.get('RTE_ARCH_64') == false
    atomic_dep = cc.find_library('atomic', required: true)
    add_project_link_arguments('-latomic', language: 'c')
    dpdk_extra_ldflags += '-latomic'
endif
"""

> [dmarchan@wsfd-netdev66 dpdk]$ cat
> /usr/lib/gcc/x86_64-redhat-linux/10/libatomic.so
> INPUT ( /usr/lib64/libatomic.so.1.2.0 )
> [dmarchan@wsfd-netdev66 dpdk]$ file /usr/lib64/libatomic.so.1.2.0
> /usr/lib64/libatomic.so.1.2.0: cannot open
> `/usr/lib64/libatomic.so.1.2.0' (No such file or directory)

We must handle this case where libatomic is not completely installed.

Hope there is a good fix possible.
  
Andrew Rybchenko July 1, 2021, 9:22 a.m. UTC | #3
On 6/21/21 12:30 PM, Thomas Monjalon wrote:
> 21/06/2021 10:28, David Marchand:
>> On Fri, Jun 18, 2021 at 3:41 PM Andrew Rybchenko
>>> +# for gcc compiles we need -latomic for 128-bit atomic ops
>>> +if cc.get_id() == 'gcc'
>>> +    ext_deps += cc.find_library('atomic')
>>> +endif
>>
>> This patch breaks compilation on rhel/fedora (most failures in UNH for
>> this series are linked to this issue) when the libatomic rpm is not
>> installed.
> [...]
>> """
>> Running compile:
>> Working directory:
>> /home/dmarchan/builds/build-gcc-static/meson-private/tmpdu27j15z
>> Command line:  gcc -L/home/dmarchan/intel-ipsec-mb/install/lib
>> -I/home/dmarchan/intel-ipsec-mb/install/include
>> /home/dmarchan/builds/build-gcc-static/meson-private/tmpdu27j15z/testfile.c
>> -o /home/dmarchan/builds/build-gcc-static/meson-private/tmpdu27j15z/output.exe
>> -pipe -D_FILE_OFFSET_BITS=64 -O0 -Wl,--start-group -latomic
>> -Wl,--end-group -Wl,--allow-shlib-undefined
>>
>> Code:
>>  int main(void) { return 0; }
>>
>> Compiler stdout:
>>
>> Compiler stderr:
>>  /usr/bin/ld: cannot find /usr/lib64/libatomic.so.1.2.0
>> collect2: error: ld returned 1 exit status
>>
>> Library atomic found: YES
>> """
> 
> Indeed it looks like a bug in meson.
> 
> How does it behave with clang 32-bit?
> 
> For reference, in config/meson.build:
> """
> # for clang 32-bit compiles we need libatomic for 64-bit atomic ops
> if cc.get_id() == 'clang' and dpdk_conf.get('RTE_ARCH_64') == false
>     atomic_dep = cc.find_library('atomic', required: true)
>     add_project_link_arguments('-latomic', language: 'c')
>     dpdk_extra_ldflags += '-latomic'
> endif
> """
> 
>> [dmarchan@wsfd-netdev66 dpdk]$ cat
>> /usr/lib/gcc/x86_64-redhat-linux/10/libatomic.so
>> INPUT ( /usr/lib64/libatomic.so.1.2.0 )
>> [dmarchan@wsfd-netdev66 dpdk]$ file /usr/lib64/libatomic.so.1.2.0
>> /usr/lib64/libatomic.so.1.2.0: cannot open
>> `/usr/lib64/libatomic.so.1.2.0' (No such file or directory)
> 
> We must handle this case where libatomic is not completely installed.
> 
> Hope there is a good fix possible.
> 

The build works fine for me on FC34, but it has
libatomic-11.1.1-3.fc34.x86_64 installed.

I'd like to understand what we're trying to solve here.
Are we trying to make meson to report the missing library
correctly?

If so, I think I can do simple check using cc.links()
which will fail if the library is not found. I'll
test that it works as expected if the library is not
completely installed.
  
David Marchand July 1, 2021, 12:34 p.m. UTC | #4
On Thu, Jul 1, 2021 at 11:22 AM Andrew Rybchenko
<andrew.rybchenko@oktetlabs.ru> wrote:
> The build works fine for me on FC34, but it has
> libatomic-11.1.1-3.fc34.x86_64 installed.

I first produced the issue on my "old" FC32.
Afaics, for FC33 and later, gcc now depends on libatomic and the
problem won't be noticed.
FC32 and before are EOL, but I then reproduced the issue on RHEL 8
(and Intel CI reported it on Centos 8 too).


>
> I'd like to understand what we're trying to solve here.
> Are we trying to make meson to report the missing library
> correctly?
>
> If so, I think I can do simple check using cc.links()
> which will fail if the library is not found. I'll
> test that it works as expected if the library is not
> completely installed.
>

I tried below diff, and it works for me.
"works" as in net/sfc gets disabled without libatomic installed:

diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
index 32b58e3d76..8d62aad774 100644
--- a/drivers/net/sfc/meson.build
+++ b/drivers/net/sfc/meson.build
@@ -15,6 +15,7 @@ endif
 if (arch_subdir != 'x86' or not dpdk_conf.get('RTE_ARCH_64')) and
(arch_subdir != 'arm' or not
host_machine.cpu_family().startswith('aarch64'))
     build = false
     reason = 'only supported on x86_64 and aarch64'
+    subdir_done()
 endif

 extra_flags = []
@@ -46,6 +47,14 @@ endif

 # for gcc compiles we need -latomic for 128-bit atomic ops
 if cc.get_id() == 'gcc'
+    code = '''#include <stdio.h>
+    void main() { printf("Atomilink me.\n"); }
+    '''
+    if not cc.links(code, args: '-latomic', name: 'libatomic link check')
+        build = false
+        reason = 'missing dependency, "libatomic"'
+        subdir_done()
+    endif
     ext_deps += cc.find_library('atomic')
 endif
  
Andrew Rybchenko July 1, 2021, 1:05 p.m. UTC | #5
@Bruce, see below.

On 7/1/21 3:34 PM, David Marchand wrote:
> On Thu, Jul 1, 2021 at 11:22 AM Andrew Rybchenko
> <andrew.rybchenko@oktetlabs.ru> wrote:
>> The build works fine for me on FC34, but it has
>> libatomic-11.1.1-3.fc34.x86_64 installed.
> 
> I first produced the issue on my "old" FC32.
> Afaics, for FC33 and later, gcc now depends on libatomic and the
> problem won't be noticed.
> FC32 and before are EOL, but I then reproduced the issue on RHEL 8
> (and Intel CI reported it on Centos 8 too).

I see. Thanks for the clarification.

>>
>> I'd like to understand what we're trying to solve here.
>> Are we trying to make meson to report the missing library
>> correctly?
>>
>> If so, I think I can do simple check using cc.links()
>> which will fail if the library is not found. I'll
>> test that it works as expected if the library is not
>> completely installed.
>>
> 
> I tried below diff, and it works for me.
> "works" as in net/sfc gets disabled without libatomic installed:
> 
> diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
> index 32b58e3d76..8d62aad774 100644
> --- a/drivers/net/sfc/meson.build
> +++ b/drivers/net/sfc/meson.build
> @@ -15,6 +15,7 @@ endif
>  if (arch_subdir != 'x86' or not dpdk_conf.get('RTE_ARCH_64')) and
> (arch_subdir != 'arm' or not
> host_machine.cpu_family().startswith('aarch64'))
>      build = false
>      reason = 'only supported on x86_64 and aarch64'
> +    subdir_done()

@Bruce  Shouldn't we add subdir_done() after all build = false
cases? As I understand it is OK for minimum supported meson
version.

>  endif
> 
>  extra_flags = []
> @@ -46,6 +47,14 @@ endif
> 
>  # for gcc compiles we need -latomic for 128-bit atomic ops
>  if cc.get_id() == 'gcc'
> +    code = '''#include <stdio.h>
> +    void main() { printf("Atomilink me.\n"); }
> +    '''
> +    if not cc.links(code, args: '-latomic', name: 'libatomic link check')
> +        build = false
> +        reason = 'missing dependency, "libatomic"'
> +        subdir_done()
> +    endif
>      ext_deps += cc.find_library('atomic')
>  endif

Many thanks, LGTM. I'll pick it up and add comments why
it is checked this way.
  
Bruce Richardson July 1, 2021, 1:35 p.m. UTC | #6
On Thu, Jul 01, 2021 at 04:05:56PM +0300, Andrew Rybchenko wrote:
> @Bruce, see below.
> 
> On 7/1/21 3:34 PM, David Marchand wrote:
> > On Thu, Jul 1, 2021 at 11:22 AM Andrew Rybchenko
> > <andrew.rybchenko@oktetlabs.ru> wrote:
> >> The build works fine for me on FC34, but it has
> >> libatomic-11.1.1-3.fc34.x86_64 installed.
> > 
> > I first produced the issue on my "old" FC32.
> > Afaics, for FC33 and later, gcc now depends on libatomic and the
> > problem won't be noticed.
> > FC32 and before are EOL, but I then reproduced the issue on RHEL 8
> > (and Intel CI reported it on Centos 8 too).
> 
> I see. Thanks for the clarification.
> 
> >>
> >> I'd like to understand what we're trying to solve here.
> >> Are we trying to make meson to report the missing library
> >> correctly?
> >>
> >> If so, I think I can do simple check using cc.links()
> >> which will fail if the library is not found. I'll
> >> test that it works as expected if the library is not
> >> completely installed.
> >>
> > 
> > I tried below diff, and it works for me.
> > "works" as in net/sfc gets disabled without libatomic installed:
> > 
> > diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
> > index 32b58e3d76..8d62aad774 100644
> > --- a/drivers/net/sfc/meson.build
> > +++ b/drivers/net/sfc/meson.build
> > @@ -15,6 +15,7 @@ endif
> >  if (arch_subdir != 'x86' or not dpdk_conf.get('RTE_ARCH_64')) and
> > (arch_subdir != 'arm' or not
> > host_machine.cpu_family().startswith('aarch64'))
> >      build = false
> >      reason = 'only supported on x86_64 and aarch64'
> > +    subdir_done()
> 
> @Bruce  Shouldn't we add subdir_done() after all build = false
> cases? As I understand it is OK for minimum supported meson
> version.
>
We can add it, no problem. For many files it's just not necessary, since in
a lot of cases we just do assignments to variables afterward and those vars
are just ignored and unused on return. That's the only reason it wasn't
added generally before.
  
Andrew Rybchenko July 2, 2021, 8:03 a.m. UTC | #7
On 7/1/21 4:35 PM, Bruce Richardson wrote:
> On Thu, Jul 01, 2021 at 04:05:56PM +0300, Andrew Rybchenko wrote:
>> @Bruce, see below.
>>
>> On 7/1/21 3:34 PM, David Marchand wrote:
>>> On Thu, Jul 1, 2021 at 11:22 AM Andrew Rybchenko
>>> <andrew.rybchenko@oktetlabs.ru> wrote:
>>>> The build works fine for me on FC34, but it has
>>>> libatomic-11.1.1-3.fc34.x86_64 installed.
>>>
>>> I first produced the issue on my "old" FC32.
>>> Afaics, for FC33 and later, gcc now depends on libatomic and the
>>> problem won't be noticed.
>>> FC32 and before are EOL, but I then reproduced the issue on RHEL 8
>>> (and Intel CI reported it on Centos 8 too).
>>
>> I see. Thanks for the clarification.
>>
>>>>
>>>> I'd like to understand what we're trying to solve here.
>>>> Are we trying to make meson to report the missing library
>>>> correctly?
>>>>
>>>> If so, I think I can do simple check using cc.links()
>>>> which will fail if the library is not found. I'll
>>>> test that it works as expected if the library is not
>>>> completely installed.
>>>>
>>>
>>> I tried below diff, and it works for me.
>>> "works" as in net/sfc gets disabled without libatomic installed:
>>>
>>> diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
>>> index 32b58e3d76..8d62aad774 100644
>>> --- a/drivers/net/sfc/meson.build
>>> +++ b/drivers/net/sfc/meson.build
>>> @@ -15,6 +15,7 @@ endif
>>>  if (arch_subdir != 'x86' or not dpdk_conf.get('RTE_ARCH_64')) and
>>> (arch_subdir != 'arm' or not
>>> host_machine.cpu_family().startswith('aarch64'))
>>>      build = false
>>>      reason = 'only supported on x86_64 and aarch64'
>>> +    subdir_done()
>>
>> @Bruce  Shouldn't we add subdir_done() after all build = false
>> cases? As I understand it is OK for minimum supported meson
>> version.
>>
> We can add it, no problem. For many files it's just not necessary, since in
> a lot of cases we just do assignments to variables afterward and those vars
> are just ignored and unused on return. That's the only reason it wasn't
> added generally before.

Thanks Bruce, I see the difference now.
  
Andrew Rybchenko July 2, 2021, 8:43 a.m. UTC | #8
Hi David,

On 7/1/21 4:05 PM, Andrew Rybchenko wrote:
> @Bruce, see below.
> 
> On 7/1/21 3:34 PM, David Marchand wrote:
>> On Thu, Jul 1, 2021 at 11:22 AM Andrew Rybchenko
>> <andrew.rybchenko@oktetlabs.ru> wrote:
>>> The build works fine for me on FC34, but it has
>>> libatomic-11.1.1-3.fc34.x86_64 installed.
>>
>> I first produced the issue on my "old" FC32.
>> Afaics, for FC33 and later, gcc now depends on libatomic and the
>> problem won't be noticed.
>> FC32 and before are EOL, but I then reproduced the issue on RHEL 8
>> (and Intel CI reported it on Centos 8 too).
> 
> I see. Thanks for the clarification.
> 
>>>
>>> I'd like to understand what we're trying to solve here.
>>> Are we trying to make meson to report the missing library
>>> correctly?
>>>
>>> If so, I think I can do simple check using cc.links()
>>> which will fail if the library is not found. I'll
>>> test that it works as expected if the library is not
>>> completely installed.
>>>
>>
>> I tried below diff, and it works for me.
>> "works" as in net/sfc gets disabled without libatomic installed:
>>
>> diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
>> index 32b58e3d76..8d62aad774 100644
>> --- a/drivers/net/sfc/meson.build
>> +++ b/drivers/net/sfc/meson.build
>> @@ -15,6 +15,7 @@ endif
>>  if (arch_subdir != 'x86' or not dpdk_conf.get('RTE_ARCH_64')) and
>> (arch_subdir != 'arm' or not
>> host_machine.cpu_family().startswith('aarch64'))
>>      build = false
>>      reason = 'only supported on x86_64 and aarch64'
>> +    subdir_done()
> 
> @Bruce  Shouldn't we add subdir_done() after all build = false
> cases? As I understand it is OK for minimum supported meson
> version.
> 
>>  endif
>>
>>  extra_flags = []
>> @@ -46,6 +47,14 @@ endif
>>
>>  # for gcc compiles we need -latomic for 128-bit atomic ops
>>  if cc.get_id() == 'gcc'
>> +    code = '''#include <stdio.h>
>> +    void main() { printf("Atomilink me.\n"); }
>> +    '''
>> +    if not cc.links(code, args: '-latomic', name: 'libatomic link check')
>> +        build = false
>> +        reason = 'missing dependency, "libatomic"'
>> +        subdir_done()
>> +    endif
>>      ext_deps += cc.find_library('atomic')
>>  endif
> 
> Many thanks, LGTM. I'll pick it up and add comments why
> it is checked this way.
> 

I've send v4 with the problem fixed. However, I'm afraid
build test systems should be updated to have libatomic
correctly installed. Otherwise, they do not really check
net/sfc build.

Andrew.
  
Thomas Monjalon July 2, 2021, 12:30 p.m. UTC | #9
02/07/2021 10:43, Andrew Rybchenko:
> On 7/1/21 4:05 PM, Andrew Rybchenko wrote:
> > On 7/1/21 3:34 PM, David Marchand wrote:
> >> On Thu, Jul 1, 2021 at 11:22 AM Andrew Rybchenko
> >> <andrew.rybchenko@oktetlabs.ru> wrote:
> >>> The build works fine for me on FC34, but it has
> >>> libatomic-11.1.1-3.fc34.x86_64 installed.
> >>
> >> I first produced the issue on my "old" FC32.
> >> Afaics, for FC33 and later, gcc now depends on libatomic and the
> >> problem won't be noticed.
> >> FC32 and before are EOL, but I then reproduced the issue on RHEL 8
> >> (and Intel CI reported it on Centos 8 too).
> > 
> > I see. Thanks for the clarification.
> > 
> >>>
> >>> I'd like to understand what we're trying to solve here.
> >>> Are we trying to make meson to report the missing library
> >>> correctly?
> >>>
> >>> If so, I think I can do simple check using cc.links()
> >>> which will fail if the library is not found. I'll
> >>> test that it works as expected if the library is not
> >>> completely installed.
> >>>
> >>
> >> I tried below diff, and it works for me.
> >> "works" as in net/sfc gets disabled without libatomic installed:
[...]
> >>  # for gcc compiles we need -latomic for 128-bit atomic ops
> >>  if cc.get_id() == 'gcc'
> >> +    code = '''#include <stdio.h>
> >> +    void main() { printf("Atomilink me.\n"); }
> >> +    '''
> >> +    if not cc.links(code, args: '-latomic', name: 'libatomic link check')
> >> +        build = false
> >> +        reason = 'missing dependency, "libatomic"'
> >> +        subdir_done()
> >> +    endif
> >>      ext_deps += cc.find_library('atomic')
> >>  endif
> > 
> > Many thanks, LGTM. I'll pick it up and add comments why
> > it is checked this way.
> > 
> 
> I've send v4 with the problem fixed. However, I'm afraid
> build test systems should be updated to have libatomic
> correctly installed. Otherwise, they do not really check
> net/sfc build.

When testing on old systems, sfc won't be tested anymore after this patchset.
On recent systems, sfc should be enabled I guess.
I don't see how to manage better, sorry.
  
Andrew Rybchenko July 2, 2021, 12:53 p.m. UTC | #10
On 7/2/21 3:30 PM, Thomas Monjalon wrote:
> 02/07/2021 10:43, Andrew Rybchenko:
>> On 7/1/21 4:05 PM, Andrew Rybchenko wrote:
>>> On 7/1/21 3:34 PM, David Marchand wrote:
>>>> On Thu, Jul 1, 2021 at 11:22 AM Andrew Rybchenko
>>>> <andrew.rybchenko@oktetlabs.ru> wrote:
>>>>> The build works fine for me on FC34, but it has
>>>>> libatomic-11.1.1-3.fc34.x86_64 installed.
>>>>
>>>> I first produced the issue on my "old" FC32.
>>>> Afaics, for FC33 and later, gcc now depends on libatomic and the
>>>> problem won't be noticed.
>>>> FC32 and before are EOL, but I then reproduced the issue on RHEL 8
>>>> (and Intel CI reported it on Centos 8 too).
>>>
>>> I see. Thanks for the clarification.
>>>
>>>>>
>>>>> I'd like to understand what we're trying to solve here.
>>>>> Are we trying to make meson to report the missing library
>>>>> correctly?
>>>>>
>>>>> If so, I think I can do simple check using cc.links()
>>>>> which will fail if the library is not found. I'll
>>>>> test that it works as expected if the library is not
>>>>> completely installed.
>>>>>
>>>>
>>>> I tried below diff, and it works for me.
>>>> "works" as in net/sfc gets disabled without libatomic installed:
> [...]
>>>>  # for gcc compiles we need -latomic for 128-bit atomic ops
>>>>  if cc.get_id() == 'gcc'
>>>> +    code = '''#include <stdio.h>
>>>> +    void main() { printf("Atomilink me.\n"); }
>>>> +    '''
>>>> +    if not cc.links(code, args: '-latomic', name: 'libatomic link check')
>>>> +        build = false
>>>> +        reason = 'missing dependency, "libatomic"'
>>>> +        subdir_done()
>>>> +    endif
>>>>      ext_deps += cc.find_library('atomic')
>>>>  endif
>>>
>>> Many thanks, LGTM. I'll pick it up and add comments why
>>> it is checked this way.
>>>
>>
>> I've send v4 with the problem fixed. However, I'm afraid
>> build test systems should be updated to have libatomic
>> correctly installed. Otherwise, they do not really check
>> net/sfc build.
> 
> When testing on old systems, sfc won't be tested anymore after this patchset.
> On recent systems, sfc should be enabled I guess.
> I don't see how to manage better, sorry.
> 

I see. I thought that it is possible to install missing
package on corresponding systems to make build coverage
better.

Now I automatically test build on problematic distros
with previously missing packages installed. So I have
internal build coverage anyway.
  
David Marchand July 2, 2021, 1:37 p.m. UTC | #11
On Fri, Jul 2, 2021 at 10:43 AM Andrew Rybchenko
<andrew.rybchenko@oktetlabs.ru> wrote:
> I've send v4 with the problem fixed. However, I'm afraid
> build test systems should be updated to have libatomic
> correctly installed. Otherwise, they do not really check
> net/sfc build.

CI systems must be updated if they check ABI.
And in general, we want them to continue testing net/sfc.
I sent a mail to ask for this.
  
Andrew Rybchenko July 2, 2021, 1:39 p.m. UTC | #12
On 7/2/21 4:37 PM, David Marchand wrote:
> On Fri, Jul 2, 2021 at 10:43 AM Andrew Rybchenko
> <andrew.rybchenko@oktetlabs.ru> wrote:
>> I've send v4 with the problem fixed. However, I'm afraid
>> build test systems should be updated to have libatomic
>> correctly installed. Otherwise, they do not really check
>> net/sfc build.
> 
> CI systems must be updated if they check ABI.
> And in general, we want them to continue testing net/sfc.
> I sent a mail to ask for this.

Many thanks, David
  
Thomas Monjalon July 4, 2021, 7:45 p.m. UTC | #13
02/07/2021 14:53, Andrew Rybchenko:
> On 7/2/21 3:30 PM, Thomas Monjalon wrote:
> > 02/07/2021 10:43, Andrew Rybchenko:
> >> On 7/1/21 4:05 PM, Andrew Rybchenko wrote:
> >>> On 7/1/21 3:34 PM, David Marchand wrote:
> >>>> On Thu, Jul 1, 2021 at 11:22 AM Andrew Rybchenko
> >>>> <andrew.rybchenko@oktetlabs.ru> wrote:
> >>>>> The build works fine for me on FC34, but it has
> >>>>> libatomic-11.1.1-3.fc34.x86_64 installed.
> >>>>
> >>>> I first produced the issue on my "old" FC32.
> >>>> Afaics, for FC33 and later, gcc now depends on libatomic and the
> >>>> problem won't be noticed.
> >>>> FC32 and before are EOL, but I then reproduced the issue on RHEL 8
> >>>> (and Intel CI reported it on Centos 8 too).
> >>>
> >>> I see. Thanks for the clarification.
> >>>
> >>>>>
> >>>>> I'd like to understand what we're trying to solve here.
> >>>>> Are we trying to make meson to report the missing library
> >>>>> correctly?
> >>>>>
> >>>>> If so, I think I can do simple check using cc.links()
> >>>>> which will fail if the library is not found. I'll
> >>>>> test that it works as expected if the library is not
> >>>>> completely installed.
> >>>>>
> >>>>
> >>>> I tried below diff, and it works for me.
> >>>> "works" as in net/sfc gets disabled without libatomic installed:
> > [...]
> >>>>  # for gcc compiles we need -latomic for 128-bit atomic ops
> >>>>  if cc.get_id() == 'gcc'
> >>>> +    code = '''#include <stdio.h>
> >>>> +    void main() { printf("Atomilink me.\n"); }
> >>>> +    '''
> >>>> +    if not cc.links(code, args: '-latomic', name: 'libatomic link check')
> >>>> +        build = false
> >>>> +        reason = 'missing dependency, "libatomic"'
> >>>> +        subdir_done()
> >>>> +    endif
> >>>>      ext_deps += cc.find_library('atomic')
> >>>>  endif
> >>>
> >>> Many thanks, LGTM. I'll pick it up and add comments why
> >>> it is checked this way.
> >>>
> >>
> >> I've send v4 with the problem fixed. However, I'm afraid
> >> build test systems should be updated to have libatomic
> >> correctly installed. Otherwise, they do not really check
> >> net/sfc build.
> > 
> > When testing on old systems, sfc won't be tested anymore after this patchset.
> > On recent systems, sfc should be enabled I guess.
> > I don't see how to manage better, sorry.
> > 
> 
> I see. I thought that it is possible to install missing
> package on corresponding systems to make build coverage
> better.
> 
> Now I automatically test build on problematic distros
> with previously missing packages installed. So I have
> internal build coverage anyway.

David asked for installing libatomic:
https://inbox.dpdk.org/ci/CAJFAV8xCNBL4yEZU0c=dJGYS+13QM7Uz7e2qnUkMuM7eaKKw+Q@mail.gmail.com/

We should wait for it to be installed otherwise ABI check will fail.
  
Andrew Rybchenko July 5, 2021, 8:41 a.m. UTC | #14
On 7/4/21 10:45 PM, Thomas Monjalon wrote:
> 02/07/2021 14:53, Andrew Rybchenko:
>> On 7/2/21 3:30 PM, Thomas Monjalon wrote:
>>> 02/07/2021 10:43, Andrew Rybchenko:
>>>> On 7/1/21 4:05 PM, Andrew Rybchenko wrote:
>>>>> On 7/1/21 3:34 PM, David Marchand wrote:
>>>>>> On Thu, Jul 1, 2021 at 11:22 AM Andrew Rybchenko
>>>>>> <andrew.rybchenko@oktetlabs.ru> wrote:
>>>>>>> The build works fine for me on FC34, but it has
>>>>>>> libatomic-11.1.1-3.fc34.x86_64 installed.
>>>>>> I first produced the issue on my "old" FC32.
>>>>>> Afaics, for FC33 and later, gcc now depends on libatomic and the
>>>>>> problem won't be noticed.
>>>>>> FC32 and before are EOL, but I then reproduced the issue on RHEL 8
>>>>>> (and Intel CI reported it on Centos 8 too).
>>>>> I see. Thanks for the clarification.
>>>>>
>>>>>>> I'd like to understand what we're trying to solve here.
>>>>>>> Are we trying to make meson to report the missing library
>>>>>>> correctly?
>>>>>>>
>>>>>>> If so, I think I can do simple check using cc.links()
>>>>>>> which will fail if the library is not found. I'll
>>>>>>> test that it works as expected if the library is not
>>>>>>> completely installed.
>>>>>>>
>>>>>> I tried below diff, and it works for me.
>>>>>> "works" as in net/sfc gets disabled without libatomic installed:
>>> [...]
>>>>>>  # for gcc compiles we need -latomic for 128-bit atomic ops
>>>>>>  if cc.get_id() == 'gcc'
>>>>>> +    code = '''#include <stdio.h>
>>>>>> +    void main() { printf("Atomilink me.\n"); }
>>>>>> +    '''
>>>>>> +    if not cc.links(code, args: '-latomic', name: 'libatomic link check')
>>>>>> +        build = false
>>>>>> +        reason = 'missing dependency, "libatomic"'
>>>>>> +        subdir_done()
>>>>>> +    endif
>>>>>>      ext_deps += cc.find_library('atomic')
>>>>>>  endif
>>>>> Many thanks, LGTM. I'll pick it up and add comments why
>>>>> it is checked this way.
>>>>>
>>>> I've send v4 with the problem fixed. However, I'm afraid
>>>> build test systems should be updated to have libatomic
>>>> correctly installed. Otherwise, they do not really check
>>>> net/sfc build.
>>> When testing on old systems, sfc won't be tested anymore after this patchset.
>>> On recent systems, sfc should be enabled I guess.
>>> I don't see how to manage better, sorry.
>>>
>> I see. I thought that it is possible to install missing
>> package on corresponding systems to make build coverage
>> better.
>>
>> Now I automatically test build on problematic distros
>> with previously missing packages installed. So I have
>> internal build coverage anyway.
> David asked for installing libatomic:
> https://inbox.dpdk.org/ci/CAJFAV8xCNBL4yEZU0c=dJGYS+13QM7Uz7e2qnUkMuM7eaKKw+Q@mail.gmail.com/
>
> We should wait for it to be installed otherwise ABI check will fail.

Yes, I see. Thanks.
  

Patch

diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst
index cf1269cc03..bd08118da7 100644
--- a/doc/guides/nics/sfc_efx.rst
+++ b/doc/guides/nics/sfc_efx.rst
@@ -240,6 +240,8 @@  Supported actions (***transfer*** rules):
 
 - PORT_ID
 
+- COUNT
+
 - DROP
 
 Validating flow rules depends on the firmware variant.
diff --git a/doc/guides/rel_notes/release_21_08.rst b/doc/guides/rel_notes/release_21_08.rst
index a6ecfdf3ce..75688304da 100644
--- a/doc/guides/rel_notes/release_21_08.rst
+++ b/doc/guides/rel_notes/release_21_08.rst
@@ -55,6 +55,12 @@  New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Updated Solarflare network PMD.**
+
+  Updated the Solarflare ``sfc_efx`` driver with changes including:
+
+  * Added COUNT action support for SN1000 NICs
+
 
 Removed Items
 -------------
diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
index f8880f740a..32b58e3d76 100644
--- a/drivers/net/sfc/meson.build
+++ b/drivers/net/sfc/meson.build
@@ -39,6 +39,16 @@  foreach flag: extra_flags
     endif
 endforeach
 
+# for clang 32-bit compiles we need libatomic for 64-bit atomic ops
+if cc.get_id() == 'clang' and dpdk_conf.get('RTE_ARCH_64') == false
+    ext_deps += cc.find_library('atomic')
+endif
+
+# for gcc compiles we need -latomic for 128-bit atomic ops
+if cc.get_id() == 'gcc'
+    ext_deps += cc.find_library('atomic')
+endif
+
 deps += ['common_sfc_efx', 'bus_pci']
 sources = files(
         'sfc_ethdev.c',
diff --git a/drivers/net/sfc/sfc_flow.c b/drivers/net/sfc/sfc_flow.c
index 2db8af1759..1294dbd3a7 100644
--- a/drivers/net/sfc/sfc_flow.c
+++ b/drivers/net/sfc/sfc_flow.c
@@ -24,6 +24,7 @@ 
 #include "sfc_flow.h"
 #include "sfc_log.h"
 #include "sfc_dp_rx.h"
+#include "sfc_mae_counter.h"
 
 struct sfc_flow_ops_by_spec {
 	sfc_flow_parse_cb_t	*parse;
@@ -2854,6 +2855,12 @@  sfc_flow_stop(struct sfc_adapter *sa)
 		efx_rx_scale_context_free(sa->nic, rss->dummy_rss_context);
 		rss->dummy_rss_context = EFX_RSS_CONTEXT_DEFAULT;
 	}
+
+	/*
+	 * MAE counter service is not stopped on flow rule remove to avoid
+	 * extra work. Make sure that it is stopped here.
+	 */
+	sfc_mae_counter_stop(sa);
 }
 
 int
diff --git a/drivers/net/sfc/sfc_mae.c b/drivers/net/sfc/sfc_mae.c
index 8ffcf72d88..c3efd5b407 100644
--- a/drivers/net/sfc/sfc_mae.c
+++ b/drivers/net/sfc/sfc_mae.c
@@ -19,6 +19,7 @@ 
 #include "sfc_mae_counter.h"
 #include "sfc_log.h"
 #include "sfc_switch.h"
+#include "sfc_service.h"
 
 static int
 sfc_mae_assign_entity_mport(struct sfc_adapter *sa,
@@ -30,6 +31,19 @@  sfc_mae_assign_entity_mport(struct sfc_adapter *sa,
 					      mportp);
 }
 
+static int
+sfc_mae_counter_registry_init(struct sfc_mae_counter_registry *registry,
+			      uint32_t nb_counters_max)
+{
+	return sfc_mae_counters_init(&registry->counters, nb_counters_max);
+}
+
+static void
+sfc_mae_counter_registry_fini(struct sfc_mae_counter_registry *registry)
+{
+	sfc_mae_counters_fini(&registry->counters);
+}
+
 int
 sfc_mae_attach(struct sfc_adapter *sa)
 {
@@ -59,6 +73,15 @@  sfc_mae_attach(struct sfc_adapter *sa)
 	if (rc != 0)
 		goto fail_mae_get_limits;
 
+	sfc_log_init(sa, "init MAE counter registry");
+	rc = sfc_mae_counter_registry_init(&mae->counter_registry,
+					   limits.eml_max_n_counters);
+	if (rc != 0) {
+		sfc_err(sa, "failed to init MAE counters registry for %u entries: %s",
+			limits.eml_max_n_counters, rte_strerror(rc));
+		goto fail_counter_registry_init;
+	}
+
 	sfc_log_init(sa, "assign entity MPORT");
 	rc = sfc_mae_assign_entity_mport(sa, &entity_mport);
 	if (rc != 0)
@@ -107,6 +130,9 @@  sfc_mae_attach(struct sfc_adapter *sa)
 fail_mae_assign_switch_port:
 fail_mae_assign_switch_domain:
 fail_mae_assign_entity_mport:
+	sfc_mae_counter_registry_fini(&mae->counter_registry);
+
+fail_counter_registry_init:
 fail_mae_get_limits:
 	efx_mae_fini(sa->nic);
 
@@ -131,6 +157,7 @@  sfc_mae_detach(struct sfc_adapter *sa)
 		return;
 
 	rte_free(mae->bounce_eh.buf);
+	sfc_mae_counter_registry_fini(&mae->counter_registry);
 
 	efx_mae_fini(sa->nic);
 
@@ -480,9 +507,72 @@  sfc_mae_encap_header_disable(struct sfc_adapter *sa,
 	--(fw_rsrc->refcnt);
 }
 
+static int
+sfc_mae_counters_enable(struct sfc_adapter *sa,
+			struct sfc_mae_counter_id *counters,
+			unsigned int n_counters,
+			efx_mae_actions_t *action_set_spec)
+{
+	int rc;
+
+	sfc_log_init(sa, "entry");
+
+	if (n_counters == 0) {
+		sfc_log_init(sa, "no counters - skip");
+		return 0;
+	}
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+	SFC_ASSERT(n_counters == 1);
+
+	rc = sfc_mae_counter_enable(sa, &counters[0]);
+	if (rc != 0) {
+		sfc_err(sa, "failed to enable MAE counter %u: %s",
+			counters[0].mae_id.id, rte_strerror(rc));
+		goto fail_counter_add;
+	}
+
+	rc = efx_mae_action_set_fill_in_counter_id(action_set_spec,
+						   &counters[0].mae_id);
+	if (rc != 0) {
+		sfc_err(sa, "failed to fill in MAE counter %u in action set: %s",
+			counters[0].mae_id.id, rte_strerror(rc));
+		goto fail_fill_in_id;
+	}
+
+	return 0;
+
+fail_fill_in_id:
+	(void)sfc_mae_counter_disable(sa, &counters[0]);
+
+fail_counter_add:
+	sfc_log_init(sa, "failed: %s", rte_strerror(rc));
+	return rc;
+}
+
+static int
+sfc_mae_counters_disable(struct sfc_adapter *sa,
+			 struct sfc_mae_counter_id *counters,
+			 unsigned int n_counters)
+{
+	if (n_counters == 0)
+		return 0;
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+	SFC_ASSERT(n_counters == 1);
+
+	if (counters[0].mae_id.id == EFX_MAE_RSRC_ID_INVALID) {
+		sfc_err(sa, "failed to disable: already disabled");
+		return EALREADY;
+	}
+
+	return sfc_mae_counter_disable(sa, &counters[0]);
+}
+
 static struct sfc_mae_action_set *
 sfc_mae_action_set_attach(struct sfc_adapter *sa,
 			  const struct sfc_mae_encap_header *encap_header,
+			  unsigned int n_count,
 			  const efx_mae_actions_t *spec)
 {
 	struct sfc_mae_action_set *action_set;
@@ -491,7 +581,12 @@  sfc_mae_action_set_attach(struct sfc_adapter *sa,
 	SFC_ASSERT(sfc_adapter_is_locked(sa));
 
 	TAILQ_FOREACH(action_set, &mae->action_sets, entries) {
+		/*
+		 * Shared counters are not supported, hence action sets with
+		 * COUNT are not attachable.
+		 */
 		if (action_set->encap_header == encap_header &&
+		    n_count == 0 &&
 		    efx_mae_action_set_specs_equal(action_set->spec, spec)) {
 			sfc_dbg(sa, "attaching to action_set=%p", action_set);
 			++(action_set->refcnt);
@@ -504,18 +599,52 @@  sfc_mae_action_set_attach(struct sfc_adapter *sa,
 
 static int
 sfc_mae_action_set_add(struct sfc_adapter *sa,
+		       const struct rte_flow_action actions[],
 		       efx_mae_actions_t *spec,
 		       struct sfc_mae_encap_header *encap_header,
+		       unsigned int n_counters,
 		       struct sfc_mae_action_set **action_setp)
 {
 	struct sfc_mae_action_set *action_set;
 	struct sfc_mae *mae = &sa->mae;
+	unsigned int i;
 
 	SFC_ASSERT(sfc_adapter_is_locked(sa));
 
 	action_set = rte_zmalloc("sfc_mae_action_set", sizeof(*action_set), 0);
-	if (action_set == NULL)
+	if (action_set == NULL) {
+		sfc_err(sa, "failed to alloc action set");
 		return ENOMEM;
+	}
+
+	if (n_counters > 0) {
+		const struct rte_flow_action *action;
+
+		action_set->counters = rte_malloc("sfc_mae_counter_ids",
+			sizeof(action_set->counters[0]) * n_counters, 0);
+		if (action_set->counters == NULL) {
+			rte_free(action_set);
+			sfc_err(sa, "failed to alloc counters");
+			return ENOMEM;
+		}
+
+		for (action = actions, i = 0;
+		     action->type != RTE_FLOW_ACTION_TYPE_END && i < n_counters;
+		     ++action) {
+			const struct rte_flow_action_count *conf;
+
+			if (action->type != RTE_FLOW_ACTION_TYPE_COUNT)
+				continue;
+
+			conf = action->conf;
+
+			action_set->counters[i].mae_id.id =
+				EFX_MAE_RSRC_ID_INVALID;
+			action_set->counters[i].rte_id = conf->id;
+			i++;
+		}
+		action_set->n_counters = n_counters;
+	}
 
 	action_set->refcnt = 1;
 	action_set->spec = spec;
@@ -555,6 +684,12 @@  sfc_mae_action_set_del(struct sfc_adapter *sa,
 
 	efx_mae_action_set_spec_fini(sa->nic, action_set->spec);
 	sfc_mae_encap_header_del(sa, action_set->encap_header);
+	if (action_set->n_counters > 0) {
+		SFC_ASSERT(action_set->n_counters == 1);
+		SFC_ASSERT(action_set->counters[0].mae_id.id ==
+			   EFX_MAE_RSRC_ID_INVALID);
+		rte_free(action_set->counters);
+	}
 	TAILQ_REMOVE(&mae->action_sets, action_set, entries);
 	rte_free(action_set);
 
@@ -566,6 +701,7 @@  sfc_mae_action_set_enable(struct sfc_adapter *sa,
 			  struct sfc_mae_action_set *action_set)
 {
 	struct sfc_mae_encap_header *encap_header = action_set->encap_header;
+	struct sfc_mae_counter_id *counters = action_set->counters;
 	struct sfc_mae_fw_rsrc *fw_rsrc = &action_set->fw_rsrc;
 	int rc;
 
@@ -580,14 +716,26 @@  sfc_mae_action_set_enable(struct sfc_adapter *sa,
 		if (rc != 0)
 			return rc;
 
-		rc = efx_mae_action_set_alloc(sa->nic, action_set->spec,
-					      &fw_rsrc->aset_id);
+		rc = sfc_mae_counters_enable(sa, counters,
+					     action_set->n_counters,
+					     action_set->spec);
 		if (rc != 0) {
+			sfc_err(sa, "failed to enable %u MAE counters: %s",
+				action_set->n_counters, rte_strerror(rc));
+
 			sfc_mae_encap_header_disable(sa, encap_header);
+			return rc;
+		}
 
+		rc = efx_mae_action_set_alloc(sa->nic, action_set->spec,
+					      &fw_rsrc->aset_id);
+		if (rc != 0) {
 			sfc_err(sa, "failed to enable action_set=%p: %s",
 				action_set, strerror(rc));
 
+			(void)sfc_mae_counters_disable(sa, counters,
+						       action_set->n_counters);
+			sfc_mae_encap_header_disable(sa, encap_header);
 			return rc;
 		}
 
@@ -627,6 +775,13 @@  sfc_mae_action_set_disable(struct sfc_adapter *sa,
 		}
 		fw_rsrc->aset_id.id = EFX_MAE_RSRC_ID_INVALID;
 
+		rc = sfc_mae_counters_disable(sa, action_set->counters,
+					      action_set->n_counters);
+		if (rc != 0) {
+			sfc_err(sa, "failed to disable %u MAE counters: %s",
+				action_set->n_counters, rte_strerror(rc));
+		}
+
 		sfc_mae_encap_header_disable(sa, action_set->encap_header);
 	}
 
@@ -2508,6 +2663,48 @@  sfc_mae_rule_parse_action_mark(const struct rte_flow_action_mark *conf,
 	return efx_mae_action_set_populate_mark(spec, conf->id);
 }
 
+static int
+sfc_mae_rule_parse_action_count(struct sfc_adapter *sa,
+				const struct rte_flow_action_count *conf,
+				efx_mae_actions_t *spec)
+{
+	int rc;
+
+	if (conf->shared) {
+		rc = ENOTSUP;
+		goto fail_counter_shared;
+	}
+
+	if ((sa->counter_rxq.state & SFC_COUNTER_RXQ_INITIALIZED) == 0) {
+		sfc_err(sa,
+			"counter queue is not configured for COUNT action");
+		rc = EINVAL;
+		goto fail_counter_queue_uninit;
+	}
+
+	if (sfc_get_service_lcore(SOCKET_ID_ANY) == RTE_MAX_LCORE) {
+		rc = EINVAL;
+		goto fail_no_service_core;
+	}
+
+	rc = efx_mae_action_set_populate_count(spec);
+	if (rc != 0) {
+		sfc_err(sa,
+			"failed to populate counters in MAE action set: %s",
+			rte_strerror(rc));
+		goto fail_populate_count;
+	}
+
+	return 0;
+
+fail_populate_count:
+fail_no_service_core:
+fail_counter_queue_uninit:
+fail_counter_shared:
+
+	return rc;
+}
+
 static int
 sfc_mae_rule_parse_action_phy_port(struct sfc_adapter *sa,
 				   const struct rte_flow_action_phy_port *conf,
@@ -2623,6 +2820,11 @@  sfc_mae_rule_parse_action(struct sfc_adapter *sa,
 							   spec, error);
 		custom_error = B_TRUE;
 		break;
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_COUNT,
+				       bundle->actions_mask);
+		rc = sfc_mae_rule_parse_action_count(sa, action->conf, spec);
+		break;
 	case RTE_FLOW_ACTION_TYPE_FLAG:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_FLAG,
 				       bundle->actions_mask);
@@ -2708,6 +2910,7 @@  sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
 	const struct rte_flow_action *action;
 	struct sfc_mae *mae = &sa->mae;
 	efx_mae_actions_t *spec;
+	unsigned int n_count;
 	int rc;
 
 	rte_errno = 0;
@@ -2745,15 +2948,22 @@  sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
 	if (rc != 0)
 		goto fail_process_encap_header;
 
+	n_count = efx_mae_action_set_get_nb_count(spec);
+	if (n_count > 1) {
+		rc = ENOTSUP;
+		sfc_err(sa, "too many count actions requested: %u", n_count);
+		goto fail_nb_count;
+	}
+
 	spec_mae->action_set = sfc_mae_action_set_attach(sa, encap_header,
-							 spec);
+							 n_count, spec);
 	if (spec_mae->action_set != NULL) {
 		sfc_mae_encap_header_del(sa, encap_header);
 		efx_mae_action_set_spec_fini(sa->nic, spec);
 		return 0;
 	}
 
-	rc = sfc_mae_action_set_add(sa, spec, encap_header,
+	rc = sfc_mae_action_set_add(sa, actions, spec, encap_header, n_count,
 				    &spec_mae->action_set);
 	if (rc != 0)
 		goto fail_action_set_add;
@@ -2761,6 +2971,7 @@  sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
 	return 0;
 
 fail_action_set_add:
+fail_nb_count:
 	sfc_mae_encap_header_del(sa, encap_header);
 
 fail_process_encap_header:
@@ -2915,6 +3126,15 @@  sfc_mae_flow_insert(struct sfc_adapter *sa,
 	if (rc != 0)
 		goto fail_action_set_enable;
 
+	if (action_set->n_counters > 0) {
+		rc = sfc_mae_counter_start(sa);
+		if (rc != 0) {
+			sfc_err(sa, "failed to start MAE counters support: %s",
+				rte_strerror(rc));
+			goto fail_mae_counter_start;
+		}
+	}
+
 	rc = efx_mae_action_rule_insert(sa->nic, spec_mae->match_spec,
 					NULL, &fw_rsrc->aset_id,
 					&spec_mae->rule_id);
@@ -2927,6 +3147,7 @@  sfc_mae_flow_insert(struct sfc_adapter *sa,
 	return 0;
 
 fail_action_rule_insert:
+fail_mae_counter_start:
 	sfc_mae_action_set_disable(sa, action_set);
 
 fail_action_set_enable:
diff --git a/drivers/net/sfc/sfc_mae.h b/drivers/net/sfc/sfc_mae.h
index 9740e54e49..2cc4334890 100644
--- a/drivers/net/sfc/sfc_mae.h
+++ b/drivers/net/sfc/sfc_mae.h
@@ -16,6 +16,8 @@ 
 
 #include "efx.h"
 
+#include "sfc_stats.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -54,10 +56,20 @@  struct sfc_mae_encap_header {
 
 TAILQ_HEAD(sfc_mae_encap_headers, sfc_mae_encap_header);
 
+/* Counter ID */
+struct sfc_mae_counter_id {
+	/* ID of a counter in MAE */
+	efx_counter_t			mae_id;
+	/* ID of a counter in RTE */
+	uint32_t			rte_id;
+};
+
 /** Action set registry entry */
 struct sfc_mae_action_set {
 	TAILQ_ENTRY(sfc_mae_action_set)	entries;
 	unsigned int			refcnt;
+	struct sfc_mae_counter_id	*counters;
+	uint32_t			n_counters;
 	efx_mae_actions_t		*spec;
 	struct sfc_mae_encap_header	*encap_header;
 	struct sfc_mae_fw_rsrc		fw_rsrc;
@@ -83,6 +95,50 @@  struct sfc_mae_bounce_eh {
 	efx_tunnel_protocol_t		type;
 };
 
+/** Counter collection entry */
+struct sfc_mae_counter {
+	bool				inuse;
+	uint32_t			generation_count;
+	union sfc_pkts_bytes		value;
+	union sfc_pkts_bytes		reset;
+};
+
+struct sfc_mae_counters_xstats {
+	uint64_t			not_inuse_update;
+	uint64_t			realloc_update;
+};
+
+struct sfc_mae_counters {
+	/** An array of all MAE counters */
+	struct sfc_mae_counter		*mae_counters;
+	/** Extra statistics for counters */
+	struct sfc_mae_counters_xstats	xstats;
+	/** Count of all MAE counters */
+	unsigned int			n_mae_counters;
+};
+
+struct sfc_mae_counter_registry {
+	/* Common counter information */
+	/** Counters collection */
+	struct sfc_mae_counters		counters;
+
+	/* Information used by counter update service */
+	/** Callback to get packets from RxQ */
+	eth_rx_burst_t			rx_pkt_burst;
+	/** Data for the callback to get packets */
+	struct sfc_dp_rxq		*rx_dp;
+	/** Number of buffers pushed to the RxQ */
+	unsigned int			pushed_n_buffers;
+	/** Are credits used by counter stream */
+	bool				use_credits;
+
+	/* Information used by configuration routines */
+	/** Counter service core ID */
+	uint32_t			service_core_id;
+	/** Counter service ID */
+	uint32_t			service_id;
+};
+
 struct sfc_mae {
 	/** Assigned switch domain identifier */
 	uint16_t			switch_domain_id;
@@ -104,6 +160,10 @@  struct sfc_mae {
 	struct sfc_mae_action_sets	action_sets;
 	/** Encap. header bounce buffer */
 	struct sfc_mae_bounce_eh	bounce_eh;
+	/** Flag indicating whether counter-only RxQ is running */
+	bool				counter_rxq_running;
+	/** Counter registry */
+	struct sfc_mae_counter_registry	counter_registry;
 };
 
 struct sfc_adapter;
diff --git a/drivers/net/sfc/sfc_mae_counter.c b/drivers/net/sfc/sfc_mae_counter.c
index c7646cf7b1..b0cb8157aa 100644
--- a/drivers/net/sfc/sfc_mae_counter.c
+++ b/drivers/net/sfc/sfc_mae_counter.c
@@ -4,8 +4,10 @@ 
  */
 
 #include <rte_common.h>
+#include <rte_service_component.h>
 
 #include "efx.h"
+#include "efx_regs_counters_pkt_format.h"
 
 #include "sfc_ev.h"
 #include "sfc.h"
@@ -49,6 +51,520 @@  sfc_mae_counter_rxq_required(struct sfc_adapter *sa)
 	return true;
 }
 
+int
+sfc_mae_counter_enable(struct sfc_adapter *sa,
+		       struct sfc_mae_counter_id *counterp)
+{
+	struct sfc_mae_counter_registry *reg = &sa->mae.counter_registry;
+	struct sfc_mae_counters *counters = &reg->counters;
+	struct sfc_mae_counter *p;
+	efx_counter_t mae_counter;
+	uint32_t generation_count;
+	uint32_t unused;
+	int rc;
+
+	/*
+	 * The actual count of counters allocated is ignored since a failure
+	 * to allocate a single counter is indicated by non-zero return code.
+	 */
+	rc = efx_mae_counters_alloc(sa->nic, 1, &unused, &mae_counter,
+				    &generation_count);
+	if (rc != 0) {
+		sfc_err(sa, "failed to alloc MAE counter: %s",
+			rte_strerror(rc));
+		goto fail_mae_counter_alloc;
+	}
+
+	if (mae_counter.id >= counters->n_mae_counters) {
+		/*
+		 * ID of a counter is expected to be within the range
+		 * between 0 and the maximum count of counters to always
+		 * fit into a pre-allocated array size of maximum counter ID.
+		 */
+		sfc_err(sa, "MAE counter ID is out of expected range");
+		rc = EFAULT;
+		goto fail_counter_id_range;
+	}
+
+	counterp->mae_id = mae_counter;
+
+	p = &counters->mae_counters[mae_counter.id];
+
+	/*
+	 * Ordering is relaxed since it is the only operation on counter value.
+	 * And it does not depend on different stores/loads in other threads.
+	 * Paired with relaxed ordering in counter increment.
+	 */
+	__atomic_store(&p->reset.pkts_bytes.int128,
+		       &p->value.pkts_bytes.int128, __ATOMIC_RELAXED);
+	p->generation_count = generation_count;
+
+	/*
+	 * The flag is set at the very end of add operation and reset
+	 * at the beginning of delete operation. Release ordering is
+	 * paired with acquire ordering on load in counter increment operation.
+	 */
+	__atomic_store_n(&p->inuse, true, __ATOMIC_RELEASE);
+
+	sfc_info(sa, "enabled MAE counter #%u with reset pkts=%" PRIu64
+		 " bytes=%" PRIu64, mae_counter.id,
+		 p->reset.pkts, p->reset.bytes);
+
+	return 0;
+
+fail_counter_id_range:
+	(void)efx_mae_counters_free(sa->nic, 1, &unused, &mae_counter, NULL);
+
+fail_mae_counter_alloc:
+	sfc_log_init(sa, "failed: %s", rte_strerror(rc));
+	return rc;
+}
+
+int
+sfc_mae_counter_disable(struct sfc_adapter *sa,
+			struct sfc_mae_counter_id *counter)
+{
+	struct sfc_mae_counter_registry *reg = &sa->mae.counter_registry;
+	struct sfc_mae_counters *counters = &reg->counters;
+	struct sfc_mae_counter *p;
+	uint32_t unused;
+	int rc;
+
+	if (counter->mae_id.id == EFX_MAE_RSRC_ID_INVALID)
+		return 0;
+
+	SFC_ASSERT(counter->mae_id.id < counters->n_mae_counters);
+	/*
+	 * The flag is set at the very end of add operation and reset
+	 * at the beginning of delete operation. Release ordering is
+	 * paired with acquire ordering on load in counter increment operation.
+	 */
+	p = &counters->mae_counters[counter->mae_id.id];
+	__atomic_store_n(&p->inuse, false, __ATOMIC_RELEASE);
+
+	rc = efx_mae_counters_free(sa->nic, 1, &unused, &counter->mae_id, NULL);
+	if (rc != 0)
+		sfc_err(sa, "failed to free MAE counter %u: %s",
+			counter->mae_id.id, rte_strerror(rc));
+
+	sfc_info(sa, "disabled MAE counter #%u with reset pkts=%" PRIu64
+		 " bytes=%" PRIu64, counter->mae_id.id,
+		 p->reset.pkts, p->reset.bytes);
+
+	/*
+	 * Do this regardless of what efx_mae_counters_free() return value is.
+	 * If there's some error, the resulting resource leakage is bad, but
+	 * nothing sensible can be done in this case.
+	 */
+	counter->mae_id.id = EFX_MAE_RSRC_ID_INVALID;
+
+	return rc;
+}
+
+static void
+sfc_mae_counter_increment(struct sfc_adapter *sa,
+			  struct sfc_mae_counters *counters,
+			  uint32_t mae_counter_id,
+			  uint32_t generation_count,
+			  uint64_t pkts, uint64_t bytes)
+{
+	struct sfc_mae_counter *p = &counters->mae_counters[mae_counter_id];
+	struct sfc_mae_counters_xstats *xstats = &counters->xstats;
+	union sfc_pkts_bytes cnt_val;
+	bool inuse;
+
+	/*
+	 * Acquire ordering is paired with release ordering in counter add
+	 * and delete operations.
+	 */
+	__atomic_load(&p->inuse, &inuse, __ATOMIC_ACQUIRE);
+	if (!inuse) {
+		/*
+		 * Two possible cases include:
+		 * 1) Counter is just allocated. Too early counter update
+		 *    cannot be processed properly.
+		 * 2) Stale update of freed and not reallocated counter.
+		 *    There is no point in processing that update.
+		 */
+		xstats->not_inuse_update++;
+		return;
+	}
+
+	if (unlikely(generation_count < p->generation_count)) {
+		/*
+		 * It is a stale update for the reallocated counter
+		 * (i.e., freed and the same ID allocated again).
+		 */
+		xstats->realloc_update++;
+		return;
+	}
+
+	cnt_val.pkts = p->value.pkts + pkts;
+	cnt_val.bytes = p->value.bytes + bytes;
+
+	/*
+	 * Ordering is relaxed since it is the only operation on counter value.
+	 * And it does not depend on different stores/loads in other threads.
+	 * Paired with relaxed ordering on counter reset.
+	 */
+	__atomic_store(&p->value.pkts_bytes,
+		       &cnt_val.pkts_bytes, __ATOMIC_RELAXED);
+
+	sfc_info(sa, "update MAE counter #%u: pkts+%" PRIu64 "=%" PRIu64
+		 ", bytes+%" PRIu64 "=%" PRIu64, mae_counter_id,
+		 pkts, cnt_val.pkts, bytes, cnt_val.bytes);
+}
+
+static void
+sfc_mae_parse_counter_packet(struct sfc_adapter *sa,
+			     struct sfc_mae_counter_registry *counter_registry,
+			     const struct rte_mbuf *m)
+{
+	uint32_t generation_count;
+	const efx_xword_t *hdr;
+	const efx_oword_t *counters_data;
+	unsigned int version;
+	unsigned int id;
+	unsigned int header_offset;
+	unsigned int payload_offset;
+	unsigned int counter_count;
+	unsigned int required_len;
+	unsigned int i;
+
+	if (unlikely(m->nb_segs != 1)) {
+		sfc_err(sa, "unexpectedly scattered MAE counters packet (%u segments)",
+			m->nb_segs);
+		return;
+	}
+
+	if (unlikely(m->data_len < ER_RX_SL_PACKETISER_HEADER_WORD_SIZE)) {
+		sfc_err(sa, "too short MAE counters packet (%u bytes)",
+			m->data_len);
+		return;
+	}
+
+	/*
+	 * The generation count is located in the Rx prefix in the USER_MARK
+	 * field which is written into hash.fdir.hi field of an mbuf. See
+	 * SF-123581-TC SmartNIC Datapath Offloads section 4.7.5 Counters.
+	 */
+	generation_count = m->hash.fdir.hi;
+
+	hdr = rte_pktmbuf_mtod(m, const efx_xword_t *);
+
+	version = EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_VERSION);
+	if (unlikely(version != ERF_SC_PACKETISER_HEADER_VERSION_2)) {
+		sfc_err(sa, "unexpected MAE counters packet version %u",
+			version);
+		return;
+	}
+
+	id = EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_IDENTIFIER);
+	if (unlikely(id != ERF_SC_PACKETISER_HEADER_IDENTIFIER_AR)) {
+		sfc_err(sa, "unexpected MAE counters source identifier %u", id);
+		return;
+	}
+
+	/* Packet layout definitions assume fixed header offset in fact */
+	header_offset =
+		EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_HEADER_OFFSET);
+	if (unlikely(header_offset !=
+		     ERF_SC_PACKETISER_HEADER_HEADER_OFFSET_DEFAULT)) {
+		sfc_err(sa, "unexpected MAE counters packet header offset %u",
+			header_offset);
+		return;
+	}
+
+	payload_offset =
+		EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_PAYLOAD_OFFSET);
+
+	counter_count = EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_COUNT);
+
+	required_len = payload_offset +
+			counter_count * sizeof(counters_data[0]);
+	if (unlikely(required_len > m->data_len)) {
+		sfc_err(sa, "truncated MAE counters packet: %u counters, packet length is %u vs %u required",
+			counter_count, m->data_len, required_len);
+		/*
+		 * In theory it is possible process available counters data,
+		 * but such condition is really unexpected and it is
+		 * better to treat entire packet as corrupted.
+		 */
+		return;
+	}
+
+	/* Ensure that counters data is 32-bit aligned */
+	if (unlikely(payload_offset % sizeof(uint32_t) != 0)) {
+		sfc_err(sa, "unsupported MAE counters payload offset %u, must be 32-bit aligned",
+			payload_offset);
+		return;
+	}
+	RTE_BUILD_BUG_ON(sizeof(counters_data[0]) !=
+			ER_RX_SL_PACKETISER_PAYLOAD_WORD_SIZE);
+
+	counters_data =
+		rte_pktmbuf_mtod_offset(m, const efx_oword_t *, payload_offset);
+
+	sfc_info(sa, "update %u MAE counters with gc=%u",
+		 counter_count, generation_count);
+
+	for (i = 0; i < counter_count; ++i) {
+		uint32_t packet_count_lo;
+		uint32_t packet_count_hi;
+		uint32_t byte_count_lo;
+		uint32_t byte_count_hi;
+
+		/*
+		 * Use 32-bit field accessors below since counters data
+		 * is not 64-bit aligned.
+		 * 32-bit alignment is checked above taking into account
+		 * that start of packet data is 32-bit aligned
+		 * (cache-line size aligned in fact).
+		 */
+		packet_count_lo =
+			EFX_OWORD_FIELD32(counters_data[i],
+				ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_LO);
+		packet_count_hi =
+			EFX_OWORD_FIELD32(counters_data[i],
+				ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_HI);
+		byte_count_lo =
+			EFX_OWORD_FIELD32(counters_data[i],
+				ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_LO);
+		byte_count_hi =
+			EFX_OWORD_FIELD32(counters_data[i],
+				ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_HI);
+		sfc_mae_counter_increment(sa,
+			&counter_registry->counters,
+			EFX_OWORD_FIELD32(counters_data[i],
+				ERF_SC_PACKETISER_PAYLOAD_COUNTER_INDEX),
+			generation_count,
+			(uint64_t)packet_count_lo |
+			((uint64_t)packet_count_hi <<
+			 ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_LO_WIDTH),
+			(uint64_t)byte_count_lo |
+			((uint64_t)byte_count_hi <<
+			 ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_LO_WIDTH));
+	}
+}
+
+static int32_t
+sfc_mae_counter_routine(void *arg)
+{
+	struct sfc_adapter *sa = arg;
+	struct sfc_mae_counter_registry *counter_registry =
+		&sa->mae.counter_registry;
+	struct rte_mbuf *mbufs[SFC_MAE_COUNTER_RX_BURST];
+	unsigned int pushed_diff;
+	unsigned int pushed;
+	unsigned int i;
+	uint16_t n;
+	int rc;
+
+	n = counter_registry->rx_pkt_burst(counter_registry->rx_dp, mbufs,
+					   SFC_MAE_COUNTER_RX_BURST);
+
+	for (i = 0; i < n; i++)
+		sfc_mae_parse_counter_packet(sa, counter_registry, mbufs[i]);
+
+	rte_pktmbuf_free_bulk(mbufs, n);
+
+	if (!counter_registry->use_credits)
+		return 0;
+
+	pushed = sfc_rx_get_pushed(sa, counter_registry->rx_dp);
+	pushed_diff = pushed - counter_registry->pushed_n_buffers;
+
+	if (pushed_diff >= SFC_COUNTER_RXQ_REFILL_LEVEL) {
+		rc = efx_mae_counters_stream_give_credits(sa->nic, pushed_diff);
+		if (rc == 0) {
+			counter_registry->pushed_n_buffers = pushed;
+		} else {
+			/*
+			 * FIXME: counters might be important for the
+			 * application. Handle the error in order to recover
+			 * from the failure
+			 */
+			SFC_GENERIC_LOG(DEBUG, "Give credits failed: %s",
+					rte_strerror(rc));
+		}
+	}
+
+	return 0;
+}
+
+static void
+sfc_mae_counter_service_unregister(struct sfc_adapter *sa)
+{
+	struct sfc_mae_counter_registry *registry =
+		&sa->mae.counter_registry;
+	const unsigned int wait_ms = 10000;
+	unsigned int i;
+
+	rte_service_runstate_set(registry->service_id, 0);
+	rte_service_component_runstate_set(registry->service_id, 0);
+
+	/*
+	 * Wait for the counter routine to finish the last iteration.
+	 * Give up on timeout.
+	 */
+	for (i = 0; i < wait_ms; i++) {
+		if (rte_service_may_be_active(registry->service_id) == 0)
+			break;
+
+		rte_delay_ms(1);
+	}
+	if (i == wait_ms)
+		sfc_warn(sa, "failed to wait for counter service to stop");
+
+	rte_service_map_lcore_set(registry->service_id,
+				  registry->service_core_id, 0);
+
+	rte_service_component_unregister(registry->service_id);
+}
+
+static struct sfc_rxq_info *
+sfc_counter_rxq_info_get(struct sfc_adapter *sa)
+{
+	return &sfc_sa2shared(sa)->rxq_info[sa->counter_rxq.sw_index];
+}
+
+static int
+sfc_mae_counter_service_register(struct sfc_adapter *sa,
+				 uint32_t counter_stream_flags)
+{
+	struct rte_service_spec service;
+	char counter_service_name[sizeof(service.name)] = "counter_service";
+	struct sfc_mae_counter_registry *counter_registry =
+		&sa->mae.counter_registry;
+	uint32_t cid;
+	uint32_t sid;
+	int rc;
+
+	sfc_log_init(sa, "entry");
+
+	/* Prepare service info */
+	memset(&service, 0, sizeof(service));
+	rte_strscpy(service.name, counter_service_name, sizeof(service.name));
+	service.socket_id = sa->socket_id;
+	service.callback = sfc_mae_counter_routine;
+	service.callback_userdata = sa;
+	counter_registry->rx_pkt_burst = sa->eth_dev->rx_pkt_burst;
+	counter_registry->rx_dp = sfc_counter_rxq_info_get(sa)->dp;
+	counter_registry->pushed_n_buffers = 0;
+	counter_registry->use_credits = counter_stream_flags &
+		EFX_MAE_COUNTERS_STREAM_OUT_USES_CREDITS;
+
+	cid = sfc_get_service_lcore(sa->socket_id);
+	if (cid == RTE_MAX_LCORE && sa->socket_id != SOCKET_ID_ANY) {
+		/* Warn and try to allocate on any NUMA node */
+		sfc_warn(sa,
+			"failed to get service lcore for counter service at socket %d",
+			sa->socket_id);
+
+		cid = sfc_get_service_lcore(SOCKET_ID_ANY);
+	}
+	if (cid == RTE_MAX_LCORE) {
+		rc = ENOTSUP;
+		sfc_err(sa, "failed to get service lcore for counter service");
+		goto fail_get_service_lcore;
+	}
+
+	/* Service core may be in "stopped" state, start it */
+	rc = rte_service_lcore_start(cid);
+	if (rc != 0 && rc != -EALREADY) {
+		sfc_err(sa, "failed to start service core for counter service: %s",
+			rte_strerror(-rc));
+		rc = ENOTSUP;
+		goto fail_start_core;
+	}
+
+	/* Register counter service */
+	rc = rte_service_component_register(&service, &sid);
+	if (rc != 0) {
+		rc = ENOEXEC;
+		sfc_err(sa, "failed to register counter service component");
+		goto fail_register;
+	}
+
+	/* Map the service with the service core */
+	rc = rte_service_map_lcore_set(sid, cid, 1);
+	if (rc != 0) {
+		rc = -rc;
+		sfc_err(sa, "failed to map lcore for counter service: %s",
+			rte_strerror(rc));
+		goto fail_map_lcore;
+	}
+
+	/* Run the service */
+	rc = rte_service_component_runstate_set(sid, 1);
+	if (rc < 0) {
+		rc = -rc;
+		sfc_err(sa, "failed to run counter service component: %s",
+			rte_strerror(rc));
+		goto fail_component_runstate_set;
+	}
+	rc = rte_service_runstate_set(sid, 1);
+	if (rc < 0) {
+		rc = -rc;
+		sfc_err(sa, "failed to run counter service");
+		goto fail_runstate_set;
+	}
+
+	counter_registry->service_core_id = cid;
+	counter_registry->service_id = sid;
+
+	sfc_log_init(sa, "done");
+
+	return 0;
+
+fail_runstate_set:
+	rte_service_component_runstate_set(sid, 0);
+
+fail_component_runstate_set:
+	rte_service_map_lcore_set(sid, cid, 0);
+
+fail_map_lcore:
+	rte_service_component_unregister(sid);
+
+fail_register:
+fail_start_core:
+fail_get_service_lcore:
+	sfc_log_init(sa, "failed: %s", rte_strerror(rc));
+
+	return rc;
+}
+
+int
+sfc_mae_counters_init(struct sfc_mae_counters *counters,
+		      uint32_t nb_counters_max)
+{
+	int rc;
+
+	SFC_GENERIC_LOG(DEBUG, "%s: entry", __func__);
+
+	counters->mae_counters = rte_zmalloc("sfc_mae_counters",
+		sizeof(*counters->mae_counters) * nb_counters_max, 0);
+	if (counters->mae_counters == NULL) {
+		rc = ENOMEM;
+		SFC_GENERIC_LOG(ERR, "%s: failed: %s", __func__,
+				rte_strerror(rc));
+		return rc;
+	}
+
+	counters->n_mae_counters = nb_counters_max;
+
+	SFC_GENERIC_LOG(DEBUG, "%s: done", __func__);
+
+	return 0;
+}
+
+void
+sfc_mae_counters_fini(struct sfc_mae_counters *counters)
+{
+	rte_free(counters->mae_counters);
+	counters->mae_counters = NULL;
+}
+
 int
 sfc_mae_counter_rxq_attach(struct sfc_adapter *sa)
 {
@@ -215,3 +731,65 @@  sfc_mae_counter_rxq_fini(struct sfc_adapter *sa)
 
 	sfc_log_init(sa, "done");
 }
+
+void
+sfc_mae_counter_stop(struct sfc_adapter *sa)
+{
+	struct sfc_mae *mae = &sa->mae;
+
+	sfc_log_init(sa, "entry");
+
+	if (!mae->counter_rxq_running) {
+		sfc_log_init(sa, "counter queue is not running - skip");
+		return;
+	}
+
+	sfc_mae_counter_service_unregister(sa);
+	efx_mae_counters_stream_stop(sa->nic, sa->counter_rxq.sw_index, NULL);
+
+	mae->counter_rxq_running = false;
+
+	sfc_log_init(sa, "done");
+}
+
+int
+sfc_mae_counter_start(struct sfc_adapter *sa)
+{
+	struct sfc_mae *mae = &sa->mae;
+	uint32_t flags;
+	int rc;
+
+	SFC_ASSERT(sa->counter_rxq.state & SFC_COUNTER_RXQ_ATTACHED);
+
+	if (mae->counter_rxq_running)
+		return 0;
+
+	sfc_log_init(sa, "entry");
+
+	rc = efx_mae_counters_stream_start(sa->nic, sa->counter_rxq.sw_index,
+					   SFC_MAE_COUNTER_STREAM_PACKET_SIZE,
+					   0 /* No flags required */, &flags);
+	if (rc != 0) {
+		sfc_err(sa, "failed to start MAE counters stream: %s",
+			rte_strerror(rc));
+		goto fail_counter_stream;
+	}
+
+	sfc_log_init(sa, "stream start flags: 0x%x", flags);
+
+	rc = sfc_mae_counter_service_register(sa, flags);
+	if (rc != 0)
+		goto fail_service_register;
+
+	mae->counter_rxq_running = true;
+
+	return 0;
+
+fail_service_register:
+	efx_mae_counters_stream_stop(sa->nic, sa->counter_rxq.sw_index, NULL);
+
+fail_counter_stream:
+	sfc_log_init(sa, "failed: %s", rte_strerror(rc));
+
+	return rc;
+}
diff --git a/drivers/net/sfc/sfc_mae_counter.h b/drivers/net/sfc/sfc_mae_counter.h
index f16d64a999..f61a6b59cb 100644
--- a/drivers/net/sfc/sfc_mae_counter.h
+++ b/drivers/net/sfc/sfc_mae_counter.h
@@ -38,6 +38,17 @@  void sfc_mae_counter_rxq_detach(struct sfc_adapter *sa);
 int sfc_mae_counter_rxq_init(struct sfc_adapter *sa);
 void sfc_mae_counter_rxq_fini(struct sfc_adapter *sa);
 
+int sfc_mae_counters_init(struct sfc_mae_counters *counters,
+			  uint32_t nb_counters_max);
+void sfc_mae_counters_fini(struct sfc_mae_counters *counters);
+int sfc_mae_counter_enable(struct sfc_adapter *sa,
+			   struct sfc_mae_counter_id *counterp);
+int sfc_mae_counter_disable(struct sfc_adapter *sa,
+			    struct sfc_mae_counter_id *counter);
+
+int sfc_mae_counter_start(struct sfc_adapter *sa);
+void sfc_mae_counter_stop(struct sfc_adapter *sa);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/drivers/net/sfc/sfc_stats.h b/drivers/net/sfc/sfc_stats.h
new file mode 100644
index 0000000000..2d7ab71f14
--- /dev/null
+++ b/drivers/net/sfc/sfc_stats.h
@@ -0,0 +1,80 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright(c) 2019-2021 Xilinx, Inc.
+ * Copyright(c) 2019 Solarflare Communications Inc.
+ *
+ * This software was jointly developed between OKTET Labs (under contract
+ * for Solarflare) and Solarflare Communications, Inc.
+ */
+
+#ifndef _SFC_STATS_H
+#define _SFC_STATS_H
+
+#include <stdint.h>
+
+#include <rte_atomic.h>
+
+#include "sfc_tweak.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * 64-bit packets and bytes counters covered by 128-bit integer
+ * in order to do atomic updates to guarantee consistency if
+ * required.
+ */
+union sfc_pkts_bytes {
+	RTE_STD_C11
+	struct {
+		uint64_t		pkts;
+		uint64_t		bytes;
+	};
+	rte_int128_t			pkts_bytes;
+};
+
+/**
+ * Update packets and bytes counters atomically in assumption that
+ * the counter is written on one core only.
+ */
+static inline void
+sfc_pkts_bytes_add(union sfc_pkts_bytes *st, uint64_t pkts, uint64_t bytes)
+{
+#if SFC_SW_STATS_ATOMIC
+	union sfc_pkts_bytes result;
+
+	/* Stats are written on single core only, so just load values */
+	result.pkts = st->pkts + pkts;
+	result.bytes = st->bytes + bytes;
+
+	/*
+	 * Store the result atomically to guarantee that the reader
+	 * core sees both counter updates together.
+	 */
+	__atomic_store_n(&st->pkts_bytes.int128, result.pkts_bytes.int128,
+			 __ATOMIC_RELEASE);
+#else
+	st->pkts += pkts;
+	st->bytes += bytes;
+#endif
+}
+
+/**
+ * Get an atomic copy of a packets and bytes counters.
+ */
+static inline void
+sfc_pkts_bytes_get(const union sfc_pkts_bytes *st, union sfc_pkts_bytes *result)
+{
+#if SFC_SW_STATS_ATOMIC
+	result->pkts_bytes.int128 = __atomic_load_n(&st->pkts_bytes.int128,
+						    __ATOMIC_ACQUIRE);
+#else
+	*result = *st;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SFC_STATS_H */
diff --git a/drivers/net/sfc/sfc_tweak.h b/drivers/net/sfc/sfc_tweak.h
index f2d8701421..d09c7a3125 100644
--- a/drivers/net/sfc/sfc_tweak.h
+++ b/drivers/net/sfc/sfc_tweak.h
@@ -42,4 +42,13 @@ 
  */
 #define SFC_RXD_WAIT_TIMEOUT_NS_DEF	(200U * 1000)
 
+/**
+ * Ideally reading packet and byte counters together should return
+ * consistent values. I.e. a number of bytes corresponds to a number of
+ * packets. Since counters are updated in one thread and queried in
+ * another it requires either locking or atomics which are very
+ * expensive from performance point of view. So, disable it by default.
+ */
+#define SFC_SW_STATS_ATOMIC		0
+
 #endif /* _SFC_TWEAK_H_ */