* RE: [net-next,v3,04/13] net: renesas: rswitch: add register definitions for vlan support
From: Michael Dege @ 2026-04-09 13:43 UTC (permalink / raw)
To: Paolo Abeni
Cc: paul@pbarker.dev, niklas.soderlund, kuba@kernel.org,
Yoshihiro Shimoda, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, edumazet@google.com,
linux-renesas-soc@vger.kernel.org, andrew+netdev@lunn.ch,
davem@davemloft.net
In-Reply-To: <20260402125919.234448-1-pabeni@redhat.com>
Hello Paolo,
Thank you for the review comments.
> -----Original Message-----
> From: Paolo Abeni <pabeni@redhat.com>
> Sent: Thursday, April 2, 2026 2:59 PM
> To: Michael Dege <michael.dege@renesas.com>
> Cc: paul@pbarker.dev; niklas.soderlund <niklas.soderlund@ragnatech.se>; kuba@kernel.org; Yoshihiro
> Shimoda <yoshihiro.shimoda.uh@renesas.com>; netdev@vger.kernel.org; linux-kernel@vger.kernel.org;
> edumazet@google.com; pabeni@redhat.com; linux-renesas-soc@vger.kernel.org; andrew+netdev@lunn.ch;
> davem@davemloft.net
> Subject: Re: [net-next,v3,04/13] net: renesas: rswitch: add register definitions for vlan support
>
> This is an AI-generated review of your patch. The human sending this email has considered the AI
> review valid, or at least plausible.
>
> For local reproduction steps see
> https://netdev-ai.bots.linux.dev/ai-
> local.html&data=05%7C02%7Cmichael.dege%40renesas.com%7Caf9485ccd5524c5f84e908de90b7afa0%7C53d82571da19
> 47e49cb4625a166a4a2a%7C0%7C0%7C639107315768063102%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYi
> OiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=K9VAYytiJjTcdyrv98
> jVvkUrYLDTdRFL3HPzVLwEm1s%3D&reserved=0
> ---
> net: renesas: rswitch: add register definitions for vlan support
>
> This commit adds missing register and bit definitions for vlan support in the Renesas R-Switch driver.
>
> > diff --git a/drivers/net/ethernet/renesas/rswitch.h
> > b/drivers/net/ethernet/renesas/rswitch.h
> > index 340524d995ac..15d3fa48b0e3 100644
> > --- a/drivers/net/ethernet/renesas/rswitch.h
> > +++ b/drivers/net/ethernet/renesas/rswitch.h
>
> [ ... ]
>
> > @@ -847,6 +903,98 @@ enum rswitch_gwca_mode {
> > #define FWMACAGC_MACAGOG BIT(28)
> > #define FWMACAGC_MACDESOG BIT(29)
> >
> > +//FWMACTL0
>
> [ ... ]
>
> > +// FWVLANTEM
> > +#define VLANTUEN GENMASK(28, 16)
> > +#define VLANTEN GENAMSK(12, 0)
> ^^^^^^^^
>
> Is GENAMSK a typo? The correct macro is GENMASK, as used on the line immediately above for VLANTUEN.
> GENAMSK is not defined in the kernel and will cause compilation errors if VLANTEN is used.
Yes, this was a typo. Will be fixed in the next version
Best regards,
Michael
>
> > +
> > +// FWVLANTS
>
> [ ... ]
^ permalink raw reply
* Re: [PATCH net-next v11 03/14] net: Add lease info to queue-get response
From: Daniel Borkmann @ 2026-04-09 13:43 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, bpf, davem, razor, pabeni, willemb, sdf, john.fastabend,
martin.lau, jordan, maciej.fijalkowski, magnus.karlsson, dw, toke,
yangzhenze, wangdongdong.6
In-Reply-To: <20260408151251.72bd2482@kernel.org>
On 4/9/26 12:12 AM, Jakub Kicinski wrote:
> On Wed, 8 Apr 2026 11:09:34 +0200 Daniel Borkmann wrote:
>>>> +void netif_put_rx_queue_lease_locked(struct net_device *orig_dev,
>>>> + struct net_device *dev)
>>>> +{
>>>> + if (orig_dev != dev)
>>>> + netdev_unlock(dev);
>>>> +}
>>>
>>> Pretty sure I already complained about these ugly helpers.
>>> I'll try to find the time tomorrow to come up with something better.
>>
>> Ok, sounds good. Happy to adapt if you find something better and then I'll
>> work this into the series, and also integrate the things mentioned in my
>> cover letter reply (netkit nl dump + additional tests).
>
> Hi! How would you feel about something like the following on top?
>
> --->8----------
>
> net: remove the netif_get_rx_queue_lease_locked() helpers
>
> The netif_get_rx_queue_lease_locked() API hides the locking
> and the descend onto the leased queue. Making the code
> harder to follow (at least to me). Remove the API and open
> code the descend a bit. Most of the code now looks like:
>
> if (!leased)
> return __helper(x);
>
> hw_rxq = ..
> netdev_lock(hw_rxq->dev);
> ret = __helper(x);
> netdev_unlock(hw_rxq->dev);
>
> return ret;
>
> Of course if we have more code paths that need the wrapping
> we may need to revisit. For now, IMHO, having to know what
> netif_get_rx_queue_lease_locked() does is not worth the 20LoC
> it saves.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Thanks for looking into it! That looks good to me. I've also retested that
it still works.
Maybe small nits could be below to move the netif_is_queue_leasee into the
netdev_rx_queue.h header since its used outside of core and it might be
worth to also have the lock assertion in netdev_queue_get_dma_dev.
Do you want me to add your patch on top for a v12 of the series?
Thanks,
Daniel
include/net/netdev_rx_queue.h | 2 ++
net/core/dev.h | 1 -
net/core/netdev_queues.c | 2 ++
net/xdp/xsk.c | 2 --
4 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/include/net/netdev_rx_queue.h b/include/net/netdev_rx_queue.h
index 9415a94d333d..e9f7f098609b 100644
--- a/include/net/netdev_rx_queue.h
+++ b/include/net/netdev_rx_queue.h
@@ -76,6 +76,8 @@ struct netdev_rx_queue *
__netif_get_rx_queue_lease(struct net_device **dev, unsigned int *rxq,
enum netif_lease_dir dir);
+bool netif_is_queue_leasee(const struct net_device *dev);
+
int netdev_rx_queue_restart(struct net_device *dev, unsigned int rxq);
void netdev_rx_queue_lease(struct netdev_rx_queue *rxq_dst,
struct netdev_rx_queue *rxq_src);
diff --git a/net/core/dev.h b/net/core/dev.h
index 376bac4a82da..95edb2d4eff8 100644
--- a/net/core/dev.h
+++ b/net/core/dev.h
@@ -101,7 +101,6 @@ int netdev_queue_config_validate(struct net_device *dev, int rxq_idx,
bool netif_rxq_has_mp(struct net_device *dev, unsigned int rxq_idx);
bool netif_rxq_is_leased(struct net_device *dev, unsigned int rxq_idx);
-bool netif_is_queue_leasee(const struct net_device *dev);
void __netif_mp_uninstall_rxq(struct netdev_rx_queue *rxq,
const struct pp_memory_provider_params *p);
diff --git a/net/core/netdev_queues.c b/net/core/netdev_queues.c
index 5597af86591b..73fb28087a93 100644
--- a/net/core/netdev_queues.c
+++ b/net/core/netdev_queues.c
@@ -40,6 +40,8 @@ struct device *netdev_queue_get_dma_dev(struct net_device *dev,
struct netdev_rx_queue *hw_rxq;
struct device *dma_dev;
+ netdev_ops_assert_locked(dev);
+
/* Only RX side supports queue leasing today. */
if (type != NETDEV_QUEUE_TYPE_RX || !netif_rxq_is_leased(dev, idx))
return __netdev_queue_get_dma_dev(dev, idx);
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index 616cd7b42502..7da620aa11d6 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -31,8 +31,6 @@
#include <net/netdev_rx_queue.h>
#include <net/xdp.h>
-#include "../core/dev.h"
-
#include "xsk_queue.h"
#include "xdp_umem.h"
#include "xsk.h"
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 1/2] drm/drm_ras: Add clear-error-counter netlink command to drm_ras
From: Rodrigo Vivi @ 2026-04-09 13:37 UTC (permalink / raw)
To: Tauro, Riana
Cc: intel-xe, dri-devel, netdev, Zack McKevitt, joonas.lahtinen,
aravind.iddamsetty, anshuman.gupta, simona.vetter, airlied,
pratik.bari, joshua.santosh.ranjan, ashwin.kumar.kulkarni,
shubham.kumar, ravi.kishore.koppuravuri, raag.jadav,
anvesh.bakwad, maarten.lankhorst, Jakub Kicinski, Lijo Lazar,
Hawking Zhang, David S. Miller, Paolo Abeni, Eric Dumazet
In-Reply-To: <e7978696-eff6-4c1f-ab0f-047dccb18b59@intel.com>
On Thu, Apr 09, 2026 at 12:51:44PM +0530, Tauro, Riana wrote:
> Hi Zack
>
> Could you please take a look at this patch if applicable to your usecase.
> Please let me know if any
> changes are required
>
> @Rodrigo This is already reviewed by Jakub and Raag.
> If there are no opens, can this be merged via drm_misc
if we push this to drm-misc-next, it might take a few weeks to propagate
back to drm-xe-next. With other work from you and Raag going fast pace
on drm-xe-next around this area, I'm afraid it could cause some conflicts.
It is definitely fine by me, but another option is to get ack from
drm-misc maintainers to get this through drm-xe-next.
so, really okay with drm-misc-next?
>
> Thanks
> Riana
>
> On 4/9/2026 1:03 PM, Riana Tauro wrote:
> > Introduce a new 'clear-error-counter' drm_ras command to reset the counter
> > value for a specific error counter of a given node.
> >
> > The command is a 'do' netlink request with 'node-id' and 'error-id'
> > as parameters with no response payload.
> >
> > Usage:
> >
> > $ sudo ynl --family drm_ras --do clear-error-counter --json \
> > '{"node-id":1, "error-id":1}'
> > None
> >
> > Cc: Jakub Kicinski <kuba@kernel.org>
> > Cc: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
> > Cc: Lijo Lazar <lijo.lazar@amd.com>
> > Cc: Hawking Zhang <Hawking.Zhang@amd.com>
> > Cc: David S. Miller <davem@davemloft.net>
> > Cc: Paolo Abeni <pabeni@redhat.com>
> > Cc: Eric Dumazet <edumazet@google.com>
> > Signed-off-by: Riana Tauro <riana.tauro@intel.com>
> > Reviewed-by: Jakub Kicinski <kuba@kernel.org>
> > Reviewed-by: Raag Jadav <raag.jadav@intel.com>
> > ---
> > Documentation/gpu/drm-ras.rst | 8 +++++
> > Documentation/netlink/specs/drm_ras.yaml | 13 ++++++-
> > drivers/gpu/drm/drm_ras.c | 43 +++++++++++++++++++++++-
> > drivers/gpu/drm/drm_ras_nl.c | 13 +++++++
> > drivers/gpu/drm/drm_ras_nl.h | 2 ++
> > include/drm/drm_ras.h | 11 ++++++
> > include/uapi/drm/drm_ras.h | 1 +
> > 7 files changed, 89 insertions(+), 2 deletions(-)
> >
> > diff --git a/Documentation/gpu/drm-ras.rst b/Documentation/gpu/drm-ras.rst
> > index 70b246a78fc8..4636e68f5678 100644
> > --- a/Documentation/gpu/drm-ras.rst
> > +++ b/Documentation/gpu/drm-ras.rst
> > @@ -52,6 +52,8 @@ User space tools can:
> > as a parameter.
> > * Query specific error counter values with the ``get-error-counter`` command, using both
> > ``node-id`` and ``error-id`` as parameters.
> > +* Clear specific error counters with the ``clear-error-counter`` command, using both
> > + ``node-id`` and ``error-id`` as parameters.
> > YAML-based Interface
> > --------------------
> > @@ -101,3 +103,9 @@ Example: Query an error counter for a given node
> > sudo ynl --family drm_ras --do get-error-counter --json '{"node-id":0, "error-id":1}'
> > {'error-id': 1, 'error-name': 'error_name1', 'error-value': 0}
> > +Example: Clear an error counter for a given node
> > +
> > +.. code-block:: bash
> > +
> > + sudo ynl --family drm_ras --do clear-error-counter --json '{"node-id":0, "error-id":1}'
> > + None
> > diff --git a/Documentation/netlink/specs/drm_ras.yaml b/Documentation/netlink/specs/drm_ras.yaml
> > index 79af25dac3c5..e113056f8c01 100644
> > --- a/Documentation/netlink/specs/drm_ras.yaml
> > +++ b/Documentation/netlink/specs/drm_ras.yaml
> > @@ -99,7 +99,7 @@ operations:
> > flags: [admin-perm]
> > do:
> > request:
> > - attributes:
> > + attributes: &id-attrs
> > - node-id
> > - error-id
> > reply:
> > @@ -113,3 +113,14 @@ operations:
> > - node-id
> > reply:
> > attributes: *errorinfo
> > + -
> > + name: clear-error-counter
> > + doc: >-
> > + Clear error counter for a given node.
> > + The request includes the error-id and node-id of the
> > + counter to be cleared.
> > + attribute-set: error-counter-attrs
> > + flags: [admin-perm]
> > + do:
> > + request:
> > + attributes: *id-attrs
> > diff --git a/drivers/gpu/drm/drm_ras.c b/drivers/gpu/drm/drm_ras.c
> > index b2fa5ab86d87..d6eab29a1394 100644
> > --- a/drivers/gpu/drm/drm_ras.c
> > +++ b/drivers/gpu/drm/drm_ras.c
> > @@ -26,7 +26,7 @@
> > * efficient lookup by ID. Nodes can be registered or unregistered
> > * dynamically at runtime.
> > *
> > - * A Generic Netlink family `drm_ras` exposes two main operations to
> > + * A Generic Netlink family `drm_ras` exposes the below operations to
> > * userspace:
> > *
> > * 1. LIST_NODES: Dump all currently registered RAS nodes.
> > @@ -37,6 +37,10 @@
> > * Returns all counters of a node if only Node ID is provided or specific
> > * error counters.
> > *
> > + * 3. CLEAR_ERROR_COUNTER: Clear error counter of a given node.
> > + * Userspace must provide Node ID, Error ID.
> > + * Clears specific error counter of a node if supported.
> > + *
> > * Node registration:
> > *
> > * - drm_ras_node_register(): Registers a new node and assigns
> > @@ -66,6 +70,8 @@
> > * operation, fetching all counters from a specific node.
> > * - drm_ras_nl_get_error_counter_doit(): Implements the GET_ERROR_COUNTER doit
> > * operation, fetching a counter value from a specific node.
> > + * - drm_ras_nl_clear_error_counter_doit(): Implements the CLEAR_ERROR_COUNTER doit
> > + * operation, clearing a counter value from a specific node.
> > */
> > static DEFINE_XARRAY_ALLOC(drm_ras_xa);
> > @@ -314,6 +320,41 @@ int drm_ras_nl_get_error_counter_doit(struct sk_buff *skb,
> > return doit_reply_value(info, node_id, error_id);
> > }
> > +/**
> > + * drm_ras_nl_clear_error_counter_doit() - Clear an error counter of a node
> > + * @skb: Netlink message buffer
> > + * @info: Generic Netlink info containing attributes of the request
> > + *
> > + * Extracts the node ID and error ID from the netlink attributes and
> > + * clears the current value.
> > + *
> > + * Return: 0 on success, or negative errno on failure.
> > + */
> > +int drm_ras_nl_clear_error_counter_doit(struct sk_buff *skb,
> > + struct genl_info *info)
> > +{
> > + struct drm_ras_node *node;
> > + u32 node_id, error_id;
> > +
> > + if (!info->attrs ||
> > + GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID) ||
> > + GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID))
> > + return -EINVAL;
> > +
> > + node_id = nla_get_u32(info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID]);
> > + error_id = nla_get_u32(info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID]);
> > +
> > + node = xa_load(&drm_ras_xa, node_id);
> > + if (!node || !node->clear_error_counter)
> > + return -ENOENT;
> > +
> > + if (error_id < node->error_counter_range.first ||
> > + error_id > node->error_counter_range.last)
> > + return -EINVAL;
> > +
> > + return node->clear_error_counter(node, error_id);
> > +}
> > +
> > /**
> > * drm_ras_node_register() - Register a new RAS node
> > * @node: Node structure to register
> > diff --git a/drivers/gpu/drm/drm_ras_nl.c b/drivers/gpu/drm/drm_ras_nl.c
> > index 16803d0c4a44..dea1c1b2494e 100644
> > --- a/drivers/gpu/drm/drm_ras_nl.c
> > +++ b/drivers/gpu/drm/drm_ras_nl.c
> > @@ -22,6 +22,12 @@ static const struct nla_policy drm_ras_get_error_counter_dump_nl_policy[DRM_RAS_
> > [DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID] = { .type = NLA_U32, },
> > };
> > +/* DRM_RAS_CMD_CLEAR_ERROR_COUNTER - do */
> > +static const struct nla_policy drm_ras_clear_error_counter_nl_policy[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID + 1] = {
> > + [DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID] = { .type = NLA_U32, },
> > + [DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID] = { .type = NLA_U32, },
> > +};
> > +
> > /* Ops table for drm_ras */
> > static const struct genl_split_ops drm_ras_nl_ops[] = {
> > {
> > @@ -43,6 +49,13 @@ static const struct genl_split_ops drm_ras_nl_ops[] = {
> > .maxattr = DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID,
> > .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
> > },
> > + {
> > + .cmd = DRM_RAS_CMD_CLEAR_ERROR_COUNTER,
> > + .doit = drm_ras_nl_clear_error_counter_doit,
> > + .policy = drm_ras_clear_error_counter_nl_policy,
> > + .maxattr = DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID,
> > + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
> > + },
> > };
> > struct genl_family drm_ras_nl_family __ro_after_init = {
> > diff --git a/drivers/gpu/drm/drm_ras_nl.h b/drivers/gpu/drm/drm_ras_nl.h
> > index 06ccd9342773..a398643572a5 100644
> > --- a/drivers/gpu/drm/drm_ras_nl.h
> > +++ b/drivers/gpu/drm/drm_ras_nl.h
> > @@ -18,6 +18,8 @@ int drm_ras_nl_get_error_counter_doit(struct sk_buff *skb,
> > struct genl_info *info);
> > int drm_ras_nl_get_error_counter_dumpit(struct sk_buff *skb,
> > struct netlink_callback *cb);
> > +int drm_ras_nl_clear_error_counter_doit(struct sk_buff *skb,
> > + struct genl_info *info);
> > extern struct genl_family drm_ras_nl_family;
> > diff --git a/include/drm/drm_ras.h b/include/drm/drm_ras.h
> > index 5d50209e51db..f2a787bc4f64 100644
> > --- a/include/drm/drm_ras.h
> > +++ b/include/drm/drm_ras.h
> > @@ -58,6 +58,17 @@ struct drm_ras_node {
> > int (*query_error_counter)(struct drm_ras_node *node, u32 error_id,
> > const char **name, u32 *val);
> > + /**
> > + * @clear_error_counter:
> > + *
> > + * This callback is used by drm_ras to clear a specific error counter.
> > + * Driver should implement this callback to support clearing error counters
> > + * of a node.
> > + *
> > + * Returns: 0 on success, negative error code on failure.
> > + */
> > + int (*clear_error_counter)(struct drm_ras_node *node, u32 error_id);
> > +
> > /** @priv: Driver private data */
> > void *priv;
> > };
> > diff --git a/include/uapi/drm/drm_ras.h b/include/uapi/drm/drm_ras.h
> > index 5f40fa5b869d..218a3ee86805 100644
> > --- a/include/uapi/drm/drm_ras.h
> > +++ b/include/uapi/drm/drm_ras.h
> > @@ -41,6 +41,7 @@ enum {
> > enum {
> > DRM_RAS_CMD_LIST_NODES = 1,
> > DRM_RAS_CMD_GET_ERROR_COUNTER,
> > + DRM_RAS_CMD_CLEAR_ERROR_COUNTER,
> > __DRM_RAS_CMD_MAX,
> > DRM_RAS_CMD_MAX = (__DRM_RAS_CMD_MAX - 1)
^ permalink raw reply
* [PATCH net v2] net: phy: fix a return path in get_phy_c45_ids()
From: Charles Perry @ 2026-04-09 13:36 UTC (permalink / raw)
To: netdev
Cc: Charles Perry, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Florian Fainelli, linux-kernel
The return value of phy_c45_probe_present() is stored in "ret", not
"phy_reg", fix this. "phy_reg" always has a positive value if we reach
this return path (since it would have returned earlier otherwise), which
means that the original goal of the patch of not considering -ENODEV
fatal wasn't achieved.
Fixes: 17b447539408 ("net: phy: c45 scanning: Don't consider -ENODEV fatal")
Signed-off-by: Charles Perry <charles.perry@microchip.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
Notes:
Changes in v2:
- Collect Andrew's Reviewed-by
- Send to net instead of net-next
- fix one typo in the commit message
drivers/net/phy/phy_device.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 81984d4ebb7c..810327fa886a 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -973,8 +973,8 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
/* returning -ENODEV doesn't stop bus
* scanning
*/
- return (phy_reg == -EIO ||
- phy_reg == -ENODEV) ? -ENODEV : -EIO;
+ return (ret == -EIO ||
+ ret == -ENODEV) ? -ENODEV : -EIO;
if (!ret)
continue;
--
2.47.3
^ permalink raw reply related
* RE: [Intel-wired-lan] [PATCH iwl-next v2 08/10] ice: program ACL entry
From: Loktionov, Aleksandr @ 2026-04-09 13:35 UTC (permalink / raw)
To: Marcin Szycik, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Penigalapati, Sandeep, S, Ananth,
alexander.duyck@gmail.com, Cao, Chinh T, Nguyen, Anthony L
In-Reply-To: <20260409120003.2719-9-marcin.szycik@linux.intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Marcin Szycik
> Sent: Thursday, April 9, 2026 2:00 PM
> To: intel-wired-lan@lists.osuosl.org
> Cc: netdev@vger.kernel.org; Penigalapati, Sandeep
> <sandeep.penigalapati@intel.com>; S, Ananth <ananth.s@intel.com>;
> alexander.duyck@gmail.com; Marcin Szycik
> <marcin.szycik@linux.intel.com>; Cao, Chinh T <chinh.t.cao@intel.com>;
> Nguyen, Anthony L <anthony.l.nguyen@intel.com>
> Subject: [Intel-wired-lan] [PATCH iwl-next v2 08/10] ice: program ACL
> entry
>
> From: Real Valiquette <real.valiquette@intel.com>
>
> Complete the filter programming process; set the flow entry and action
> into the scenario and write it to hardware. Configure the VSI for ACL
> filters.
>
> Co-developed-by: Chinh Cao <chinh.t.cao@intel.com>
> Signed-off-by: Chinh Cao <chinh.t.cao@intel.com>
> Signed-off-by: Real Valiquette <real.valiquette@intel.com>
> Co-developed-by: Tony Nguyen <anthony.l.nguyen@intel.com>
> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
> Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
> ---
> v2:
> * Use plain alloc instead of devm_ for ice_flow_entry::acts
> * Use FIELD_PREP_CONST() for ICE_ACL_RX_*_MISS_CNTR
> * Fix wrong struct ice_acl_act_entry alloc count in
> ice_flow_acl_add_scen_entry_sync() - was e->entry_sz, which is an
> unrelated value
> * Only set acts_cnt after successful allocation in
> ice_flow_acl_add_scen_entry_sync()
> * Return -EINVAL instead of -ENOSPC on wrong index in
> ice_acl_scen_free_entry_idx()
> ---
> drivers/net/ethernet/intel/ice/ice.h | 2 +
> drivers/net/ethernet/intel/ice/ice_acl.h | 21 +
> .../net/ethernet/intel/ice/ice_adminq_cmd.h | 2 +
> drivers/net/ethernet/intel/ice/ice_flow.h | 3 +
> drivers/net/ethernet/intel/ice/ice_acl.c | 53 ++-
> drivers/net/ethernet/intel/ice/ice_acl_ctrl.c | 251 +++++++++++
> drivers/net/ethernet/intel/ice/ice_acl_main.c | 4 +
> .../ethernet/intel/ice/ice_ethtool_ntuple.c | 48 ++-
> drivers/net/ethernet/intel/ice/ice_flow.c | 395
> ++++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_lib.c | 10 +-
> 10 files changed, 782 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/ice.h
> b/drivers/net/ethernet/intel/ice/ice.h
> index 9e6643931022..f9a43daf04fe 100644
> --- a/drivers/net/ethernet/intel/ice/ice.h
> +++ b/drivers/net/ethernet/intel/ice/ice.h
> @@ -1061,6 +1061,8 @@ void ice_aq_prep_for_event(struct ice_pf *pf,
> struct ice_aq_task *task,
> u16 opcode);
> int ice_aq_wait_for_event(struct ice_pf *pf, struct ice_aq_task
> *task,
> unsigned long timeout);
> +int ice_ntuple_update_list_entry(struct ice_pf *pf,
> + struct ice_ntuple_fltr *input, int
> fltr_idx);
> int ice_open(struct net_device *netdev); int
> ice_open_internal(struct net_device *netdev); int ice_stop(struct
> net_device *netdev); diff --git
> a/drivers/net/ethernet/intel/ice/ice_acl.h
> b/drivers/net/ethernet/intel/ice/ice_acl.h
> index 3a4adcf368cf..0b5651401eb7 100644
> --- a/drivers/net/ethernet/intel/ice/ice_acl.h
> +++ b/drivers/net/ethernet/intel/ice/ice_acl.h
> @@ -39,6 +39,7 @@ struct ice_acl_tbl {
> DECLARE_BITMAP(avail, ICE_AQC_ACL_ALLOC_UNITS); };
>
> +#define ICE_MAX_ACL_TCAM_ENTRY (ICE_AQC_ACL_TCAM_DEPTH *
> +ICE_AQC_ACL_SLICES)
> enum ice_acl_entry_prio {
> ICE_ACL_PRIO_LOW = 0,
> ICE_ACL_PRIO_NORMAL,
> @@ -65,6 +66,11 @@ struct ice_acl_scen {
> * participate in this scenario
> */
> DECLARE_BITMAP(act_mem_bitmap, ICE_AQC_MAX_ACTION_MEMORIES);
...
> + /* Determine number of cascaded TCAMs */
> + num_cscd = DIV_ROUND_UP(scen->width,
> ICE_AQC_ACL_KEY_WIDTH_BYTES);
> +
> + entry_tcam = ICE_ACL_TBL_TCAM_IDX(scen->start);
> + idx = ICE_ACL_TBL_TCAM_ENTRY_IDX(scen->start + *entry_idx);
> +
> + for (u8 i = 0; i < num_cscd; i++) {
> + /* If the key spans more than one TCAM in the case of
> cascaded
> + * TCAMs, the key and key inverts need to be properly
> split
> + * among TCAMs.E.g.bytes 0 - 4 go to an index in the
> first TCAM
"E.g.bytes" -> "E.g. bytes"
> + * and bytes 5 - 9 go to the same index in the next
> TCAM, etc.
> + * If the entry spans more than one TCAM in a cascaded
> TCAM
> + * mode, the programming of the entries in the TCAMs
> must be in
> + * reversed order - the TCAM entry of the rightmost TCAM
> should
> + * be programmed first; the TCAM entry of the leftmost
> TCAM
> + * should be programmed last.
> + */
> + offset = num_cscd - i - 1;
> + memcpy(&buf.entry_key.val,
> + &keys[offset * sizeof(buf.entry_key.val)],
> + sizeof(buf.entry_key.val));
> + memcpy(&buf.entry_key_invert.val,
> + &inverts[offset *
> sizeof(buf.entry_key_invert.val)],
> + sizeof(buf.entry_key_invert.val));
> + err = ice_aq_program_acl_entry(hw, entry_tcam + offset,
> idx,
> + &buf, NULL);
> + if (err) {
> + ice_debug(hw, ICE_DBG_ACL, "aq program acl entry
> failed status: %d\n",
> + err);
> + goto out;
> + }
> + }
> +
> + err = ice_acl_prog_act(hw, scen, acts, acts_cnt, *entry_idx);
> +
> +out:
> + if (err) {
> + ice_acl_rem_entry(hw, scen, *entry_idx);
> + *entry_idx = 0;
> + }
> +
> + return err;
> +}
> +
> +/**
> + * ice_acl_prog_act - Program a scenario's action memory
> + * @hw: pointer to the HW struct
> + * @scen: scenario to add the entry to
> + * @acts: pointer to a buffer containing formatted actions
> + * @acts_cnt: indicates the number of actions stored in "acts"
> + * @entry_idx: scenario relative index of the added flow entry
> + *
> + * Return: 0 on success, negative on error */ int
> +ice_acl_prog_act(struct ice_hw *hw, struct ice_acl_scen *scen,
> + struct ice_acl_act_entry *acts, u8 acts_cnt, u16
> entry_idx) {
> + u8 entry_tcam, num_cscd, i, actx_idx = 0;
> + struct ice_aqc_actpair act_buf = {};
> + int err = 0;
> + u16 idx;
> +
> + if (entry_idx >= scen->num_entry)
> + return -ENOSPC;
> +
> + /* Determine number of cascaded TCAMs */
> + num_cscd = DIV_ROUND_UP(scen->width,
> ICE_AQC_ACL_KEY_WIDTH_BYTES);
> +
> + entry_tcam = ICE_ACL_TBL_TCAM_IDX(scen->start);
> + idx = ICE_ACL_TBL_TCAM_ENTRY_IDX(scen->start + entry_idx);
> +
> + for_each_set_bit(i, scen->act_mem_bitmap,
> ICE_AQC_MAX_ACTION_MEMORIES) {
> + struct ice_acl_act_mem *mem = &hw->acl_tbl->act_mems[i];
> +
> + if (actx_idx >= acts_cnt)
> + break;
> + if (mem->member_of_tcam >= entry_tcam &&
> + mem->member_of_tcam < entry_tcam + num_cscd) {
> + memcpy(&act_buf.act[0], &acts[actx_idx],
> + sizeof(struct ice_acl_act_entry));
> +
> + if (++actx_idx < acts_cnt) {
> + memcpy(&act_buf.act[1], &acts[actx_idx],
> + sizeof(struct ice_acl_act_entry));
> + }
> +
> + err = ice_aq_program_actpair(hw, i, idx,
> &act_buf,
> + NULL);
> + if (err) {
> + ice_debug(hw, ICE_DBG_ACL, "program actpair
> failed status: %d\n",
> + err);
> + break;
> + }
> + actx_idx++;
> + }
> + }
> +
> + if (!err && actx_idx < acts_cnt)
> + err = -ENOSPC;
> +
> + return err;
> +}
> +
> +/**
> + * ice_acl_rem_entry - Remove a flow entry from an ACL scenario
> + * @hw: pointer to the HW struct
> + * @scen: scenario to remove the entry from
> + * @entry_idx: the scenario-relative index of the flow entry being
> +removed
> + *
> + * Return: 0 on success, negative on error */ int
> +ice_acl_rem_entry(struct ice_hw *hw, struct ice_acl_scen *scen,
> + u16 entry_idx)
> +{
> + struct ice_aqc_actpair act_buf = {};
> + struct ice_aqc_acl_data buf;
> + u8 entry_tcam, num_cscd, i;
> + int err = 0;
> + u16 idx;
> +
> + if (!scen)
> + return -ENOENT;
> +
> + if (entry_idx >= scen->num_entry)
> + return -ENOSPC;
> +
> + if (!test_bit(entry_idx, scen->entry_bitmap))
> + return -ENOENT;
> +
> + /* Determine number of cascaded TCAMs */
> + num_cscd = DIV_ROUND_UP(scen->width,
> ICE_AQC_ACL_KEY_WIDTH_BYTES);
> +
> + entry_tcam = ICE_ACL_TBL_TCAM_IDX(scen->start);
> + idx = ICE_ACL_TBL_TCAM_ENTRY_IDX(scen->start + entry_idx);
> +
> + /* invalidate the flow entry */
> + memset(&buf, 0, sizeof(buf));
> + for (i = 0; i < num_cscd; i++) {
> + err = ice_aq_program_acl_entry(hw, entry_tcam + i, idx,
> &buf,
> + NULL);
> + if (err)
> + ice_debug(hw, ICE_DBG_ACL, "AQ program ACL entry
> failed status: %d\n",
> + err);
> + }
> +
> + for_each_set_bit(i, scen->act_mem_bitmap,
> ICE_AQC_MAX_ACTION_MEMORIES) {
> + struct ice_acl_act_mem *mem = &hw->acl_tbl->act_mems[i];
> +
> + if (mem->member_of_tcam >= entry_tcam &&
> + mem->member_of_tcam < entry_tcam + num_cscd) {
> + /* Invalidate allocated action pairs */
> + err = ice_aq_program_actpair(hw, i, idx,
> &act_buf,
> + NULL);
> + if (err)
> + ice_debug(hw, ICE_DBG_ACL, "program actpair
> failed status: %d\n",
> + err);
> + }
> + }
> +
> + ice_acl_scen_free_entry_idx(scen, entry_idx);
> +
> + return err;
> +}
> diff --git a/drivers/net/ethernet/intel/ice/ice_acl_main.c
> b/drivers/net/ethernet/intel/ice/ice_acl_main.c
> index 53cca0526756..16228be574ed 100644
> --- a/drivers/net/ethernet/intel/ice/ice_acl_main.c
> +++ b/drivers/net/ethernet/intel/ice/ice_acl_main.c
> @@ -280,6 +280,10 @@ int ice_acl_add_rule_ethtool(struct ice_vsi *vsi,
> struct ethtool_rxnfc *cmd)
> hw_prof->entry_h[hw_prof->cnt++][0] = entry_h;
> }
>
> + input->acl_fltr = true;
> + /* input struct is added to the HW filter list */
> + ice_ntuple_update_list_entry(pf, input, fsp->location);
> +
> return 0;
>
> free_input:
> diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
> b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
> index 3e79c0bf40f4..21d4f4e3a1d0 100644
> --- a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
> +++ b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
> @@ -1791,6 +1791,21 @@ void ice_vsi_manage_fdir(struct ice_vsi *vsi,
> bool ena)
> mutex_unlock(&hw->fdir_fltr_lock);
> }
>
> +/**
> + * ice_del_acl_ethtool - delete an ACL rule entry
> + * @hw: pointer to HW instance
> + * @fltr: filter structure
> + *
> + * Return: 0 on success, negative on error */ static int
> +ice_del_acl_ethtool(struct ice_hw *hw, struct ice_ntuple_fltr *fltr)
> {
> + u64 entry;
> +
> + entry = ice_flow_find_entry(hw, ICE_BLK_ACL, fltr->fltr_id);
> + return ice_flow_rem_entry(hw, ICE_BLK_ACL, entry); }
> +
> /**
> * ice_fdir_do_rem_flow - delete flow and possibly add perfect flow
> * @pf: PF structure
> @@ -1824,7 +1839,7 @@ ice_fdir_do_rem_flow(struct ice_pf *pf, enum
> ice_fltr_ptype flow_type)
> *
> * Return: 0 on success and negative on errors
> */
> -static int
> +int
> ice_ntuple_update_list_entry(struct ice_pf *pf, struct
> ice_ntuple_fltr *input,
> int fltr_idx)
> {
> @@ -1843,13 +1858,36 @@ ice_ntuple_update_list_entry(struct ice_pf
> *pf, struct ice_ntuple_fltr *input,
>
> old_fltr = ice_fdir_find_fltr_by_idx(hw, fltr_idx);
> if (old_fltr) {
> - err = ice_fdir_write_all_fltr(pf, old_fltr, false);
> - if (err)
> - return err;
> + if (old_fltr->acl_fltr) {
> + /* ACL filter - if the input buffer is present
> + * then this is an update and we don't want to
> + * delete the filter from the HW. We've already
> + * written the change to the HW at this point, so
> + * just update the SW structures to make sure
> + * everything is hunky-dory. If no input then
> this
> + * is a delete so we should delete the filter
> from
> + * the HW and clean up our SW structures.
> + */
> + if (!input) {
> + err = ice_del_acl_ethtool(hw, old_fltr);
> + if (err)
> + return err;
> + }
> + } else {
> + /* FD filter */
> + err = ice_fdir_write_all_fltr(pf, old_fltr,
> false);
> + if (err)
> + return err;
> + }
> +
> ice_fdir_update_cntrs(hw, old_fltr->flow_type, false,
> false);
> /* update sb-filters count, specific to ring->channel */
> ice_update_per_q_fltr(vsi, old_fltr->orig_q_index,
> false);
> - if (!input && !hw->fdir_fltr_cnt[old_fltr->flow_type])
> + /* Also delete the HW filter info if we have just
> deleted the
> + * last filter of flow_type.
> + */
> + if (!old_fltr->acl_fltr && !input &&
> + !hw->fdir_fltr_cnt[old_fltr->flow_type])
> /* we just deleted the last filter of flow_type
> so we
> * should also delete the HW filter info.
> */
> diff --git a/drivers/net/ethernet/intel/ice/ice_flow.c
> b/drivers/net/ethernet/intel/ice/ice_flow.c
> index dce6d2ffcb15..144d8326d4f9 100644
> --- a/drivers/net/ethernet/intel/ice/ice_flow.c
> +++ b/drivers/net/ethernet/intel/ice/ice_flow.c
> @@ -1744,6 +1744,16 @@ static int ice_flow_rem_entry_sync(struct
> ice_hw *hw, enum ice_block blk,
> return -EINVAL;
>
> if (blk == ICE_BLK_ACL) {
> + int err;
> +
> + if (!entry->prof)
> + return -EINVAL;
> +
> + err = ice_acl_rem_entry(hw, entry->prof->cfg.scen,
> + entry->scen_entry_idx);
> + if (err)
> + return err;
> +
> if (entry->acts_cnt && entry->acts)
> ice_flow_acl_free_act_cntr(hw, entry->acts,
> entry->acts_cnt);
> @@ -1879,10 +1889,34 @@ ice_flow_rem_prof_sync(struct ice_hw *hw, enum
> ice_block blk,
> }
>
> if (blk == ICE_BLK_ACL) {
> + struct ice_aqc_acl_prof_generic_frmt buf;
> + u8 prof_id = 0;
> +
> /* Disassociate the scenario from the profile for the PF
> */
> status = ice_flow_acl_disassoc_scen(hw, prof);
> if (status)
> return status;
> +
> + status = ice_flow_get_hw_prof(hw, blk, prof->id,
> &prof_id);
> + if (status)
> + return status;
> +
> + status = ice_query_acl_prof(hw, prof_id, &buf, NULL);
> + if (status)
> + return status;
> +
> + /* Clear the range-checker if the profile ID is no
> longer
> + * used by any PF
> + */
> + if (!ice_flow_acl_is_prof_in_use(&buf)) {
> + /* Clear the range-checker value for profile ID
> */
> + struct ice_aqc_acl_profile_ranges query_rng_buf =
> {};
> +
> + status = ice_prog_acl_prof_ranges(hw, prof_id,
> + &query_rng_buf,
> NULL);
> + if (status)
> + return status;
> + }
> }
>
> /* Remove all hardware profiles associated with this flow
> profile */ @@ -2214,6 +2248,44 @@ int ice_flow_rem_prof(struct ice_hw
> *hw, enum ice_block blk, u64 prof_id)
> return status;
> }
>
> +/**
> + * ice_flow_find_entry - look for a flow entry using its unique ID
> + * @hw: pointer to the HW struct
> + * @blk: classification stage
> + * @entry_id: unique ID to identify this flow entry
> + *
> + * Look for the flow entry with the specified unique ID in all flow
> +profiles of
> + * the specified classification stage.
> + *
> + * Return: flow entry handle if entry found, ICE_FLOW_ENTRY_ID_INVAL
> +otherwise */
> +u64 ice_flow_find_entry(struct ice_hw *hw, enum ice_block blk, u64
> +entry_id) {
> + struct ice_flow_entry *found = NULL;
> + struct ice_flow_prof *p;
> +
> + mutex_lock(&hw->fl_profs_locks[blk]);
> +
> + list_for_each_entry(p, &hw->fl_profs[blk], l_entry) {
> + struct ice_flow_entry *e;
> +
> + mutex_lock(&p->entries_lock);
> + list_for_each_entry(e, &p->entries, l_entry)
> + if (e->id == entry_id) {
> + found = e;
> + break;
> + }
> + mutex_unlock(&p->entries_lock);
> +
> + if (found)
> + break;
> + }
> +
> + mutex_unlock(&hw->fl_profs_locks[blk]);
> +
> + return found ? ICE_FLOW_ENTRY_HNDL(found) :
> +ICE_FLOW_ENTRY_HANDLE_INVAL; }
> +
> /**
> * ice_flow_acl_check_actions - Checks the ACL rule's actions
> * @hw: pointer to the hardware structure @@ -2541,6 +2613,325 @@
> static int ice_flow_acl_frmt_entry(struct ice_hw *hw,
>
> return err;
> }
> +
> +/**
> + * ice_flow_acl_find_scen_entry_cond - Find an ACL scenario entry
> that matches
> + * the compared data
> + * @prof: pointer to flow profile
> + * @e: pointer to the comparing flow entry
> + * @do_chg_action: decide if we want to change the ACL action
> + * @do_add_entry: decide if we want to add the new ACL entry
> + * @do_rem_entry: decide if we want to remove the current ACL entry
> + *
> + * Find an ACL scenario entry that matches the compared data. Also
> figure out:
> + * a) If we want to change the ACL action
> + * b) If we want to add the new ACL entry
> + * c) If we want to remove the current ACL entry
> + *
> + * Return: ACL scenario entry, or NULL if not found */ static struct
> +ice_flow_entry * ice_flow_acl_find_scen_entry_cond(struct
> ice_flow_prof
> +*prof,
> + struct ice_flow_entry *e, bool
> *do_chg_action,
> + bool *do_add_entry, bool *do_rem_entry) {
> + struct ice_flow_entry *p, *return_entry = NULL;
> +
> + /* Check if:
> + * a) There exists an entry with same matching data, but
> different
> + * priority, then we remove this existing ACL entry. Then,
> we
> + * will add the new entry to the ACL scenario.
> + * b) There exists an entry with same matching data, priority,
> and
> + * result action, then we do nothing
> + * c) There exists an entry with same matching data, priority,
> but
> + * different, action, then do only change the action's
> entry.
Too much of commas, please reduce the number.
> + * d) Else, we add this new entry to the ACL scenario.
> + */
> + *do_chg_action = false;
> + *do_add_entry = true;
> + *do_rem_entry = false;
> + list_for_each_entry(p, &prof->entries, l_entry) {
> + if (memcmp(p->entry, e->entry, p->entry_sz))
> + continue;
> +
> + /* From this point, we have the same matching_data. */
> + *do_add_entry = false;
> + return_entry = p;
> +
> + if (p->priority != e->priority) {
> + /* matching data && !priority */
> + *do_add_entry = true;
> + *do_rem_entry = true;
> + break;
> + }
> +
> + /* From this point, we will have matching_data &&
> priority */
> + if (p->acts_cnt != e->acts_cnt)
> + *do_chg_action = true;
> + for (int i = 0; i < p->acts_cnt; i++) {
> + bool found_not_match = false;
> +
> + for (int j = 0; j < e->acts_cnt; j++)
> + if (memcmp(&p->acts[i], &e->acts[j],
> + sizeof(struct ice_flow_action)))
Due to comment above it should be if (!memcmp(&p->acts[i], &e->acts[j],
Please fix the comment or code.
Otherwise, it looks good.
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> {
> + found_not_match = true;
> + break;
...
> }
>
> /**
> --
> 2.49.0
^ permalink raw reply
* [RFC net PATCH v1] net: pcs: pcs-mtk-lynxi: fix bpi-r3 serdes configuration
From: Frank Wunderlich @ 2026-04-09 13:33 UTC (permalink / raw)
To: Alexander Couzens, Daniel Golle, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Matthias Brugger, AngeloGioacchino Del Regno,
Vladimir Oltean
Cc: Frank Wunderlich, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek
From: Frank Wunderlich <frank-w@public-files.de>
Commit 8871389da151 introduces common pcs dts properties which writes
rx=normal,tx=normal polarity to register SGMSYS_QPHY_WRAP_CTRL of switch.
This is initialized with tx-bit set and so change inverts polarity
compared to before.
It looks like mt7531 has tx polarity inverted in hardware and set tx-bit
by default to restore the normal polarity.
Till this patch the register write was only called when mediatek,pnswap
property was set which cannot be done for switch because the fw-node param
was always NULL from switch driver in the mtk_pcs_lynxi_create call.
Do not configure switch side like it's done before.
Fixes: 8871389da151 ("net: pcs: pcs-mtk-lynxi: deprecate "mediatek,pnswap"")
Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
---
drivers/net/pcs/pcs-mtk-lynxi.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/pcs/pcs-mtk-lynxi.c b/drivers/net/pcs/pcs-mtk-lynxi.c
index c12f8087af9b..a753bd88cbc2 100644
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
@@ -129,6 +129,9 @@ static int mtk_pcs_config_polarity(struct mtk_pcs_lynxi *mpcs,
unsigned int val = 0;
int ret;
+ if (!fwnode)
+ return 0;
+
if (fwnode_property_read_bool(fwnode, "mediatek,pnswap"))
default_pol = PHY_POL_INVERT;
--
2.43.0
^ permalink raw reply related
* RE: [PATCH net v2 2/2] net: phy: micrel: remove ksz9131_resume()
From: Biju Das @ 2026-04-09 13:25 UTC (permalink / raw)
To: Andrew Lunn, Russell King (Oracle)
Cc: Ovidiu Panait, hkallweit1@gmail.com, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-renesas-soc@vger.kernel.org
In-Reply-To: <31b1e474-4b87-4a75-93fa-1bcf3a41221a@lunn.ch>
Hi Andrew/Russell,
> -----Original Message-----
> From: Andrew Lunn <andrew@lunn.ch>
> Sent: 09 April 2026 13:44
> Subject: Re: [PATCH net v2 2/2] net: phy: micrel: remove ksz9131_resume()
>
> > I think we need a simple solution for 7.0, but subject to Andrew's
> > agreement, I think we should consider having phy_init_hw() inside
> > phy_resume(), and a series of cleanup patches that result from that
> > change, including getting rid of unnecessary code in micrel.c for the
> > next kernel cycle. As I say, subject to Andrew's agreement, please can
> > you look into this. Thanks.
>
> It does seem reasonable, and it would impose some uniformity on drivers.
Yes sure, we can send cleanup patches for the next kernel cycle.
Cheers,
Biju
^ permalink raw reply
* Re: [PATCH net v3 0/3] net: lan966x: fix page_pool error handling and error paths
From: patchwork-bot+netdevbpf @ 2026-04-09 13:20 UTC (permalink / raw)
To: David CARLIER
Cc: horatiu.vultur, UNGLinuxDriver, andrew+netdev, davem, edumazet,
kuba, pabeni, netdev, linux-kernel, stable
In-Reply-To: <20260405055241.35767-1-devnexen@gmail.com>
Hello:
This series was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Sun, 5 Apr 2026 06:52:38 +0100 you wrote:
> This series fixes error handling around the lan966x page pool:
>
> 1/3 adds the missing IS_ERR check after page_pool_create(), preventing
> a kernel oops when the error pointer flows into
> xdp_rxq_info_reg_mem_model().
>
> 2/3 plugs page pool leaks in the lan966x_fdma_rx_alloc() and
> lan966x_fdma_init() error paths, now reachable after 1/3.
>
> [...]
Here is the summary with links:
- [net,v3,v3,1/3] net: lan966x: fix page_pool error handling in lan966x_fdma_rx_alloc_page_pool()
https://git.kernel.org/netdev/net/c/3fd0da4fd885
- [net,v3,v3,2/3] net: lan966x: fix page pool leak in error paths
https://git.kernel.org/netdev/net/c/076344a6ad9d
- [net,v3,v3,3/3] net: lan966x: fix use-after-free and leak in lan966x_fdma_reload()
https://git.kernel.org/netdev/net/c/59c3d55a946c
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net-next v2 3/3] net: mdio: treat PSE EPROBE_DEFER as non-fatal during PHY registration
From: Kory Maincent @ 2026-04-09 13:09 UTC (permalink / raw)
To: Andrew Lunn
Cc: Carlo Szelinsky, o.rempel, andrew+netdev, hkallweit1, linux, kuba,
davem, edumazet, pabeni, horms, netdev, linux-kernel
In-Reply-To: <841e0a96-5e5c-4225-a426-24726163699f@lunn.ch>
On Thu, 9 Apr 2026 14:30:53 +0200
Andrew Lunn <andrew@lunn.ch> wrote:
> On Wed, Apr 08, 2026 at 11:07:11PM +0200, Carlo Szelinsky wrote:
> > So I went ahead and tested the phy_probe() approach on my setup (RTL930x
> > DSA switch with an I2C Hasivo HS104 PSE controller as module).
> >
> > PoE itself works fine, but phydev->psec never gets set - ethtool just
> > says "No PSE is attached" on all ports.
> >
> > Took me a while to figure out what's going on. The problem is how DSA
> > handles PHYs: when phy_probe() returns -EPROBE_DEFER because the PSE
> > controller hasn't probed yet, the PHY device is registered but sits
> > there unprobed. Then the DSA switch comes along, sets up its ports, and
> > phy_attach_direct() force-binds the generic PHY driver with
> > device_bind_driver(). Now the device already has a driver, so when the
> > deferred probe retry kicks in it just skips it. phy_probe() never runs
> > again and psec stays NULL.
> >
> > What I'm seeing timing-wise:
> > - MDIO scan registers PHYs, phy_probe() defers (no PSE yet)
> > - DSA probes, phy_attach_direct() binds genphy
> > - t=17s: HS104 finally probes
> > - deferred retry: nope, driver already bound
> > - t=35s: regulator_late_cleanup (caught by admin_state_synced)
> >
> > Not sure what the best path forward is here. Should we look at fixing
> > phy_attach_direct() to handle this case, or go back to the non-fatal
> > EPROBE_DEFER approach from v2 for now?
>
> I thought about this some more. Looking at the phydev->drv might solve
> it for DSA switches, but it is not a general solution for when the PSE
> is used with a normal MAC, and the phylink_connect() happens in
> open().
>
> Now, the HS104 appears to be a quad device, intended for switches. So
> maybe solving the issue only for DSA is O.K?
>
> Otherwise, maybe we need the ability to associate the PSE controller
> to the MAC? At a quick look, PSE and PHY are not tightly bound
> together. So we could make the MAC driver call something similar to
> of_pse_control_get() in its probe function, where it can return
> EPRODE_DEFFER. net/ethtool/pse-pd.c would need some changes as well.
I don't think we should associate it to the MAC. In a hardware point of view the
PSE Power Interfaces are wired the MDIs. It was associated historically to the
PHY, because, at that time there was no representation of the MDI. Now thanks
to Maxime there is, so in the future we should associate the PSE control to the
MDI only. We will already have to keep the PHY association compatibility to not
break binding API but please do not add another layer of association that will
increase more compatibility burden for the future.
As I proposed in another thread, if the PHY can't find the PSE PI, we could
save the phandle of the PSE PI somewhere in the PHY structure. Then at PSE
register time, look for each PHY and try to resolve every unresolved phandle.
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH RFC net-next 4/4] net: ethernet: mtk_eth_soc: report INGRESS_L2 byte_type in flow stats
From: Daniel Golle @ 2026-04-09 13:07 UTC (permalink / raw)
To: Felix Fietkau, John Crispin, Lorenzo Bianconi, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Matthias Brugger, AngeloGioacchino Del Regno, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, netdev,
linux-kernel, linux-arm-kernel, linux-mediatek, netfilter-devel,
coreteam
In-Reply-To: <cover.1775739840.git.daniel@makrotopia.org>
The MediaTek PPE MIB counters report ingress L2 frame bytes
including Ethernet, VLAN and PPPoE headers. Tell the flow offload
framework so it can derive correct L3 byte counts for conntrack
and update sub-interface counters.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
index cc8c4ef8038f3..68cb03a193f3f 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -557,6 +557,7 @@ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
&diff)) {
f->stats.pkts += diff.packets;
f->stats.bytes += diff.bytes;
+ f->stats.byte_type = FLOW_STATS_BYTES_INGRESS_L2;
}
return 0;
--
2.53.0
^ permalink raw reply related
* [PATCH RFC net-next 3/4] nf_flow_table: convert hw byte counts and update sub-interface stats
From: Daniel Golle @ 2026-04-09 13:07 UTC (permalink / raw)
To: Felix Fietkau, John Crispin, Lorenzo Bianconi, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Matthias Brugger, AngeloGioacchino Del Regno, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, netdev,
linux-kernel, linux-arm-kernel, linux-mediatek, netfilter-devel,
coreteam
In-Reply-To: <cover.1775739840.git.daniel@makrotopia.org>
Hardware flow offload counters may report L2 frame bytes while
conntrack expects L3 (IP) bytes. When a driver sets byte_type
to INGRESS_L2 or EGRESS_L2, subtract the appropriate per-direction
encap and tunnel overhead to derive L3 byte counts for conntrack.
Additionally, propagate per-flow stats to bridge, VLAN and PPPoE
sub-interfaces that are bypassed by hardware offloading. Each
sub-interface gets the L3 byte count plus the overhead of any
inner encap layers below it, matching what the software path
would count. Both RX and TX directions are updated.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
net/netfilter/nf_flow_table_offload.c | 174 +++++++++++++++++++++++++-
1 file changed, 172 insertions(+), 2 deletions(-)
diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index 002ec15d988bd..67452da487c94 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -5,6 +5,8 @@
#include <linux/netfilter.h>
#include <linux/rhashtable.h>
#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/if_pppox.h>
#include <linux/tc_act/tc_csum.h>
#include <net/flow_offload.h>
#include <net/ip_tunnels.h>
@@ -1008,10 +1010,135 @@ static void flow_offload_tuple_stats(struct flow_offload_work *offload,
&offload->flowtable->flow_block.cb_list);
}
+static int flow_offload_encap_hlen(const struct flow_offload_tuple *tuple,
+ int idx)
+{
+ switch (tuple->encap[idx].proto) {
+ case htons(ETH_P_8021Q):
+ case htons(ETH_P_8021AD):
+ return VLAN_HLEN;
+ case htons(ETH_P_PPP_SES):
+ return PPPOE_SES_HLEN;
+ }
+ return 0;
+}
+
+static void flow_offload_encap_netstats(struct net_device *dev,
+ __be16 encap_proto,
+ bool rx, u64 pkts, u64 bytes)
+{
+ struct pcpu_sw_netstats *tstats;
+ struct vlan_pcpu_stats *vstats;
+
+ if (encap_proto == htons(ETH_P_8021Q) ||
+ encap_proto == htons(ETH_P_8021AD)) {
+ vstats = this_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats);
+ u64_stats_update_begin(&vstats->syncp);
+ if (rx) {
+ u64_stats_add(&vstats->rx_packets, pkts);
+ u64_stats_add(&vstats->rx_bytes, bytes);
+ } else {
+ u64_stats_add(&vstats->tx_packets, pkts);
+ u64_stats_add(&vstats->tx_bytes, bytes);
+ }
+ u64_stats_update_end(&vstats->syncp);
+ } else if (dev->tstats) {
+ tstats = this_cpu_ptr(dev->tstats);
+ u64_stats_update_begin(&tstats->syncp);
+ if (rx) {
+ u64_stats_add(&tstats->rx_packets, pkts);
+ u64_stats_add(&tstats->rx_bytes, bytes);
+ } else {
+ u64_stats_add(&tstats->tx_packets, pkts);
+ u64_stats_add(&tstats->tx_bytes, bytes);
+ }
+ u64_stats_update_end(&tstats->syncp);
+ }
+}
+
+/* Update sub-interface (VLAN, PPPoE) stats for hw-offloaded flows.
+ *
+ * The driver reports L3 (IP) bytes. Each sub-interface in the
+ * software path sees the frame with the headers of all layers
+ * BELOW it still present, so we add back inner-layer overhead.
+ *
+ * encap[] is ordered outermost to innermost, so walk from the
+ * innermost layer outward, accumulating overhead as we go.
+ */
+static void flow_offload_update_encap_stats(struct flow_offload *flow,
+ struct flow_offload_tuple *tuple,
+ bool rx, u64 pkts, u64 bytes)
+{
+ struct net_device *dev;
+ int inner_hlen = 0;
+ int i;
+
+ for (i = tuple->encap_num - 1; i >= 0; i--) {
+ if (tuple->in_vlan_ingress & BIT(i))
+ continue;
+
+ dev = dev_get_by_index_rcu(dev_net(flow->ct->ct_net),
+ tuple->encap_ifidx[i]);
+ if (dev)
+ flow_offload_encap_netstats(dev,
+ tuple->encap[i].proto, rx,
+ pkts,
+ bytes + inner_hlen * pkts);
+
+ inner_hlen += flow_offload_encap_hlen(tuple, i);
+ }
+
+ /* Bridge device sits outside all encap layers -- it sees
+ * L3 bytes plus the full encap overhead.
+ */
+ if (tuple->bridge_ifidx) {
+ dev = dev_get_by_index_rcu(dev_net(flow->ct->ct_net),
+ tuple->bridge_ifidx);
+ if (dev && dev->tstats)
+ flow_offload_encap_netstats(dev, 0, rx, pkts,
+ bytes + inner_hlen * pkts);
+ }
+}
+
+/* Compute per-direction input overhead from the encap and tunnel
+ * chains. Hardware flow counters report L2 frame bytes but
+ * conntrack expects L3 (inner IP) bytes -- matching what the
+ * software path sees after stripping all encap and tunnel headers.
+ */
+static int flow_offload_input_l2_overhead(struct flow_offload_tuple *tuple)
+{
+ int overhead = ETH_HLEN;
+ int i;
+
+ for (i = 0; i < tuple->encap_num; i++) {
+ if (tuple->in_vlan_ingress & BIT(i))
+ continue;
+
+ overhead += flow_offload_encap_hlen(tuple, i);
+ }
+
+ if (tuple->tun_num) {
+ switch (tuple->tun.l3_proto) {
+ case IPPROTO_IPIP:
+ overhead += sizeof(struct iphdr);
+ break;
+ case IPPROTO_IPV6:
+ overhead += sizeof(struct ipv6hdr);
+ break;
+ }
+ }
+
+ return overhead;
+}
+
static void flow_offload_work_stats(struct flow_offload_work *offload)
{
+ struct flow_offload_tuple *tuple;
struct flow_stats stats[FLOW_OFFLOAD_DIR_MAX] = {};
+ u64 l3_bytes[FLOW_OFFLOAD_DIR_MAX];
+ int l2_overhead;
u64 lastused;
+ int i;
flow_offload_tuple_stats(offload, FLOW_OFFLOAD_DIR_ORIGINAL, &stats[0]);
if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
@@ -1022,16 +1149,59 @@ static void flow_offload_work_stats(struct flow_offload_work *offload)
offload->flow->timeout = max_t(u64, offload->flow->timeout,
lastused + flow_offload_get_timeout(offload->flow));
+ /* Convert hardware byte counts to L3 based on what the driver
+ * reports. Drivers that already report L3 (or do not set
+ * byte_type) need no conversion.
+ */
+ for (i = 0; i < FLOW_OFFLOAD_DIR_MAX; i++) {
+ l2_overhead = 0;
+
+ switch (stats[i].byte_type) {
+ case FLOW_STATS_BYTES_INGRESS_L2:
+ tuple = &offload->flow->tuplehash[i].tuple;
+ l2_overhead = flow_offload_input_l2_overhead(tuple);
+ break;
+ case FLOW_STATS_BYTES_EGRESS_L2:
+ tuple = &offload->flow->tuplehash[!i].tuple;
+ l2_overhead = flow_offload_input_l2_overhead(tuple);
+ break;
+ default:
+ break;
+ }
+ l3_bytes[i] = stats[i].bytes - stats[i].pkts * l2_overhead;
+ }
+
if (offload->flowtable->flags & NF_FLOWTABLE_COUNTER) {
if (stats[0].pkts)
nf_ct_acct_add(offload->flow->ct,
FLOW_OFFLOAD_DIR_ORIGINAL,
- stats[0].pkts, stats[0].bytes);
+ stats[0].pkts, l3_bytes[0]);
if (stats[1].pkts)
nf_ct_acct_add(offload->flow->ct,
FLOW_OFFLOAD_DIR_REPLY,
- stats[1].pkts, stats[1].bytes);
+ stats[1].pkts, l3_bytes[1]);
+ }
+
+ rcu_read_lock();
+ for (i = 0; i < FLOW_OFFLOAD_DIR_MAX; i++) {
+ tuple = &offload->flow->tuplehash[i].tuple;
+ if (!tuple->encap_num)
+ continue;
+
+ /* Input-side encap devices get RX stats */
+ if (stats[i].pkts)
+ flow_offload_update_encap_stats(offload->flow,
+ tuple, true,
+ stats[i].pkts,
+ l3_bytes[i]);
+ /* Same devices get TX stats from the other direction */
+ if (stats[!i].pkts)
+ flow_offload_update_encap_stats(offload->flow,
+ tuple, false,
+ stats[!i].pkts,
+ l3_bytes[!i]);
}
+ rcu_read_unlock();
}
static void flow_offload_work_handler(struct work_struct *work)
--
2.53.0
^ permalink raw reply related
* [PATCH RFC net-next 2/4] nf_flow_table: track sub-interface and bridge ifindex in flow tuple
From: Daniel Golle @ 2026-04-09 13:07 UTC (permalink / raw)
To: Felix Fietkau, John Crispin, Lorenzo Bianconi, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Matthias Brugger, AngeloGioacchino Del Regno, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, netdev,
linux-kernel, linux-arm-kernel, linux-mediatek, netfilter-devel,
coreteam
In-Reply-To: <cover.1775739840.git.daniel@makrotopia.org>
Store the net_device ifindex alongside each encap entry and for
bridge devices during path discovery so the flow offload stats
path can later update sub-interface (VLAN, PPPoE, bridge)
counters for hw-offloaded flows.
The indices are placed below __hash so they do not affect flow
tuple lookups.
No functional change -- the indices are stored but not yet used.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
include/net/netfilter/nf_flow_table.h | 5 +++++
net/netfilter/nf_flow_table_core.c | 2 ++
net/netfilter/nf_flow_table_path.c | 8 ++++++++
3 files changed, 15 insertions(+)
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index b09c11c048d51..ec1a18cfd9621 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -148,6 +148,9 @@ struct flow_offload_tuple {
/* All members above are keys for lookups, see flow_offload_hash(). */
struct { } __hash;
+ int encap_ifidx[NF_FLOW_TABLE_ENCAP_MAX];
+ int bridge_ifidx;
+
u8 dir:2,
xmit_type:3,
encap_num:2,
@@ -221,11 +224,13 @@ struct nf_flow_route {
struct {
u16 id;
__be16 proto;
+ int ifindex;
} encap[NF_FLOW_TABLE_ENCAP_MAX];
struct flow_offload_tunnel tun;
u8 num_encaps:2,
num_tuns:2,
ingress_vlans:2;
+ int bridge_ifindex;
} in;
struct {
u32 ifindex;
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index 2c4140e6f53c5..9bc8be177b392 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -115,6 +115,7 @@ static int flow_offload_fill_route(struct flow_offload *flow,
for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) {
flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id;
flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto;
+ flow_tuple->encap_ifidx[j] = route->tuple[dir].in.encap[i].ifindex;
if (route->tuple[dir].in.ingress_vlans & BIT(i))
flow_tuple->in_vlan_ingress |= BIT(j);
j++;
@@ -123,6 +124,7 @@ static int flow_offload_fill_route(struct flow_offload *flow,
flow_tuple->tun = route->tuple[dir].in.tun;
flow_tuple->encap_num = route->tuple[dir].in.num_encaps;
flow_tuple->tun_num = route->tuple[dir].in.num_tuns;
+ flow_tuple->bridge_ifidx = route->tuple[dir].in.bridge_ifindex;
switch (route->tuple[dir].xmit_type) {
case FLOW_OFFLOAD_XMIT_DIRECT:
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index 6bb9579dcc2ab..c5817cb96a9f6 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -79,8 +79,10 @@ struct nft_forward_info {
struct id {
__u16 id;
__be16 proto;
+ int ifindex;
} encap[NF_FLOW_TABLE_ENCAP_MAX];
u8 num_encaps;
+ int bridge_ifindex;
struct flow_offload_tunnel tun;
u8 num_tuns;
u8 ingress_vlans;
@@ -136,12 +138,15 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack,
path->encap.id;
info->encap[info->num_encaps].proto =
path->encap.proto;
+ info->encap[info->num_encaps].ifindex =
+ path->dev->ifindex;
info->num_encaps++;
}
if (path->type == DEV_PATH_PPPOE)
memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN);
break;
case DEV_PATH_BRIDGE:
+ info->bridge_ifindex = path->dev->ifindex;
if (is_zero_ether_addr(info->h_source))
memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
@@ -156,6 +161,7 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack,
}
info->encap[info->num_encaps].id = path->bridge.vlan_id;
info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
+ info->encap[info->num_encaps].ifindex = path->dev->ifindex;
info->num_encaps++;
break;
case DEV_PATH_BR_VLAN_UNTAG:
@@ -261,6 +267,7 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt,
for (i = 0; i < info.num_encaps; i++) {
route->tuple[!dir].in.encap[i].id = info.encap[i].id;
route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
+ route->tuple[!dir].in.encap[i].ifindex = info.encap[i].ifindex;
}
if (info.num_tuns &&
@@ -273,6 +280,7 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt,
route->tuple[!dir].in.num_encaps = info.num_encaps;
route->tuple[!dir].in.ingress_vlans = info.ingress_vlans;
+ route->tuple[!dir].in.bridge_ifindex = info.bridge_ifindex;
if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
--
2.53.0
^ permalink raw reply related
* [PATCH RFC net-next 1/4] net: flow_offload: let drivers report byte counter semantics
From: Daniel Golle @ 2026-04-09 13:07 UTC (permalink / raw)
To: Felix Fietkau, John Crispin, Lorenzo Bianconi, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Matthias Brugger, AngeloGioacchino Del Regno, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, netdev,
linux-kernel, linux-arm-kernel, linux-mediatek, netfilter-devel,
coreteam
In-Reply-To: <cover.1775739840.git.daniel@makrotopia.org>
Hardware flow offload engines count bytes at different points --
some report ingress L2 frame bytes, others egress L2, others L3.
Add an enum so drivers can declare what their counters represent.
The framework can then convert to L3 as needed for conntrack.
Default is FLOW_STATS_BYTES_L3 (zero), preserving existing
behaviour for all current drivers.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
include/net/flow_offload.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h
index 70a02ee143080..7f5ef29b3abce 100644
--- a/include/net/flow_offload.h
+++ b/include/net/flow_offload.h
@@ -541,12 +541,19 @@ static inline bool flow_rule_match_has_control_flags(const struct flow_rule *rul
return flow_rule_has_control_flags(match.mask->flags, extack);
}
+enum flow_stats_byte_type {
+ FLOW_STATS_BYTES_L3 = 0, /* L3 (inner IP) bytes */
+ FLOW_STATS_BYTES_INGRESS_L2, /* full ingress L2 frame bytes */
+ FLOW_STATS_BYTES_EGRESS_L2, /* full egress L2 frame bytes */
+};
+
struct flow_stats {
u64 pkts;
u64 bytes;
u64 drops;
u64 lastused;
enum flow_action_hw_stats used_hw_stats;
+ enum flow_stats_byte_type byte_type;
bool used_hw_stats_valid;
};
--
2.53.0
^ permalink raw reply related
* [PATCH RFC net-next 0/4] improve hw flow offload byte accounting
From: Daniel Golle @ 2026-04-09 13:07 UTC (permalink / raw)
To: Felix Fietkau, John Crispin, Lorenzo Bianconi, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Matthias Brugger, AngeloGioacchino Del Regno, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, netdev,
linux-kernel, linux-arm-kernel, linux-mediatek, netfilter-devel,
coreteam
Hardware flow counters report raw byte counts whose semantics
vary by vendor -- some count ingress L2 frames, others egress
L2, others L3. The nf_flow_table framework currently passes
these bytes straight to conntrack without conversion, and
sub-interfaces (VLAN, PPPoE) that are bypassed by hw offload
never see any counter updates at all.
This series lets drivers declare what their counters represent,
so the framework can normalize to L3 for conntrack and
propagate per-layer stats to encap sub-interfaces.
Questions:
- Sub-interface stats accesses vlan_dev_priv() directly --
should there be a generic netdev callback instead?
- Are there hw offload drivers whose counters do not fit the
ingress-L2 / egress-L2 / L3 model?
Daniel Golle (4):
net: flow_offload: let drivers report byte counter semantics
nf_flow_table: track sub-interface and bridge ifindex in flow tuple
nf_flow_table: convert hw byte counts and update sub-interface stats
net: ethernet: mtk_eth_soc: report INGRESS_L2 byte_type in flow stats
.../net/ethernet/mediatek/mtk_ppe_offload.c | 1 +
include/net/flow_offload.h | 7 +
include/net/netfilter/nf_flow_table.h | 5 +
net/netfilter/nf_flow_table_core.c | 2 +
net/netfilter/nf_flow_table_offload.c | 174 +++++++++++++++++-
net/netfilter/nf_flow_table_path.c | 8 +
6 files changed, 195 insertions(+), 2 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH 0/5] uaccess/sockptr: copy_struct_ fixes and more helpers
From: Christian Brauner @ 2026-04-09 13:05 UTC (permalink / raw)
To: linux-kernel, Stefan Metzmacher
Cc: Christian Brauner, Dmitry Safonov, Francesco Ruggeri,
Salam Noureddine, David Ahern, David S . Miller, Michal Luczaj,
David Wei, Luiz Augusto von Dentz, Luiz Augusto von Dentz,
Marcel Holtmann, Xin Long, Eric Dumazet, Kuniyuki Iwashima,
Paolo Abeni, Willem de Bruijn, Neal Cardwell, Jakub Kicinski,
Simon Horman, Aleksa Sarai, netdev, linux-bluetooth, Kees Cook
In-Reply-To: <cover.1775576651.git.metze@samba.org>
On Tue, 07 Apr 2026 18:03:12 +0200, Stefan Metzmacher wrote:
> here are some patches related to
> copy_struct_{from,to}_{user,sockptr}()
> I collected during my work on an IPPROTO_SMBDIRECT
> implementation wrapping the smbdirect code used
> by cifs.ko and ksmbd.ko.
>
> The first patch fixes copy_struct_to_user()
> to behave like documented.
>
> [...]
Applied to the vfs-7.2.misc branch of the vfs/vfs.git tree.
Patches in the vfs-7.2.misc branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.
Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-7.2.misc
[1/5] uaccess: fix ignored_trailing logic in copy_struct_to_user()
https://git.kernel.org/vfs/vfs/c/0a8d67b47a5c
[2/5] sockptr: fix usize check in copy_struct_from_sockptr() for user pointers
https://git.kernel.org/vfs/vfs/c/e555ebefea0a
[3/5] uaccess: add copy_struct_{from,to}_bounce_buffer() helpers
https://git.kernel.org/vfs/vfs/c/14e710721cd3
[4/5] sockptr: let copy_struct_from_sockptr() use copy_struct_from_bounce_buffer()
https://git.kernel.org/vfs/vfs/c/62343ea38031
[5/5] sockptr: introduce copy_struct_to_sockptr()
https://git.kernel.org/vfs/vfs/c/9899f22cc41d
^ permalink raw reply
* [PATCH iwl-next v2 07/10] ice: create ACL entry
From: Marcin Szycik @ 2026-04-09 12:00 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Chinh Cao, Tony Nguyen, Aleksandr Loktionov
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
From: Real Valiquette <real.valiquette@intel.com>
Create an ACL entry for the mask match data and set the desired action.
Generate and program the associated extraction sequence.
Co-developed-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Real Valiquette <real.valiquette@intel.com>
Co-developed-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Co-developed-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Co-developed-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
v2:
* Fix invalid profile ID passed to ice_flow_add_entry() in
ice_acl_add_rule_ethtool()
* Fix uninitialized cntrs.amount field in ice_aq_dealloc_acl_cntrs()
* Make ice_flow_acl_is_prof_in_use() more readable and return bool
* Add ice_flow_acl_is_cntr_act() helper
* Remove prof_id initialization when it's immediately set by
ice_flow_get_hw_prof() anyway
* Check if src overflows in ice_flow_acl_set_xtrct_seq_fld()
* Adjust error codes in ice_flow_acl_check_actions() to more reasonable
ones
* Add ICE_RX_PKT_DROP_DROP instead of using a magic number
* Reverse condition to decrease indent level in ice_aq_alloc_acl_cntrs()
* Get rid of useless variable in ice_acl_add_rule_ethtool()
* Use plain alloc and kfree instead of devm_ for ice_ntuple_fltr in
ice_acl_add_rule_ethtool(), ice_flow_entry::entry and
ice_flow_entry::range_buf
* Use plain kmemdup and kfree instead of devm_ for ice_flow_entry::acts
* ice_flow_entry members are being deallocated on device unload via
ice_deinit_fdir -> ice_vsi_manage_fdir -> ice_fdir_rem_flow ->
ice_fdir_erase_flow_from_hw -> ice_flow_rem_entry ->
ice_flow_rem_entry_sync
* Add missing entry->range_buf and entry->acts dealloc in
ice_flow_add_entry() unroll
* Remove redundant checks from ice_flow_acl_frmt_entry() unroll
---
drivers/net/ethernet/intel/ice/ice.h | 3 +
drivers/net/ethernet/intel/ice/ice_acl.h | 24 +
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 123 +++-
.../net/ethernet/intel/ice/ice_flex_pipe.h | 2 +
drivers/net/ethernet/intel/ice/ice_flow.h | 9 +-
.../net/ethernet/intel/ice/ice_lan_tx_rx.h | 3 +
drivers/net/ethernet/intel/ice/ice_acl.c | 183 +++++
drivers/net/ethernet/intel/ice/ice_acl_main.c | 62 +-
.../ethernet/intel/ice/ice_ethtool_ntuple.c | 37 +-
.../net/ethernet/intel/ice/ice_flex_pipe.c | 5 +-
drivers/net/ethernet/intel/ice/ice_flow.c | 626 +++++++++++++++++-
drivers/net/ethernet/intel/ice/ice_main.c | 2 +-
drivers/net/ethernet/intel/ice/virt/fdir.c | 4 +-
13 files changed, 1044 insertions(+), 39 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index d10e67d8bf02..9e6643931022 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -1025,6 +1025,9 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
int ice_del_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
int ice_get_ethtool_fdir_entry(struct ice_hw *hw, struct ethtool_rxnfc *cmd);
u32 ice_ntuple_get_max_fltr_cnt(struct ice_hw *hw);
+int ice_ntuple_set_input_set(struct ice_vsi *vsi, enum ice_block blk,
+ struct ethtool_rx_flow_spec *fsp,
+ struct ice_ntuple_fltr *input);
int ice_ntuple_l4_proto_to_port(enum ice_flow_seg_hdr l4_proto,
enum ice_flow_field *src_port,
enum ice_flow_field *dst_port);
diff --git a/drivers/net/ethernet/intel/ice/ice_acl.h b/drivers/net/ethernet/intel/ice/ice_acl.h
index d4e6f0e25a12..3a4adcf368cf 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl.h
+++ b/drivers/net/ethernet/intel/ice/ice_acl.h
@@ -99,6 +99,20 @@ struct ice_acl_alloc_tbl {
} buf;
};
+/* Input and output params for [de]allocate_acl_counters */
+struct ice_acl_cntrs {
+ u8 amount;
+ u8 type;
+ u8 bank;
+
+ /* first/last:
+ * Output in case of alloc_acl_counters
+ * Input in case of deallocate_acl_counters
+ */
+ u16 first_cntr;
+ u16 last_cntr;
+};
+
int ice_acl_create_tbl(struct ice_hw *hw, struct ice_acl_tbl_params *params);
int ice_acl_destroy_tbl(struct ice_hw *hw);
int ice_acl_create_scen(struct ice_hw *hw, u16 match_width, u16 num_entries,
@@ -113,6 +127,16 @@ int ice_aq_program_acl_entry(struct ice_hw *hw, u8 tcam_idx, u16 entry_idx,
struct ice_sq_cd *cd);
int ice_aq_program_actpair(struct ice_hw *hw, u8 act_mem_idx, u16 act_entry_idx,
struct ice_aqc_actpair *buf, struct ice_sq_cd *cd);
+int ice_prgm_acl_prof_xtrct(struct ice_hw *hw, u8 prof_id,
+ struct ice_aqc_acl_prof_generic_frmt *buf,
+ struct ice_sq_cd *cd);
+int ice_query_acl_prof(struct ice_hw *hw, u8 prof_id,
+ struct ice_aqc_acl_prof_generic_frmt *buf,
+ struct ice_sq_cd *cd);
+int ice_aq_alloc_acl_cntrs(struct ice_hw *hw, struct ice_acl_cntrs *cntrs,
+ struct ice_sq_cd *cd);
+int ice_aq_dealloc_acl_cntrs(struct ice_hw *hw, struct ice_acl_cntrs *cntrs,
+ struct ice_sq_cd *cd);
int ice_aq_alloc_acl_scen(struct ice_hw *hw, u16 *scen_id,
struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd);
int ice_aq_dealloc_acl_scen(struct ice_hw *hw, u16 scen_id,
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 1a32400e70bd..b494fa6e0943 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -2150,6 +2150,67 @@ struct ice_aqc_acl_scen {
u8 act_mem_cfg[ICE_AQC_MAX_ACTION_MEMORIES];
};
+/* Allocate ACL counters (indirect 0x0C16) */
+struct ice_aqc_acl_alloc_counters {
+ /* Amount of contiguous counters requested. Min value is 1 and
+ * max value is 255
+ */
+ u8 counter_amount;
+
+ /* Counter type: 'single counter' which can be configured to count
+ * either bytes or packets
+ */
+#define ICE_AQC_ACL_CNT_TYPE_SINGLE 0x0
+
+ /* Counter type: 'counter pair' which counts number of bytes and number
+ * of packets.
+ */
+#define ICE_AQC_ACL_CNT_TYPE_DUAL 0x1
+ /* requested counter type, single/dual */
+ u8 counters_type;
+
+ /* counter bank allocation shall be 0-3 for 'byte or packet counter' */
+#define ICE_AQC_ACL_MAX_CNT_SINGLE 0x3
+ /* counter bank allocation shall be 0-1 for 'byte and packet counter
+ * dual'
+ */
+#define ICE_AQC_ACL_MAX_CNT_DUAL 0x1
+ /* requested counter bank allocation */
+ u8 bank_alloc;
+
+ u8 reserved;
+
+ union {
+ /* Applicable only in case of command */
+ struct {
+ u8 reserved[12];
+ } cmd;
+ /* Applicable only in case of response */
+#define ICE_AQC_ACL_ALLOC_CNT_INVAL 0xFFFF
+ struct {
+ /* Index of first allocated counter. 0xFFFF in case
+ * of unsuccessful allocation
+ */
+ __le16 first_counter;
+ /* Index of last allocated counter. 0xFFFF in case
+ * of unsuccessful allocation
+ */
+ __le16 last_counter;
+ u8 rsvd[8];
+ } resp;
+ } ops;
+};
+
+/* De-allocate ACL counters (direct 0x0C17) */
+struct ice_aqc_acl_dealloc_counters {
+ __le16 first_counter;
+ __le16 last_counter;
+ /* single/dual */
+ u8 counters_type;
+ u8 bank_alloc;
+ u8 reserved[10];
+};
+
/* Program ACL actionpair (indirect 0x0C1C) */
struct ice_aqc_acl_actpair {
u8 act_mem_index;
@@ -2161,6 +2222,8 @@ struct ice_aqc_acl_actpair {
__le32 addr_low;
};
+#define ICE_RX_PKT_DROP_DROP 0x1
+
/* Input buffer format for program/query action-pair admin command */
struct ice_acl_act_entry {
/* Action priority, values must be between 0..7 */
@@ -2177,13 +2240,59 @@ struct ice_aqc_actpair {
struct ice_acl_act_entry act[ICE_ACL_NUM_ACT_PER_ACT_PAIR];
};
-/* The first byte of the byte selection base is reserved to keep the
- * first byte of the field vector where the packet direction info is
- * available. Thus we should start at index 1 of the field vector to
- * map its entries to the byte selection base.
- */
+ /* The first byte of the byte selection base is reserved to keep the
+ * first byte of the field vector where the packet direction info is
+ * available. Thus we should start at index 1 of the field vector to
+ * map its entries to the byte selection base.
+ */
#define ICE_AQC_ACL_PROF_BYTE_SEL_START_IDX 1
+
#define ICE_AQC_ACL_PROF_BYTE_SEL_ELEMS 30
+#define ICE_AQC_ACL_PROF_WORD_SEL_ELEMS 32
+#define ICE_AQC_ACL_PROF_DWORD_SEL_ELEMS 15
+#define ICE_AQC_ACL_PROF_PF_SCEN_NUM_ELEMS 8
+
+/* Generic format used to describe either input or response buffer
+ * for admin commands related to ACL profile
+ */
+struct ice_aqc_acl_prof_generic_frmt {
+ /* In each byte:
+ * Bit 0..5 = Byte selection for the byte selection base from the
+ * extracted fields (expressed as byte offset in extracted fields).
+ * Applicable values are 0..63
+ * Bit 6..7 = Reserved
+ */
+ u8 byte_selection[ICE_AQC_ACL_PROF_BYTE_SEL_ELEMS];
+ /* In each byte:
+ * Bit 0..4 = Word selection for the word selection base from the
+ * extracted fields (expressed as word offset in extracted fields).
+ * Applicable values are 0..31
+ * Bit 5..7 = Reserved
+ */
+ u8 word_selection[ICE_AQC_ACL_PROF_WORD_SEL_ELEMS];
+ /* In each byte:
+ * Bit 0..3 = Double word selection for the double-word selection base
+ * from the extracted fields (expressed as double-word offset in
+ * extracted fields).
+ * Applicable values are 0..15
+ * Bit 4..7 = Reserved
+ */
+ u8 dword_selection[ICE_AQC_ACL_PROF_DWORD_SEL_ELEMS];
+ /* Scenario numbers for individual Physical Function's */
+ u8 pf_scenario_num[ICE_AQC_ACL_PROF_PF_SCEN_NUM_ELEMS];
+};
+
+/* Program ACL profile extraction (indirect 0x0C1D)
+ * Program ACL profile ranges (indirect 0x0C1E)
+ * Query ACL profile (indirect 0x0C21)
+ * Query ACL profile ranges (indirect 0x0C22)
+ */
+struct ice_aqc_acl_profile {
+ u8 profile_id; /* Programmed/Updated profile ID */
+ u8 reserved[7];
+ __le32 addr_high;
+ __le32 addr_low;
+};
/* Input buffer format for program profile extraction admin command and
* response buffer format for query profile admin command is as defined
@@ -2918,9 +3027,13 @@ enum ice_adminq_opc {
ice_aqc_opc_dealloc_acl_tbl = 0x0C11,
ice_aqc_opc_alloc_acl_scen = 0x0C14,
ice_aqc_opc_dealloc_acl_scen = 0x0C15,
+ ice_aqc_opc_alloc_acl_counters = 0x0C16,
+ ice_aqc_opc_dealloc_acl_counters = 0x0C17,
ice_aqc_opc_update_acl_scen = 0x0C1B,
ice_aqc_opc_program_acl_actpair = 0x0C1C,
+ ice_aqc_opc_program_acl_prof_extraction = 0x0C1D,
ice_aqc_opc_program_acl_entry = 0x0C20,
+ ice_aqc_opc_query_acl_prof = 0x0C21,
ice_aqc_opc_query_acl_scen = 0x0C23,
/* Tx queue handling commands/events */
diff --git a/drivers/net/ethernet/intel/ice/ice_flex_pipe.h b/drivers/net/ethernet/intel/ice/ice_flex_pipe.h
index ee5d9f9c9d53..edb98afe200b 100644
--- a/drivers/net/ethernet/intel/ice/ice_flex_pipe.h
+++ b/drivers/net/ethernet/intel/ice/ice_flex_pipe.h
@@ -8,6 +8,8 @@
#define ICE_FDIR_REG_SET_SIZE 4
+int ice_set_key(u8 *key, u16 size, u8 *val, u8 *upd, u8 *dc, u8 *nm, u16 off,
+ u16 len);
int
ice_acquire_change_lock(struct ice_hw *hw, enum ice_aq_res_access_type access);
void ice_release_change_lock(struct ice_hw *hw);
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.h b/drivers/net/ethernet/intel/ice/ice_flow.h
index ff6af6589862..53456d48f6ae 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.h
+++ b/drivers/net/ethernet/intel/ice/ice_flow.h
@@ -452,17 +452,23 @@ struct ice_flow_seg_info {
struct ice_flow_seg_fld_raw raws[ICE_FLOW_SEG_RAW_FLD_MAX];
};
+#define ICE_FLOW_ACL_MAX_NUM_ACT 2
/* This structure describes a flow entry, and is tracked only in this file */
struct ice_flow_entry {
struct list_head l_entry;
u64 id;
struct ice_flow_prof *prof;
+ /* Action list */
+ struct ice_flow_action *acts;
/* Flow entry's content */
void *entry;
+ /* Range buffer (For ACL only) */
+ struct ice_aqc_acl_profile_ranges *range_buf;
enum ice_flow_priority priority;
u16 vsi_handle;
u16 entry_sz;
+ u8 acts_cnt;
};
#define ICE_FLOW_ENTRY_HNDL(e) ((u64)(uintptr_t)e)
@@ -535,7 +541,8 @@ ice_flow_set_parser_prof(struct ice_hw *hw, u16 dest_vsi, u16 fdir_vsi,
int
ice_flow_add_entry(struct ice_hw *hw, enum ice_block blk, u64 prof_id,
u64 entry_id, u16 vsi, enum ice_flow_priority prio,
- void *data, u64 *entry_h);
+ void *data, struct ice_flow_action *acts, u8 acts_cnt,
+ u64 *entry_h);
int ice_flow_rem_entry(struct ice_hw *hw, enum ice_block blk, u64 entry_h);
void
ice_flow_set_fld(struct ice_flow_seg_info *seg, enum ice_flow_field fld,
diff --git a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
index 185672c7e17d..7010afb787c3 100644
--- a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
+++ b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
@@ -312,6 +312,8 @@ enum ice_flex_mdid_pkt_flags {
enum ice_flex_rx_mdid {
ICE_RX_MDID_FLOW_ID_LOWER = 5,
ICE_RX_MDID_FLOW_ID_HIGH,
+ ICE_MDID_RX_PKT_DROP = 8,
+ ICE_MDID_RX_DST_Q = 12,
ICE_RX_MDID_SRC_VSI = 19,
ICE_RX_MDID_HASH_LOW = 56,
ICE_RX_MDID_HASH_HIGH,
@@ -320,6 +322,7 @@ enum ice_flex_rx_mdid {
/* Rx/Tx Flag64 packet flag bits */
enum ice_flg64_bits {
ICE_FLG_PKT_DSI = 0,
+ ICE_FLG_PKT_DIR = 4,
ICE_FLG_EVLAN_x8100 = 14,
ICE_FLG_EVLAN_x9100,
ICE_FLG_VLAN_x8100,
diff --git a/drivers/net/ethernet/intel/ice/ice_acl.c b/drivers/net/ethernet/intel/ice/ice_acl.c
index 81bddac8d0a2..837adbda14e0 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl.c
+++ b/drivers/net/ethernet/intel/ice/ice_acl.c
@@ -135,6 +135,189 @@ int ice_aq_program_actpair(struct ice_hw *hw, u8 act_mem_idx, u16 act_entry_idx,
return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
}
+/**
+ * ice_acl_prof_aq_send - send ACL profile AQ commands
+ * @hw: pointer to the HW struct
+ * @opc: command opcode
+ * @prof_id: profile ID
+ * @buf: ptr to buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_acl_prof_aq_send(struct ice_hw *hw, u16 opc, u8 prof_id,
+ struct ice_aqc_acl_prof_generic_frmt *buf,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_profile *cmd;
+ struct libie_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, opc);
+ cmd = libie_aq_raw(&desc);
+ cmd->profile_id = prof_id;
+
+ if (opc == ice_aqc_opc_program_acl_prof_extraction)
+ desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD);
+
+ return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
+}
+
+/**
+ * ice_prgm_acl_prof_xtrct - program ACL profile extraction sequence
+ * @hw: pointer to the HW struct
+ * @prof_id: profile ID
+ * @buf: ptr to buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Program ACL profile extraction (indirect 0x0C1D)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_prgm_acl_prof_xtrct(struct ice_hw *hw, u8 prof_id,
+ struct ice_aqc_acl_prof_generic_frmt *buf,
+ struct ice_sq_cd *cd)
+{
+ return ice_acl_prof_aq_send(hw, ice_aqc_opc_program_acl_prof_extraction,
+ prof_id, buf, cd);
+}
+
+/**
+ * ice_query_acl_prof - query ACL profile
+ * @hw: pointer to the HW struct
+ * @prof_id: profile ID
+ * @buf: ptr to buffer (which will contain response of this command)
+ * @cd: pointer to command details structure or NULL
+ *
+ * Query ACL profile (indirect 0x0C21)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_query_acl_prof(struct ice_hw *hw, u8 prof_id,
+ struct ice_aqc_acl_prof_generic_frmt *buf,
+ struct ice_sq_cd *cd)
+{
+ return ice_acl_prof_aq_send(hw, ice_aqc_opc_query_acl_prof, prof_id,
+ buf, cd);
+}
+
+/**
+ * ice_aq_acl_cntrs_chk_params - Checks ACL counter parameters
+ * @cntrs: ptr to buffer describing input and output params
+ *
+ * This function checks the counter bank range for counter type and returns
+ * success or failure.
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_aq_acl_cntrs_chk_params(struct ice_acl_cntrs *cntrs)
+{
+ int err = 0;
+
+ if (!cntrs->amount)
+ return -EINVAL;
+
+ switch (cntrs->type) {
+ case ICE_AQC_ACL_CNT_TYPE_SINGLE:
+ /* Single counter type - configured to count either bytes
+ * or packets, the valid values for byte or packet counters
+ * shall be 0-3.
+ */
+ if (cntrs->bank > ICE_AQC_ACL_MAX_CNT_SINGLE)
+ err = -EIO;
+ break;
+ case ICE_AQC_ACL_CNT_TYPE_DUAL:
+ /* Pair counter type - counts number of bytes and packets
+ * The valid values for byte/packet counter duals shall be 0-1
+ */
+ if (cntrs->bank > ICE_AQC_ACL_MAX_CNT_DUAL)
+ err = -EIO;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+/**
+ * ice_aq_alloc_acl_cntrs - allocate ACL counters
+ * @hw: pointer to the HW struct
+ * @cntrs: ptr to buffer describing input and output params
+ * @cd: pointer to command details structure or NULL
+ *
+ * Allocate ACL counters (indirect 0x0C16). This function attempts to
+ * allocate a contiguous block of counters. In case of failures, caller can
+ * attempt to allocate a smaller chunk. The allocation is considered
+ * unsuccessful if returned counter value is invalid. In this case it returns
+ * an error otherwise success.
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_alloc_acl_cntrs(struct ice_hw *hw, struct ice_acl_cntrs *cntrs,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_alloc_counters *cmd;
+ u16 first_cntr, last_cntr;
+ struct libie_aq_desc desc;
+ int err;
+
+ err = ice_aq_acl_cntrs_chk_params(cntrs);
+ if (err)
+ return err;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_alloc_acl_counters);
+ cmd = libie_aq_raw(&desc);
+ cmd->counter_amount = cntrs->amount;
+ cmd->counters_type = cntrs->type;
+ cmd->bank_alloc = cntrs->bank;
+
+ err = ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+ if (err)
+ return err;
+
+ first_cntr = le16_to_cpu(cmd->ops.resp.first_counter);
+ last_cntr = le16_to_cpu(cmd->ops.resp.last_counter);
+
+ if (first_cntr == ICE_AQC_ACL_ALLOC_CNT_INVAL ||
+ last_cntr == ICE_AQC_ACL_ALLOC_CNT_INVAL)
+ return -EIO;
+
+ cntrs->first_cntr = first_cntr;
+ cntrs->last_cntr = last_cntr;
+
+ return 0;
+}
+
+/**
+ * ice_aq_dealloc_acl_cntrs - deallocate ACL counters
+ * @hw: pointer to the HW struct
+ * @cntrs: ptr to buffer describing input and output params
+ * @cd: pointer to command details structure or NULL
+ *
+ * De-allocate ACL counters (direct 0x0C17)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_dealloc_acl_cntrs(struct ice_hw *hw, struct ice_acl_cntrs *cntrs,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_dealloc_counters *cmd;
+ struct libie_aq_desc desc;
+ int err;
+
+ err = ice_aq_acl_cntrs_chk_params(cntrs);
+ if (err)
+ return err;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_dealloc_acl_counters);
+ cmd = libie_aq_raw(&desc);
+ cmd->first_counter = cpu_to_le16(cntrs->first_cntr);
+ cmd->last_counter = cpu_to_le16(cntrs->last_cntr);
+ cmd->counters_type = cntrs->type;
+ cmd->bank_alloc = cntrs->bank;
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+}
+
/**
* ice_aq_alloc_acl_scen - allocate ACL scenario
* @hw: pointer to the HW struct
diff --git a/drivers/net/ethernet/intel/ice/ice_acl_main.c b/drivers/net/ethernet/intel/ice/ice_acl_main.c
index 841e4d567ff2..53cca0526756 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_acl_main.c
@@ -5,6 +5,9 @@
#include "ice_lib.h"
#include "ice_acl_main.h"
+/* Default ACL Action priority */
+#define ICE_ACL_ACT_PRIO 3
+
/* Number of action */
#define ICE_ACL_NUM_ACT 1
@@ -218,12 +221,69 @@ static int ice_acl_prof_add_ethtool(struct ice_pf *pf,
*/
int ice_acl_add_rule_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
{
+ struct ice_flow_action acts[ICE_ACL_NUM_ACT];
struct ethtool_rx_flow_spec *fsp;
+ struct ice_fd_hw_prof *hw_prof;
+ struct ice_ntuple_fltr *input;
+ enum ice_fltr_ptype flow;
+ struct device *dev;
struct ice_pf *pf;
+ struct ice_hw *hw;
+ u64 entry_h = 0;
+ int err;
pf = vsi->back;
+ hw = &pf->hw;
+ dev = ice_pf_to_dev(pf);
fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
- return ice_acl_prof_add_ethtool(pf, fsp);
+ err = ice_acl_prof_add_ethtool(pf, fsp);
+ if (err)
+ return err;
+
+ /* Add new rule */
+ input = kzalloc_obj(*input);
+ if (!input)
+ return -ENOMEM;
+
+ err = ice_ntuple_set_input_set(vsi, ICE_BLK_ACL, fsp, input);
+ if (err)
+ goto free_input;
+
+ memset(&acts, 0, sizeof(acts));
+ if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
+ acts[0].type = ICE_FLOW_ACT_DROP;
+ acts[0].data.acl_act.mdid = ICE_MDID_RX_PKT_DROP;
+ acts[0].data.acl_act.prio = ICE_ACL_ACT_PRIO;
+ acts[0].data.acl_act.value = cpu_to_le16(ICE_RX_PKT_DROP_DROP);
+ } else {
+ acts[0].type = ICE_FLOW_ACT_FWD_QUEUE;
+ acts[0].data.acl_act.mdid = ICE_MDID_RX_DST_Q;
+ acts[0].data.acl_act.prio = ICE_ACL_ACT_PRIO;
+ acts[0].data.acl_act.value = cpu_to_le16(input->q_index);
+ }
+
+ flow = ice_ethtool_flow_to_fltr(fsp->flow_type & ~FLOW_EXT);
+ hw_prof = hw->acl_prof[flow];
+
+ err = ice_flow_add_entry(hw, ICE_BLK_ACL, hw_prof->prof_id[0],
+ fsp->location, vsi->idx, ICE_FLOW_PRIO_NORMAL,
+ input, acts, ICE_ACL_NUM_ACT, &entry_h);
+ if (err) {
+ dev_err(dev, "Could not add flow entry %d\n", flow);
+ goto free_input;
+ }
+
+ if (!hw_prof->cnt || vsi->idx != hw_prof->vsi_h[hw_prof->cnt - 1]) {
+ hw_prof->vsi_h[hw_prof->cnt] = vsi->idx;
+ hw_prof->entry_h[hw_prof->cnt++][0] = entry_h;
+ }
+
+ return 0;
+
+free_input:
+ kfree(input);
+
+ return err;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
index b5a841732b58..3e79c0bf40f4 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
@@ -486,7 +486,7 @@ void ice_fdir_replay_flows(struct ice_hw *hw)
prof->vsi_h[0],
prof->vsi_h[j],
prio, prof->fdir_seg,
- &entry_h);
+ NULL, 0, &entry_h);
if (err) {
dev_err(ice_hw_to_dev(hw), "Could not replay Flow Director, flow type %d\n",
flow);
@@ -719,12 +719,12 @@ ice_fdir_set_hw_fltr_rule(struct ice_pf *pf, struct ice_flow_seg_info *seg,
return err;
err = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id, main_vsi->idx,
main_vsi->idx, ICE_FLOW_PRIO_NORMAL,
- seg, &entry1_h);
+ seg, NULL, 0, &entry1_h);
if (err)
goto err_prof;
err = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id, main_vsi->idx,
ctrl_vsi->idx, ICE_FLOW_PRIO_NORMAL,
- seg, &entry2_h);
+ seg, NULL, 0, &entry2_h);
if (err)
goto err_entry;
@@ -748,7 +748,7 @@ ice_fdir_set_hw_fltr_rule(struct ice_pf *pf, struct ice_flow_seg_info *seg,
vsi_h = main_vsi->tc_map_vsi[idx]->idx;
err = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id,
main_vsi->idx, vsi_h,
- ICE_FLOW_PRIO_NORMAL, seg,
+ ICE_FLOW_PRIO_NORMAL, seg, NULL, 0,
&entry1_h);
if (err) {
dev_err(dev, "Could not add Channel VSI %d to flow group\n",
@@ -1988,28 +1988,36 @@ ice_update_ring_dest_vsi(struct ice_vsi *vsi, u16 *dest_vsi, u32 *ring)
}
/**
- * ice_ntuple_set_input_set - Set the input set for Flow Director
+ * ice_ntuple_set_input_set - Set the input set for specified block
* @vsi: pointer to target VSI
+ * @blk: filter block to configure
* @fsp: pointer to ethtool Rx flow specification
* @input: filter structure
*
* Return: 0 on success, negative on failure
*/
-static int
-ice_ntuple_set_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
- struct ice_ntuple_fltr *input)
+int ice_ntuple_set_input_set(struct ice_vsi *vsi, enum ice_block blk,
+ struct ethtool_rx_flow_spec *fsp,
+ struct ice_ntuple_fltr *input)
{
s16 q_index = ICE_FDIR_NO_QUEUE_IDX;
+ int flow_type, flow_mask;
u16 orig_q_index = 0;
struct ice_pf *pf;
struct ice_hw *hw;
- int flow_type;
u16 dest_vsi;
u8 dest_ctl;
if (!vsi || !fsp || !input)
return -EINVAL;
+ if (blk == ICE_BLK_FD)
+ flow_mask = FLOW_EXT;
+ else if (blk == ICE_BLK_ACL)
+ flow_mask = FLOW_MAC_EXT;
+ else
+ return -EINVAL;
+
pf = vsi->back;
hw = &pf->hw;
@@ -2021,7 +2029,8 @@ ice_ntuple_set_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
u8 vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie);
if (vf) {
- dev_err(ice_pf_to_dev(pf), "Failed to add filter. Flow director filters are not supported on VF queues.\n");
+ dev_err(ice_pf_to_dev(pf), "Failed to add filter. %s filters are not supported on VF queues.\n",
+ blk == ICE_BLK_FD ? "Flow Director" : "ACL");
return -EINVAL;
}
@@ -2036,7 +2045,7 @@ ice_ntuple_set_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
input->fltr_id = fsp->location;
input->q_index = q_index;
- flow_type = fsp->flow_type & ~FLOW_EXT;
+ flow_type = fsp->flow_type & ~flow_mask;
/* Record the original queue index as specified by user.
* with channel configuration 'q_index' becomes relative
@@ -2090,9 +2099,9 @@ ice_ntuple_set_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
case TCP_V6_FLOW:
case UDP_V6_FLOW:
case SCTP_V6_FLOW:
- memcpy(input->ip.v6.dst_ip, fsp->h_u.usr_ip6_spec.ip6dst,
+ memcpy(input->ip.v6.dst_ip, fsp->h_u.tcp_ip6_spec.ip6dst,
sizeof(struct in6_addr));
- memcpy(input->ip.v6.src_ip, fsp->h_u.usr_ip6_spec.ip6src,
+ memcpy(input->ip.v6.src_ip, fsp->h_u.tcp_ip6_spec.ip6src,
sizeof(struct in6_addr));
input->ip.v6.dst_port = fsp->h_u.tcp_ip6_spec.pdst;
input->ip.v6.src_port = fsp->h_u.tcp_ip6_spec.psrc;
@@ -2210,7 +2219,7 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
if (!input)
return -ENOMEM;
- ret = ice_ntuple_set_input_set(vsi, fsp, input);
+ ret = ice_ntuple_set_input_set(vsi, ICE_BLK_FD, fsp, input);
if (ret)
goto free_input;
diff --git a/drivers/net/ethernet/intel/ice/ice_flex_pipe.c b/drivers/net/ethernet/intel/ice/ice_flex_pipe.c
index d255ffcd5c86..92289b97117a 100644
--- a/drivers/net/ethernet/intel/ice/ice_flex_pipe.c
+++ b/drivers/net/ethernet/intel/ice/ice_flex_pipe.c
@@ -235,9 +235,8 @@ static bool ice_bits_max_set(const u8 *mask, u16 size, u16 max)
* dc == NULL --> dc mask is all 0's (no don't care bits)
* nm == NULL --> nm mask is all 0's (no never match bits)
*/
-static int
-ice_set_key(u8 *key, u16 size, u8 *val, u8 *upd, u8 *dc, u8 *nm, u16 off,
- u16 len)
+int ice_set_key(u8 *key, u16 size, u8 *val, u8 *upd, u8 *dc, u8 *nm, u16 off,
+ u16 len)
{
u16 half_size;
u16 i;
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.c b/drivers/net/ethernet/intel/ice/ice_flow.c
index 440e9fdb6b5b..dce6d2ffcb15 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.c
+++ b/drivers/net/ethernet/intel/ice/ice_flow.c
@@ -1589,22 +1589,171 @@ ice_flow_find_prof_id(struct ice_hw *hw, enum ice_block blk, u64 prof_id)
return NULL;
}
+/**
+ * ice_flow_get_hw_prof - return the HW profile for a specific profile ID handle
+ * @hw: pointer to the HW struct
+ * @blk: classification stage
+ * @prof_id: the profile ID handle
+ * @hw_prof_id: pointer to variable to return the HW profile ID
+ *
+ * Return: 0 on success, negative on failure
+ */
+static int ice_flow_get_hw_prof(struct ice_hw *hw, enum ice_block blk,
+ u64 prof_id, u8 *hw_prof_id)
+{
+ struct ice_prof_map *map;
+ int err = -ENOENT;
+
+ mutex_lock(&hw->blk[blk].es.prof_map_lock);
+
+ map = ice_search_prof_id(hw, blk, prof_id);
+ if (map) {
+ *hw_prof_id = map->prof_id;
+ err = 0;
+ }
+
+ mutex_unlock(&hw->blk[blk].es.prof_map_lock);
+
+ return err;
+}
+
+#define ICE_ACL_INVALID_SCEN 0x3f
+
+/**
+ * ice_flow_acl_is_prof_in_use - verify if the profile is associated to any PF
+ * @buf: ACL profile buffer
+ *
+ * Return: true if at least one PF is associated to the given profile
+ */
+static bool
+ice_flow_acl_is_prof_in_use(const struct ice_aqc_acl_prof_generic_frmt *buf)
+{
+ u8 first = buf->pf_scenario_num[0];
+
+ /* If all PF's associated scenarios are all 0 or all
+ * ICE_ACL_INVALID_SCEN for the given profile, then the profile has not
+ * been configured yet.
+ */
+
+ if (first != 0 && first != ICE_ACL_INVALID_SCEN)
+ return true;
+
+ for (int i = 1; i < ICE_AQC_ACL_PROF_PF_SCEN_NUM_ELEMS; i++) {
+ if (buf->pf_scenario_num[i] != first)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * ice_flow_acl_is_cntr_act - check if flow action is a counter action
+ * @type: action type
+ *
+ * Return: true if counter action, false otherwise
+ */
+static bool ice_flow_acl_is_cntr_act(enum ice_flow_action_type type)
+{
+ return type == ICE_FLOW_ACT_CNTR_PKT ||
+ type == ICE_FLOW_ACT_CNTR_BYTES ||
+ type == ICE_FLOW_ACT_CNTR_PKT_BYTES;
+}
+
+/**
+ * ice_flow_acl_free_act_cntr - Free the ACL rule's actions
+ * @hw: pointer to the hardware structure
+ * @acts: array of actions to be performed on a match
+ * @acts_cnt: number of actions
+ *
+ * Return: 0 on success, negative on failure
+ */
+static int ice_flow_acl_free_act_cntr(struct ice_hw *hw,
+ struct ice_flow_action *acts, u8 acts_cnt)
+{
+ for (int i = 0; i < acts_cnt; i++) {
+ if (ice_flow_acl_is_cntr_act(acts[i].type)) {
+ struct ice_acl_cntrs cntrs = { 0 };
+ int err;
+
+ /* amount is unused in the dealloc path but the common
+ * parameter check routine wants a value set, as zero
+ * is invalid for the check. Just set it.
+ */
+ cntrs.amount = 1;
+ cntrs.bank = 0; /* Only bank0 for the moment */
+ cntrs.first_cntr =
+ le16_to_cpu(acts[i].data.acl_act.value);
+ cntrs.last_cntr =
+ le16_to_cpu(acts[i].data.acl_act.value);
+
+ if (acts[i].type == ICE_FLOW_ACT_CNTR_PKT_BYTES)
+ cntrs.type = ICE_AQC_ACL_CNT_TYPE_DUAL;
+ else
+ cntrs.type = ICE_AQC_ACL_CNT_TYPE_SINGLE;
+
+ err = ice_aq_dealloc_acl_cntrs(hw, &cntrs, NULL);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_flow_acl_disassoc_scen - Disassociate the scenario from the profile
+ * @hw: pointer to the hardware structure
+ * @prof: pointer to flow profile
+ *
+ * Disassociate the scenario from the profile for the PF of the VSI.
+ *
+ * Return: 0 on success, negative on failure
+ */
+static int ice_flow_acl_disassoc_scen(struct ice_hw *hw,
+ struct ice_flow_prof *prof)
+{
+ struct ice_aqc_acl_prof_generic_frmt buf = {};
+ int err = 0;
+ u8 prof_id;
+
+ err = ice_flow_get_hw_prof(hw, ICE_BLK_ACL, prof->id, &prof_id);
+ if (err)
+ return err;
+
+ err = ice_query_acl_prof(hw, prof_id, &buf, NULL);
+ if (err)
+ return err;
+
+ /* Clear scenario for this PF */
+ buf.pf_scenario_num[hw->pf_id] = ICE_ACL_INVALID_SCEN;
+ return ice_prgm_acl_prof_xtrct(hw, prof_id, &buf, NULL);
+}
+
/**
* ice_flow_rem_entry_sync - Remove a flow entry
* @hw: pointer to the HW struct
* @blk: classification stage
* @entry: flow entry to be removed
+ *
+ * Return: 0 on success, negative on failure
*/
-static int
-ice_flow_rem_entry_sync(struct ice_hw *hw, enum ice_block __always_unused blk,
- struct ice_flow_entry *entry)
+static int ice_flow_rem_entry_sync(struct ice_hw *hw, enum ice_block blk,
+ struct ice_flow_entry *entry)
{
if (!entry)
return -EINVAL;
+ if (blk == ICE_BLK_ACL) {
+ if (entry->acts_cnt && entry->acts)
+ ice_flow_acl_free_act_cntr(hw, entry->acts,
+ entry->acts_cnt);
+ }
+
list_del(&entry->l_entry);
- devm_kfree(ice_hw_to_dev(hw), entry->entry);
+ kfree(entry->entry);
+ kfree(entry->range_buf);
+ kfree(entry->acts);
devm_kfree(ice_hw_to_dev(hw), entry);
return 0;
@@ -1729,6 +1878,13 @@ ice_flow_rem_prof_sync(struct ice_hw *hw, enum ice_block blk,
mutex_unlock(&prof->entries_lock);
}
+ if (blk == ICE_BLK_ACL) {
+ /* Disassociate the scenario from the profile for the PF */
+ status = ice_flow_acl_disassoc_scen(hw, prof);
+ if (status)
+ return status;
+ }
+
/* Remove all hardware profiles associated with this flow profile */
status = ice_rem_prof(hw, blk, prof->id);
if (!status) {
@@ -1741,6 +1897,101 @@ ice_flow_rem_prof_sync(struct ice_hw *hw, enum ice_block blk,
return status;
}
+/**
+ * ice_flow_acl_set_xtrct_seq_fld - Populate xtrct seq for single field
+ * @buf: Destination buffer function writes partial xtrct sequence to
+ * @info: Info about field
+ *
+ * Return: 0 on success, negative on failure
+ */
+static int
+ice_flow_acl_set_xtrct_seq_fld(struct ice_aqc_acl_prof_generic_frmt *buf,
+ struct ice_flow_fld_info *info)
+{
+ u16 src, dst;
+
+ src = info->xtrct.idx * ICE_FLOW_FV_EXTRACT_SZ +
+ info->xtrct.disp / BITS_PER_BYTE;
+ if (src > U8_MAX)
+ return -ERANGE;
+
+ dst = info->entry.val;
+ for (int i = 0; i < info->entry.last; i++)
+ /* HW stores field vector words in LE, convert words back to BE
+ * so constructed entries will end up in network order
+ */
+ buf->byte_selection[dst++] = src++ ^ 1;
+
+ return 0;
+}
+
+/**
+ * ice_flow_acl_set_xtrct_seq - Program ACL extraction sequence
+ * @hw: pointer to the hardware structure
+ * @prof: pointer to flow profile
+ *
+ * Return: 0 on success, negative on failure
+ */
+static int ice_flow_acl_set_xtrct_seq(struct ice_hw *hw,
+ struct ice_flow_prof *prof)
+{
+ struct ice_aqc_acl_prof_generic_frmt buf = {};
+ struct ice_flow_fld_info *info;
+ u8 prof_id = 0;
+ int err;
+
+ err = ice_flow_get_hw_prof(hw, ICE_BLK_ACL, prof->id, &prof_id);
+ if (err)
+ return err;
+
+ err = ice_query_acl_prof(hw, prof_id, &buf, NULL);
+ if (err)
+ return err;
+
+ if (!ice_flow_acl_is_prof_in_use(&buf)) {
+ /* Program the profile dependent configuration. This is done
+ * only once regardless of the number of PFs using that profile
+ */
+ memset(&buf, 0, sizeof(buf));
+
+ for (int i = 0; i < prof->segs_cnt; i++) {
+ struct ice_flow_seg_info *seg = &prof->segs[i];
+ u16 j;
+
+ for_each_set_bit(j, (unsigned long *)&seg->match,
+ ICE_FLOW_FIELD_IDX_MAX) {
+ info = &seg->fields[j];
+
+ if (info->type == ICE_FLOW_FLD_TYPE_RANGE) {
+ buf.word_selection[info->entry.val] =
+ info->xtrct.idx;
+ continue;
+ }
+
+ err = ice_flow_acl_set_xtrct_seq_fld(&buf,
+ info);
+ if (err)
+ return err;
+ }
+
+ for (j = 0; j < seg->raws_cnt; j++) {
+ info = &seg->raws[j].info;
+ err = ice_flow_acl_set_xtrct_seq_fld(&buf,
+ info);
+ if (err)
+ return err;
+ }
+ }
+
+ memset(&buf.pf_scenario_num[0], ICE_ACL_INVALID_SCEN,
+ ICE_AQC_ACL_PROF_PF_SCEN_NUM_ELEMS);
+ }
+
+ /* Update the current PF */
+ buf.pf_scenario_num[hw->pf_id] = (u8)prof->cfg.scen->id;
+ return ice_prgm_acl_prof_xtrct(hw, prof_id, &buf, NULL);
+}
+
/**
* ice_flow_assoc_prof - associate a VSI with a flow profile
* @hw: pointer to the hardware structure
@@ -1758,6 +2009,12 @@ ice_flow_assoc_prof(struct ice_hw *hw, enum ice_block blk,
int status = 0;
if (!test_bit(vsi_handle, prof->vsis)) {
+ if (blk == ICE_BLK_ACL) {
+ status = ice_flow_acl_set_xtrct_seq(hw, prof);
+ if (status)
+ return status;
+ }
+
status = ice_add_prof_id_flow(hw, blk,
ice_get_hw_vsi_num(hw,
vsi_handle),
@@ -1957,6 +2214,333 @@ int ice_flow_rem_prof(struct ice_hw *hw, enum ice_block blk, u64 prof_id)
return status;
}
+/**
+ * ice_flow_acl_check_actions - Checks the ACL rule's actions
+ * @hw: pointer to the hardware structure
+ * @acts: array of actions to be performed on a match
+ * @acts_cnt: number of actions
+ * @cnt_alloc: indicates if an ACL counter has been allocated.
+ *
+ * Return: 0 on success, negative on failure
+ */
+static int ice_flow_acl_check_actions(struct ice_hw *hw,
+ struct ice_flow_action *acts, u8 acts_cnt,
+ bool *cnt_alloc)
+{
+ DECLARE_BITMAP(dup_check, ICE_AQC_TBL_MAX_ACTION_PAIRS * 2);
+
+ bitmap_zero(dup_check, ICE_AQC_TBL_MAX_ACTION_PAIRS * 2);
+ *cnt_alloc = false;
+
+ if (acts_cnt > ICE_FLOW_ACL_MAX_NUM_ACT)
+ return -ERANGE;
+
+ for (int i = 0; i < acts_cnt; i++) {
+ if (acts[i].type != ICE_FLOW_ACT_NOP &&
+ acts[i].type != ICE_FLOW_ACT_DROP &&
+ acts[i].type != ICE_FLOW_ACT_CNTR_PKT &&
+ acts[i].type != ICE_FLOW_ACT_FWD_QUEUE)
+ return -EINVAL;
+
+ /* If the caller want to add two actions of the same type, then
+ * it is considered invalid configuration.
+ */
+ if (test_and_set_bit(acts[i].type, dup_check))
+ return -EINVAL;
+ }
+
+ /* Checks if ACL counters are needed. */
+ for (int i = 0; i < acts_cnt; i++) {
+ if (ice_flow_acl_is_cntr_act(acts[i].type)) {
+ struct ice_acl_cntrs cntrs = { 0 };
+ int err;
+
+ cntrs.amount = 1;
+ cntrs.bank = 0; /* Only bank0 for the moment */
+
+ if (acts[i].type == ICE_FLOW_ACT_CNTR_PKT_BYTES)
+ cntrs.type = ICE_AQC_ACL_CNT_TYPE_DUAL;
+ else
+ cntrs.type = ICE_AQC_ACL_CNT_TYPE_SINGLE;
+
+ err = ice_aq_alloc_acl_cntrs(hw, &cntrs, NULL);
+ if (err)
+ return err;
+ /* Counter index within the bank */
+ acts[i].data.acl_act.value =
+ cpu_to_le16(cntrs.first_cntr);
+ *cnt_alloc = true;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_flow_acl_frmt_entry_range - Format an ACL range checker for a given field
+ * @fld: number of the given field
+ * @info: info about field
+ * @range_buf: range checker configuration buffer
+ * @data: pointer to a data buffer containing flow entry's match values/masks
+ * @range: Input/output param indicating which range checkers are being used
+ */
+static void
+ice_flow_acl_frmt_entry_range(u16 fld, struct ice_flow_fld_info *info,
+ struct ice_aqc_acl_profile_ranges *range_buf,
+ u8 *data, u8 *range)
+{
+ u16 new_mask;
+
+ /* If not specified, default mask is all bits in field */
+ new_mask = (info->src.mask == ICE_FLOW_FLD_OFF_INVAL ?
+ BIT(ice_flds_info[fld].size) - 1 :
+ (*(u16 *)(data + info->src.mask))) << info->xtrct.disp;
+
+ /* If the mask is 0, then we don't need to worry about this input
+ * range checker value.
+ */
+ if (new_mask) {
+ u16 new_high =
+ (*(u16 *)(data + info->src.last)) << info->xtrct.disp;
+ u16 new_low =
+ (*(u16 *)(data + info->src.val)) << info->xtrct.disp;
+ u8 range_idx = info->entry.val;
+
+ range_buf->checker_cfg[range_idx].low_boundary =
+ cpu_to_be16(new_low);
+ range_buf->checker_cfg[range_idx].high_boundary =
+ cpu_to_be16(new_high);
+ range_buf->checker_cfg[range_idx].mask = cpu_to_be16(new_mask);
+
+ /* Indicate which range checker is being used */
+ *range |= BIT(range_idx);
+ }
+}
+
+/**
+ * ice_flow_acl_frmt_entry_fld - Partially format ACL entry for a given field
+ * @fld: number of the given field
+ * @info: info about the field
+ * @buf: buffer containing the entry
+ * @dontcare: buffer containing don't care mask for entry
+ * @data: pointer to a data buffer containing flow entry's match values/masks
+ */
+static void ice_flow_acl_frmt_entry_fld(u16 fld, struct ice_flow_fld_info *info,
+ u8 *buf, u8 *dontcare, u8 *data)
+{
+ u16 dst, src, mask, end_disp, tmp_s = 0, tmp_m = 0;
+ bool use_mask = false;
+ u8 disp;
+
+ src = info->src.val;
+ mask = info->src.mask;
+ dst = info->entry.val - ICE_AQC_ACL_PROF_BYTE_SEL_START_IDX;
+ disp = info->xtrct.disp % BITS_PER_BYTE;
+
+ if (mask != ICE_FLOW_FLD_OFF_INVAL)
+ use_mask = true;
+
+ for (u16 i = 0; i < info->entry.last; i++, dst++) {
+ /* Add overflow bits from previous byte */
+ buf[dst] = (tmp_s & 0xff00) >> 8;
+
+ /* If mask is not valid, tmp_m is always zero, so just setting
+ * dontcare to 0 (no masked bits). If mask is valid, pulls in
+ * overflow bits of mask from prev byte
+ */
+ dontcare[dst] = (tmp_m & 0xff00) >> 8;
+
+ /* If there is displacement, last byte will only contain
+ * displaced data, but there is no more data to read from user
+ * buffer, so skip so as not to potentially read beyond end of
+ * user buffer
+ */
+ if (!disp || i < info->entry.last - 1) {
+ /* Store shifted data to use in next byte */
+ tmp_s = data[src++] << disp;
+
+ /* Add current (shifted) byte */
+ buf[dst] |= tmp_s & 0xff;
+
+ /* Handle mask if valid */
+ if (use_mask) {
+ tmp_m = (~data[mask++] & 0xff) << disp;
+ dontcare[dst] |= tmp_m & 0xff;
+ }
+ }
+ }
+
+ /* Fill in don't care bits at beginning of field */
+ if (disp) {
+ dst = info->entry.val - ICE_AQC_ACL_PROF_BYTE_SEL_START_IDX;
+ for (int i = 0; i < disp; i++)
+ dontcare[dst] |= BIT(i);
+ }
+
+ end_disp = (disp + ice_flds_info[fld].size) % BITS_PER_BYTE;
+
+ /* Fill in don't care bits at end of field */
+ if (end_disp) {
+ dst = info->entry.val - ICE_AQC_ACL_PROF_BYTE_SEL_START_IDX +
+ info->entry.last - 1;
+ for (int i = end_disp; i < BITS_PER_BYTE; i++)
+ dontcare[dst] |= BIT(i);
+ }
+}
+
+/**
+ * ice_flow_acl_frmt_entry - Format ACL entry
+ * @hw: pointer to the hardware structure
+ * @prof: pointer to flow profile
+ * @e: pointer to the flow entry
+ * @data: pointer to a data buffer containing flow entry's match values/masks
+ * @acts: array of actions to be performed on a match
+ * @acts_cnt: number of actions
+ *
+ * Formats the key (and key_inverse) to be matched from the data passed in,
+ * along with data from the flow profile. This key/key_inverse pair makes up
+ * the 'entry' for an ACL flow entry.
+ *
+ * Return: 0 on success, negative on failure
+ */
+static int ice_flow_acl_frmt_entry(struct ice_hw *hw,
+ struct ice_flow_prof *prof,
+ struct ice_flow_entry *e, u8 *data,
+ struct ice_flow_action *acts, u8 acts_cnt)
+{
+ u8 *buf = NULL, *dontcare = NULL, *key = NULL, range = 0, dir_flag_msk,
+ prof_id;
+ struct ice_aqc_acl_profile_ranges *range_buf = NULL;
+ bool cnt_alloc;
+ u16 buf_sz;
+ int err;
+
+ err = ice_flow_get_hw_prof(hw, ICE_BLK_ACL, prof->id, &prof_id);
+ if (err)
+ return err;
+
+ /* Format the result action */
+
+ err = ice_flow_acl_check_actions(hw, acts, acts_cnt, &cnt_alloc);
+ if (err)
+ return err;
+
+ e->acts = kmemdup(acts, acts_cnt * sizeof(*acts), GFP_KERNEL);
+ if (!e->acts)
+ goto out;
+
+ e->acts_cnt = acts_cnt;
+
+ /* Format the matching data */
+ buf_sz = prof->cfg.scen->width;
+ buf = kzalloc_objs(*buf, buf_sz);
+ if (!buf)
+ goto out;
+
+ dontcare = kzalloc_objs(*dontcare, buf_sz);
+ if (!dontcare)
+ goto out;
+
+ /* 'key' buffer will store both key and key_inverse, so must be twice
+ * size of buf
+ */
+ key = kzalloc_objs(*key, buf_sz * 2);
+ if (!key)
+ goto out;
+
+ range_buf = kzalloc_obj(*range_buf);
+ if (!range_buf)
+ goto out;
+
+ /* Set don't care mask to all 1's to start, will zero out used bytes */
+ memset(dontcare, 0xff, buf_sz);
+
+ for (int i = 0; i < prof->segs_cnt; i++) {
+ struct ice_flow_seg_info *seg = &prof->segs[i];
+ u8 j;
+
+ for_each_set_bit(j, (unsigned long *)&seg->match,
+ ICE_FLOW_FIELD_IDX_MAX) {
+ struct ice_flow_fld_info *info = &seg->fields[j];
+
+ if (info->type == ICE_FLOW_FLD_TYPE_RANGE)
+ ice_flow_acl_frmt_entry_range(j, info,
+ range_buf, data,
+ &range);
+ else
+ ice_flow_acl_frmt_entry_fld(j, info, buf,
+ dontcare, data);
+ }
+
+ for (j = 0; j < seg->raws_cnt; j++) {
+ struct ice_flow_fld_info *info = &seg->raws[j].info;
+ u16 dst, src, mask, k;
+ bool use_mask = false;
+
+ src = info->src.val;
+ dst = info->entry.val -
+ ICE_AQC_ACL_PROF_BYTE_SEL_START_IDX;
+ mask = info->src.mask;
+
+ if (mask != ICE_FLOW_FLD_OFF_INVAL)
+ use_mask = true;
+
+ for (k = 0; k < info->entry.last; k++, dst++) {
+ buf[dst] = data[src++];
+ if (use_mask)
+ dontcare[dst] = ~data[mask++];
+ else
+ dontcare[dst] = 0;
+ }
+ }
+ }
+
+ buf[prof->cfg.scen->pid_idx] = (u8)prof_id;
+ dontcare[prof->cfg.scen->pid_idx] = 0;
+
+ /* Format the buffer for direction flags */
+ dir_flag_msk = BIT(ICE_FLG_PKT_DIR);
+
+ if (prof->dir == ICE_FLOW_RX)
+ buf[prof->cfg.scen->pkt_dir_idx] = dir_flag_msk;
+
+ if (range) {
+ buf[prof->cfg.scen->rng_chk_idx] = range;
+ /* Mark any unused range checkers as don't care */
+ dontcare[prof->cfg.scen->rng_chk_idx] = ~range;
+ e->range_buf = range_buf;
+ } else {
+ kfree(range_buf);
+ }
+
+ err = ice_set_key(key, buf_sz * 2, buf, NULL, dontcare, NULL, 0,
+ buf_sz);
+ if (err)
+ goto out;
+
+ e->entry = key;
+ e->entry_sz = buf_sz * 2;
+
+out:
+ kfree(buf);
+ kfree(dontcare);
+
+ if (err) {
+ kfree(key);
+
+ kfree(range_buf);
+ e->range_buf = NULL;
+
+ kfree(e->acts);
+ e->acts = NULL;
+ e->acts_cnt = 0;
+
+ if (cnt_alloc)
+ ice_flow_acl_free_act_cntr(hw, acts, acts_cnt);
+ }
+
+ return err;
+}
/**
* ice_flow_add_entry - Add a flow entry
* @hw: pointer to the HW struct
@@ -1966,17 +2550,23 @@ int ice_flow_rem_prof(struct ice_hw *hw, enum ice_block blk, u64 prof_id)
* @vsi_handle: software VSI handle for the flow entry
* @prio: priority of the flow entry
* @data: pointer to a data buffer containing flow entry's match values/masks
+ * @acts: array of actions to be performed on a match
+ * @acts_cnt: number of actions
* @entry_h: pointer to buffer that receives the new flow entry's handle
*/
-int
-ice_flow_add_entry(struct ice_hw *hw, enum ice_block blk, u64 prof_id,
- u64 entry_id, u16 vsi_handle, enum ice_flow_priority prio,
- void *data, u64 *entry_h)
+int ice_flow_add_entry(struct ice_hw *hw, enum ice_block blk, u64 prof_id,
+ u64 entry_id, u16 vsi_handle,
+ enum ice_flow_priority prio, void *data,
+ struct ice_flow_action *acts, u8 acts_cnt, u64 *entry_h)
{
struct ice_flow_entry *e = NULL;
struct ice_flow_prof *prof;
int status;
+ /* ACL entries must indicate an action */
+ if (blk == ICE_BLK_ACL && (!acts || !acts_cnt))
+ return -EINVAL;
+
/* No flow entry data is expected for RSS */
if (!entry_h || (!data && blk != ICE_BLK_RSS))
return -EINVAL;
@@ -2013,20 +2603,32 @@ ice_flow_add_entry(struct ice_hw *hw, enum ice_block blk, u64 prof_id,
case ICE_BLK_FD:
case ICE_BLK_RSS:
break;
+ case ICE_BLK_ACL:
+ /* ACL will handle the entry management */
+ status = ice_flow_acl_frmt_entry(hw, prof, e, (u8 *)data, acts,
+ acts_cnt);
+ if (status)
+ goto out;
+ break;
default:
status = -EOPNOTSUPP;
goto out;
}
- mutex_lock(&prof->entries_lock);
- list_add(&e->l_entry, &prof->entries);
- mutex_unlock(&prof->entries_lock);
+ if (blk != ICE_BLK_ACL) {
+ /* ACL will handle the entry management */
+ mutex_lock(&prof->entries_lock);
+ list_add(&e->l_entry, &prof->entries);
+ mutex_unlock(&prof->entries_lock);
+ }
*entry_h = ICE_FLOW_ENTRY_HNDL(e);
out:
if (status && e) {
- devm_kfree(ice_hw_to_dev(hw), e->entry);
+ kfree(e->entry);
+ kfree(e->range_buf);
+ kfree(e->acts);
devm_kfree(ice_hw_to_dev(hw), e);
}
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 59036a22ba91..02be10710687 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -8658,7 +8658,7 @@ static int ice_add_vsi_to_fdir(struct ice_pf *pf, struct ice_vsi *vsi)
prof->prof_id[tun],
prof->vsi_h[0], vsi->idx,
prio, prof->fdir_seg[tun],
- &entry_h);
+ NULL, 0, &entry_h);
if (status) {
dev_err(dev, "channel VSI idx %d, not able to add to group %d\n",
vsi->idx, flow);
diff --git a/drivers/net/ethernet/intel/ice/virt/fdir.c b/drivers/net/ethernet/intel/ice/virt/fdir.c
index eca9eda04f31..38e68d3d030c 100644
--- a/drivers/net/ethernet/intel/ice/virt/fdir.c
+++ b/drivers/net/ethernet/intel/ice/virt/fdir.c
@@ -688,7 +688,7 @@ ice_vc_fdir_write_flow_prof(struct ice_vf *vf, enum ice_fltr_ptype flow,
ret = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id, vf_vsi->idx,
vf_vsi->idx, ICE_FLOW_PRIO_NORMAL,
- seg, &entry1_h);
+ seg, NULL, 0, &entry1_h);
if (ret) {
dev_dbg(dev, "Could not add flow 0x%x VSI entry for VF %d\n",
flow, vf->vf_id);
@@ -697,7 +697,7 @@ ice_vc_fdir_write_flow_prof(struct ice_vf *vf, enum ice_fltr_ptype flow,
ret = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id, vf_vsi->idx,
ctrl_vsi->idx, ICE_FLOW_PRIO_NORMAL,
- seg, &entry2_h);
+ seg, NULL, 0, &entry2_h);
if (ret) {
dev_dbg(dev,
"Could not add flow 0x%x Ctrl VSI entry for VF %d\n",
--
2.49.0
^ permalink raw reply related
* [PATCH iwl-next v2 10/10] ice: use ACL for ntuple rules that conflict with FDir
From: Marcin Szycik @ 2026-04-09 12:00 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Lukasz Czapnik, Aleksandr Loktionov
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
From: Lukasz Czapnik <lukasz.czapnik@intel.com>
Flow Director can keep only one input set per flow type. After ACL
support was added for ethtool ntuple rules, the driver still only
selected ACL for rules with partial masks.
That leaves a gap for rules with full masks that still require a
different input set than the one already programmed for Flow Director.
Such rules go through the FDir path, build a different extraction
sequence and then fail because the existing FDir profile cannot be
reused.
Detect this case before programming the rule. Build the candidate IP
flow segment, compare it with the active non-tunneled FDir profile and,
when the input sets differ, offload the rule through ACL if ACL is
available.
Refactor the IP flow segment setup into a helper so the same logic can
be used both by the extraction-sequence configuration path and by the
conflict check.
Signed-off-by: Lukasz Czapnik <lukasz.czapnik@intel.com>
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
v2:
* Add this patch
---
.../ethernet/intel/ice/ice_ethtool_ntuple.c | 154 ++++++++++++------
1 file changed, 107 insertions(+), 47 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
index 21d4f4e3a1d0..13073387376e 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
@@ -1425,6 +1425,102 @@ ice_set_fdir_vlan_seg(struct ice_flow_seg_info *seg,
return 0;
}
+/**
+ * ice_set_fdir_ip_flow_seg - set IP flow segment based on ethtool flow type
+ * @fsp: pointer to ethtool Rx flow specification
+ * @seg: flow segment for programming
+ * @perfect_fltr: valid on success; returns true if perfect fltr, false if not
+ *
+ * Return: 0 on success and errno in case of error.
+ */
+static int ice_set_fdir_ip_flow_seg(struct ethtool_rx_flow_spec *fsp,
+ struct ice_flow_seg_info *seg,
+ bool *perfect_fltr)
+{
+ switch (fsp->flow_type & ~FLOW_EXT) {
+ case TCP_V4_FLOW:
+ return ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
+ ICE_FLOW_SEG_HDR_TCP, perfect_fltr);
+ case UDP_V4_FLOW:
+ return ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
+ ICE_FLOW_SEG_HDR_UDP, perfect_fltr);
+ case SCTP_V4_FLOW:
+ return ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
+ ICE_FLOW_SEG_HDR_SCTP,
+ perfect_fltr);
+ case IPV4_USER_FLOW:
+ return ice_set_fdir_ip4_usr_seg(seg, &fsp->m_u.usr_ip4_spec,
+ perfect_fltr);
+ case TCP_V6_FLOW:
+ return ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
+ ICE_FLOW_SEG_HDR_TCP, perfect_fltr);
+ case UDP_V6_FLOW:
+ return ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
+ ICE_FLOW_SEG_HDR_UDP, perfect_fltr);
+ case SCTP_V6_FLOW:
+ return ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
+ ICE_FLOW_SEG_HDR_SCTP,
+ perfect_fltr);
+ case IPV6_USER_FLOW:
+ return ice_set_fdir_ip6_usr_seg(seg, &fsp->m_u.usr_ip6_spec,
+ perfect_fltr);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/**
+ * ice_fdir_has_input_set_conflict - Check conflict with existing FD filters
+ * @pf: PF structure
+ * @fsp: pointer to ethtool Rx flow specification
+ *
+ * Checks if adding this filter to Flow Director would cause an input set
+ * mismatch with existing filters for the same flow type by building
+ * the segment and comparing with existing profiles.
+ *
+ * Return: true if there's a conflict (use ACL), false otherwise (can use FD)
+ */
+static bool ice_fdir_has_input_set_conflict(struct ice_pf *pf,
+ struct ethtool_rx_flow_spec *fsp)
+{
+ struct ice_flow_seg_info *test_seg, *old_seg;
+ bool perfect_fltr, conflict = false;
+ struct ice_fd_hw_prof *hw_prof;
+ struct ice_hw *hw = &pf->hw;
+ enum ice_fltr_ptype flow;
+ int err;
+
+ flow = ice_ethtool_flow_to_fltr(fsp->flow_type & ~FLOW_EXT);
+ if (flow >= ICE_FLTR_PTYPE_MAX || !hw->fdir_prof ||
+ !hw->fdir_prof[flow]) {
+ return false;
+ }
+
+ hw_prof = hw->fdir_prof[flow];
+ old_seg = hw_prof->fdir_seg[ICE_FD_HW_SEG_NON_TUN];
+
+ if (!old_seg || hw->fdir_fltr_cnt[flow] == 0)
+ return false;
+
+ test_seg = kzalloc_obj(*test_seg);
+ if (!test_seg)
+ return false;
+
+ err = ice_set_fdir_ip_flow_seg(fsp, test_seg, &perfect_fltr);
+ if (err) {
+ kfree(test_seg);
+ return false;
+ }
+
+ /* Compare the test segment with the existing segment */
+ if (memcmp(old_seg, test_seg, sizeof(*test_seg)) != 0)
+ conflict = true;
+
+ kfree(test_seg);
+
+ return conflict;
+}
+
/**
* ice_cfg_fdir_xtrct_seq - Configure extraction sequence for the given filter
* @pf: PF structure
@@ -1455,57 +1551,16 @@ ice_cfg_fdir_xtrct_seq(struct ice_pf *pf, struct ethtool_rx_flow_spec *fsp,
return -ENOMEM;
}
- switch (fsp->flow_type & ~FLOW_EXT) {
- case TCP_V4_FLOW:
- ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
- ICE_FLOW_SEG_HDR_TCP,
- &perfect_filter);
- break;
- case UDP_V4_FLOW:
- ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
- ICE_FLOW_SEG_HDR_UDP,
- &perfect_filter);
- break;
- case SCTP_V4_FLOW:
- ret = ice_set_fdir_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
- ICE_FLOW_SEG_HDR_SCTP,
- &perfect_filter);
- break;
- case IPV4_USER_FLOW:
- ret = ice_set_fdir_ip4_usr_seg(seg, &fsp->m_u.usr_ip4_spec,
- &perfect_filter);
- break;
- case TCP_V6_FLOW:
- ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
- ICE_FLOW_SEG_HDR_TCP,
- &perfect_filter);
- break;
- case UDP_V6_FLOW:
- ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
- ICE_FLOW_SEG_HDR_UDP,
- &perfect_filter);
- break;
- case SCTP_V6_FLOW:
- ret = ice_set_fdir_ip6_seg(seg, &fsp->m_u.tcp_ip6_spec,
- ICE_FLOW_SEG_HDR_SCTP,
- &perfect_filter);
- break;
- case IPV6_USER_FLOW:
- ret = ice_set_fdir_ip6_usr_seg(seg, &fsp->m_u.usr_ip6_spec,
- &perfect_filter);
- break;
- case ETHER_FLOW:
+ if ((fsp->flow_type & ~FLOW_EXT) == ETHER_FLOW) {
ret = ice_set_ether_flow_seg(dev, seg, &fsp->m_u.ether_spec);
if (!ret && (fsp->m_ext.vlan_etype || fsp->m_ext.vlan_tci)) {
- if (!ice_fdir_vlan_valid(dev, fsp)) {
+ if (!ice_fdir_vlan_valid(dev, fsp))
ret = -EINVAL;
- break;
- }
- ret = ice_set_fdir_vlan_seg(seg, &fsp->m_ext);
+ else
+ ret = ice_set_fdir_vlan_seg(seg, &fsp->m_ext);
}
- break;
- default:
- ret = -EINVAL;
+ } else {
+ ret = ice_set_fdir_ip_flow_seg(fsp, seg, &perfect_filter);
}
if (ret)
goto err_exit;
@@ -2241,6 +2296,11 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
if (pf->hw.acl_tbl && ice_is_acl_filter(fsp))
return ice_acl_add_rule_ethtool(vsi, cmd);
+ /* Check if this would cause input set conflict with existing FD filters
+ */
+ if (pf->hw.acl_tbl && ice_fdir_has_input_set_conflict(pf, fsp))
+ return ice_acl_add_rule_ethtool(vsi, cmd);
+
ret = ice_cfg_fdir_xtrct_seq(pf, fsp, &userdata);
if (ret)
return ret;
--
2.49.0
^ permalink raw reply related
* [PATCH iwl-next v2 04/10] ice: create flow profile
From: Marcin Szycik @ 2026-04-09 11:59 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Chinh Cao, Tony Nguyen, Aleksandr Loktionov
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
From: Real Valiquette <real.valiquette@intel.com>
Implement the initial steps for creating an ACL filter to support ntuple
masks. Create a flow profile based on a given mask rule and program it to
the hardware. Though the profile is written to hardware, no actions are
associated with the profile yet.
Co-developed-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Real Valiquette <real.valiquette@intel.com>
Co-developed-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Co-developed-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
v2:
* Add ice_acl_main.h in order to not awkwardly add prototypes to ice.h.
This will also help avoid potential dependency issues for future
additions to ice_acl_main.c
* Rename ice_acl_check_input_set() to a more fiting
ice_acl_prof_add_ethtool() as it adds a profile
* Set hw->acl_prof = 0 in ice_acl_prof_add_ethtool() to avoid use after
free
* Add ipv4 and port full mask defines in ice_ethtool_ntuple.c
* Move hw->acl_prof allocation to ice_init_acl(). Previously, it was
being deallocated when hw->acl_prof[fltr_type] allocation failed,
possibly with already existing other elements. Extend array lifetime
to driver's lifetime
* Change hw->acl_prof[fltr_type] alloc from devm_ to plain
* Add hw->acl_prof[fltr_type] and hw->acl_prof deallocation in
ice_deinit_acl() - previously were only deallocated on failure
* Tweak alloc/unroll logic in ice_acl_prof_add_ethtool()
---
drivers/net/ethernet/intel/ice/Makefile | 1 +
drivers/net/ethernet/intel/ice/ice.h | 6 +
drivers/net/ethernet/intel/ice/ice_acl_main.h | 9 +
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 39 +++
drivers/net/ethernet/intel/ice/ice_flow.h | 17 +
drivers/net/ethernet/intel/ice/ice_acl_main.c | 229 ++++++++++++++
.../ethernet/intel/ice/ice_ethtool_ntuple.c | 299 +++++++++++++-----
.../net/ethernet/intel/ice/ice_flex_pipe.c | 6 +
drivers/net/ethernet/intel/ice/ice_flow.c | 173 ++++++++++
drivers/net/ethernet/intel/ice/ice_main.c | 33 +-
10 files changed, 731 insertions(+), 81 deletions(-)
create mode 100644 drivers/net/ethernet/intel/ice/ice_acl_main.h
create mode 100644 drivers/net/ethernet/intel/ice/ice_acl_main.c
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index 6afe7be056ba..7f06d9bafe4a 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -25,6 +25,7 @@ ice-y := ice_main.o \
ice_vsi_vlan_lib.o \
ice_fdir.o \
ice_ethtool_ntuple.o \
+ ice_acl_main.o \
ice_acl.o \
ice_acl_ctrl.o \
ice_vlan_mode.o \
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index e064323d983c..d10e67d8bf02 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -1025,6 +1025,11 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
int ice_del_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
int ice_get_ethtool_fdir_entry(struct ice_hw *hw, struct ethtool_rxnfc *cmd);
u32 ice_ntuple_get_max_fltr_cnt(struct ice_hw *hw);
+int ice_ntuple_l4_proto_to_port(enum ice_flow_seg_hdr l4_proto,
+ enum ice_flow_field *src_port,
+ enum ice_flow_field *dst_port);
+int ice_ntuple_check_ip4_seg(struct ethtool_tcpip4_spec *tcp_ip4_spec);
+int ice_ntuple_check_ip4_usr_seg(struct ethtool_usrip4_spec *usr_ip4_spec);
int
ice_get_fdir_fltr_ids(struct ice_hw *hw, struct ethtool_rxnfc *cmd,
u32 *rule_locs);
@@ -1033,6 +1038,7 @@ void ice_fdir_release_flows(struct ice_hw *hw);
void ice_fdir_replay_flows(struct ice_hw *hw);
void ice_fdir_replay_fltrs(struct ice_pf *pf);
int ice_fdir_create_dflt_rules(struct ice_pf *pf);
+enum ice_fltr_ptype ice_ethtool_flow_to_fltr(int eth);
enum ice_aq_task_state {
ICE_AQ_TASK_NOT_PREPARED,
diff --git a/drivers/net/ethernet/intel/ice/ice_acl_main.h b/drivers/net/ethernet/intel/ice/ice_acl_main.h
new file mode 100644
index 000000000000..6665af2e7053
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_acl_main.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2026, Intel Corporation. */
+
+#ifndef _ICE_ACL_MAIN_H_
+#define _ICE_ACL_MAIN_H_
+#include "ice.h"
+#include <linux/ethtool.h>
+int ice_acl_add_rule_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
+#endif /* _ICE_ACL_MAIN_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 46d2675baa4e..1a32400e70bd 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -172,6 +172,8 @@ struct ice_aqc_set_port_params {
#define ICE_AQC_RES_TYPE_FDIR_COUNTER_BLOCK 0x21
#define ICE_AQC_RES_TYPE_FDIR_GUARANTEED_ENTRIES 0x22
#define ICE_AQC_RES_TYPE_FDIR_SHARED_ENTRIES 0x23
+#define ICE_AQC_RES_TYPE_ACL_PROF_BLDR_PROFID 0x50
+#define ICE_AQC_RES_TYPE_ACL_PROF_BLDR_TCAM 0x51
#define ICE_AQC_RES_TYPE_FD_PROF_BLDR_PROFID 0x58
#define ICE_AQC_RES_TYPE_FD_PROF_BLDR_TCAM 0x59
#define ICE_AQC_RES_TYPE_HASH_PROF_BLDR_PROFID 0x60
@@ -2175,6 +2177,43 @@ struct ice_aqc_actpair {
struct ice_acl_act_entry act[ICE_ACL_NUM_ACT_PER_ACT_PAIR];
};
+/* The first byte of the byte selection base is reserved to keep the
+ * first byte of the field vector where the packet direction info is
+ * available. Thus we should start at index 1 of the field vector to
+ * map its entries to the byte selection base.
+ */
+#define ICE_AQC_ACL_PROF_BYTE_SEL_START_IDX 1
+#define ICE_AQC_ACL_PROF_BYTE_SEL_ELEMS 30
+
+/* Input buffer format for program profile extraction admin command and
+ * response buffer format for query profile admin command is as defined
+ * in struct ice_aqc_acl_prof_generic_frmt
+ */
+
+/* Input buffer format for program profile ranges and query profile ranges
+ * admin commands. Same format is used for response buffer in case of query
+ * profile ranges command
+ */
+struct ice_acl_rng_data {
+ /* The range checker output shall be sent when the value
+ * related to this range checker is lower than low boundary
+ */
+ __be16 low_boundary;
+ /* The range checker output shall be sent when the value
+ * related to this range checker is higher than high boundary
+ */
+ __be16 high_boundary;
+ /* A value of '0' in bit shall clear the relevant bit input
+ * to the range checker
+ */
+ __be16 mask;
+};
+
+#define ICE_AQC_ACL_PROF_RANGES_NUM_CFG 8
+struct ice_aqc_acl_profile_ranges {
+ struct ice_acl_rng_data checker_cfg[ICE_AQC_ACL_PROF_RANGES_NUM_CFG];
+};
+
/* Program ACL entry (indirect 0x0C20) */
struct ice_aqc_acl_entry {
u8 tcam_index;
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.h b/drivers/net/ethernet/intel/ice/ice_flow.h
index a20ef320e1f9..bbfc7b4a432e 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.h
+++ b/drivers/net/ethernet/intel/ice/ice_flow.h
@@ -504,6 +504,23 @@ struct ice_rss_cfg {
struct ice_rss_hash_cfg hash;
};
+enum ice_flow_action_type {
+ ICE_FLOW_ACT_NOP,
+ ICE_FLOW_ACT_DROP,
+ ICE_FLOW_ACT_CNTR_PKT,
+ ICE_FLOW_ACT_FWD_QUEUE,
+ ICE_FLOW_ACT_CNTR_BYTES,
+ ICE_FLOW_ACT_CNTR_PKT_BYTES,
+};
+
+struct ice_flow_action {
+ enum ice_flow_action_type type;
+ union {
+ struct ice_acl_act_entry acl_act;
+ u32 dummy;
+ } data;
+};
+
int
ice_flow_add_prof(struct ice_hw *hw, enum ice_block blk, enum ice_flow_dir dir,
struct ice_flow_seg_info *segs, u8 segs_cnt,
diff --git a/drivers/net/ethernet/intel/ice/ice_acl_main.c b/drivers/net/ethernet/intel/ice/ice_acl_main.c
new file mode 100644
index 000000000000..841e4d567ff2
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_acl_main.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2018-2026, Intel Corporation. */
+
+#include "ice.h"
+#include "ice_lib.h"
+#include "ice_acl_main.h"
+
+/* Number of action */
+#define ICE_ACL_NUM_ACT 1
+
+/**
+ * ice_acl_set_ip4_addr_seg - set flow segment IPv4 addresses masks
+ * @seg: flow segment for programming
+ */
+static void ice_acl_set_ip4_addr_seg(struct ice_flow_seg_info *seg)
+{
+ u16 val_loc, mask_loc;
+
+ /* IP source address */
+ val_loc = offsetof(struct ice_ntuple_fltr, ip.v4.src_ip);
+ mask_loc = offsetof(struct ice_ntuple_fltr, mask.v4.src_ip);
+
+ ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_SA, val_loc,
+ mask_loc, ICE_FLOW_FLD_OFF_INVAL, false);
+
+ /* IP destination address */
+ val_loc = offsetof(struct ice_ntuple_fltr, ip.v4.dst_ip);
+ mask_loc = offsetof(struct ice_ntuple_fltr, mask.v4.dst_ip);
+
+ ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_DA, val_loc,
+ mask_loc, ICE_FLOW_FLD_OFF_INVAL, false);
+}
+
+/**
+ * ice_acl_set_ip4_port_seg - set flow segment port masks based on L4 port
+ * @seg: flow segment for programming
+ * @l4_proto: Layer 4 protocol to program
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_acl_set_ip4_port_seg(struct ice_flow_seg_info *seg,
+ enum ice_flow_seg_hdr l4_proto)
+{
+ enum ice_flow_field src_port, dst_port;
+ u16 val_loc, mask_loc;
+ int err;
+
+ err = ice_ntuple_l4_proto_to_port(l4_proto, &src_port, &dst_port);
+ if (err)
+ return err;
+
+ /* Layer 4 source port */
+ val_loc = offsetof(struct ice_ntuple_fltr, ip.v4.src_port);
+ mask_loc = offsetof(struct ice_ntuple_fltr, mask.v4.src_port);
+
+ ice_flow_set_fld(seg, src_port, val_loc, mask_loc,
+ ICE_FLOW_FLD_OFF_INVAL, false);
+
+ /* Layer 4 destination port */
+ val_loc = offsetof(struct ice_ntuple_fltr, ip.v4.dst_port);
+ mask_loc = offsetof(struct ice_ntuple_fltr, mask.v4.dst_port);
+
+ ice_flow_set_fld(seg, dst_port, val_loc, mask_loc,
+ ICE_FLOW_FLD_OFF_INVAL, false);
+
+ return 0;
+}
+
+/**
+ * ice_acl_set_ip4_seg - set flow segment IPv4 and L4 masks
+ * @seg: flow segment for programming
+ * @tcp_ip4_spec: mask data from ethtool
+ * @l4_proto: Layer 4 protocol to program
+ *
+ * Set the mask data into the flow segment to be used to program HW
+ * table based on provided L4 protocol for IPv4
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_acl_set_ip4_seg(struct ice_flow_seg_info *seg,
+ struct ethtool_tcpip4_spec *tcp_ip4_spec,
+ enum ice_flow_seg_hdr l4_proto)
+{
+ int err;
+
+ err = ice_ntuple_check_ip4_seg(tcp_ip4_spec);
+ if (err)
+ return err;
+
+ ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4 | l4_proto);
+ ice_acl_set_ip4_addr_seg(seg);
+
+ return ice_acl_set_ip4_port_seg(seg, l4_proto);
+}
+
+/**
+ * ice_acl_set_ip4_usr_seg - set flow segment IPv4 masks
+ * @seg: flow segment for programming
+ * @usr_ip4_spec: ethtool userdef packet offset
+ *
+ * Set the offset data into the flow segment to be used to program HW
+ * table for IPv4
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_acl_set_ip4_usr_seg(struct ice_flow_seg_info *seg,
+ struct ethtool_usrip4_spec *usr_ip4_spec)
+{
+ int err;
+
+ err = ice_ntuple_check_ip4_usr_seg(usr_ip4_spec);
+ if (err)
+ return err;
+
+ ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4);
+ ice_acl_set_ip4_addr_seg(seg);
+
+ return 0;
+}
+
+/**
+ * ice_acl_prof_add_ethtool - Check ethtool input set and add ACL profile
+ * @pf: ice PF structure
+ * @fsp: pointer to ethtool Rx flow specification
+ *
+ * Return: 0 on success and negative values for failure
+ */
+static int ice_acl_prof_add_ethtool(struct ice_pf *pf,
+ struct ethtool_rx_flow_spec *fsp)
+{
+ struct ice_flow_prof *prof = NULL;
+ struct ice_flow_seg_info *old_seg;
+ struct ice_fd_hw_prof *hw_prof;
+ struct ice_flow_seg_info *seg;
+ enum ice_fltr_ptype fltr_type;
+ struct ice_hw *hw = &pf->hw;
+ int err;
+
+ seg = kzalloc_obj(*seg);
+ if (!seg)
+ return -ENOMEM;
+
+ switch (fsp->flow_type & ~FLOW_EXT) {
+ case TCP_V4_FLOW:
+ err = ice_acl_set_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
+ ICE_FLOW_SEG_HDR_TCP);
+ break;
+ case UDP_V4_FLOW:
+ err = ice_acl_set_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
+ ICE_FLOW_SEG_HDR_UDP);
+ break;
+ case SCTP_V4_FLOW:
+ err = ice_acl_set_ip4_seg(seg, &fsp->m_u.tcp_ip4_spec,
+ ICE_FLOW_SEG_HDR_SCTP);
+ break;
+ case IPV4_USER_FLOW:
+ err = ice_acl_set_ip4_usr_seg(seg, &fsp->m_u.usr_ip4_spec);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ }
+ if (err)
+ goto free_seg;
+
+ fltr_type = ice_ethtool_flow_to_fltr(fsp->flow_type & ~FLOW_EXT);
+
+ hw_prof = hw->acl_prof[fltr_type];
+ if (!hw_prof) {
+ hw_prof = kzalloc_obj(**hw->acl_prof);
+ if (!hw_prof) {
+ err = -ENOMEM;
+ goto free_seg;
+ }
+ hw_prof->cnt = 0;
+ }
+
+ old_seg = hw_prof->fdir_seg[0];
+ if (old_seg) {
+ /* This flow_type already has an input set.
+ * If it matches the requested input set then we are
+ * done. If it's different then it's an error.
+ */
+ if (!memcmp(old_seg, seg, sizeof(*seg))) {
+ kfree(seg);
+ return 0;
+ }
+
+ err = -EINVAL;
+ goto free_acl_prof;
+ }
+
+ /* Adding a profile for the given flow specification with no
+ * actions (NULL) and zero actions 0.
+ */
+ err = ice_flow_add_prof(hw, ICE_BLK_ACL, ICE_FLOW_RX, seg, 1, false,
+ &prof);
+ if (err)
+ goto free_acl_prof;
+
+ hw_prof->fdir_seg[0] = seg;
+ hw->acl_prof[fltr_type] = hw_prof;
+ return 0;
+
+free_acl_prof:
+ kfree(hw_prof);
+free_seg:
+ kfree(seg);
+
+ return err;
+}
+
+/**
+ * ice_acl_add_rule_ethtool - add an ACL rule
+ * @vsi: pointer to target VSI
+ * @cmd: command to add or delete ACL rule
+ *
+ * Return: 0 on success and negative values for failure
+ */
+int ice_acl_add_rule_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
+{
+ struct ethtool_rx_flow_spec *fsp;
+ struct ice_pf *pf;
+
+ pf = vsi->back;
+
+ fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
+
+ return ice_acl_prof_add_ethtool(pf, fsp);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
index 053d6b7a66bd..eca15cb2665e 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
@@ -7,6 +7,7 @@
#include "ice_lib.h"
#include "ice_fdir.h"
#include "ice_flow.h"
+#include "ice_acl_main.h"
static struct in6_addr full_ipv6_addr_mask = {
.in6_u = {
@@ -26,6 +27,9 @@ static struct in6_addr zero_ipv6_addr_mask = {
}
};
+#define ICE_FULL_IPV4_ADDR_MASK 0xFFFFFFFF
+#define ICE_FULL_PORT_MASK 0xFFFF
+
/* calls to ice_flow_add_prof require the number of segments in the array
* for segs_cnt. In this code that is one more than the index.
*/
@@ -71,7 +75,7 @@ static int ice_fltr_to_ethtool_flow(enum ice_fltr_ptype flow)
*
* Returns flow enum
*/
-static enum ice_fltr_ptype ice_ethtool_flow_to_fltr(int eth)
+enum ice_fltr_ptype ice_ethtool_flow_to_fltr(int eth)
{
switch (eth) {
case ETHER_FLOW:
@@ -932,23 +936,13 @@ ice_create_init_fdir_rule(struct ice_pf *pf, enum ice_fltr_ptype flow)
}
/**
- * ice_set_fdir_ip4_seg
- * @seg: flow segment for programming
+ * ice_ntuple_check_ip4_seg - Check valid fields are provided for filter
* @tcp_ip4_spec: mask data from ethtool
- * @l4_proto: Layer 4 protocol to program
- * @perfect_fltr: only valid on success; returns true if perfect filter,
- * false if not
*
- * Set the mask data into the flow segment to be used to program HW
- * table based on provided L4 protocol for IPv4
+ * Return: 0 if fields valid, negative otherwise
*/
-static int
-ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
- struct ethtool_tcpip4_spec *tcp_ip4_spec,
- enum ice_flow_seg_hdr l4_proto, bool *perfect_fltr)
+int ice_ntuple_check_ip4_seg(struct ethtool_tcpip4_spec *tcp_ip4_spec)
{
- enum ice_flow_field src_port, dst_port;
-
/* make sure we don't have any empty rule */
if (!tcp_ip4_spec->psrc && !tcp_ip4_spec->ip4src &&
!tcp_ip4_spec->pdst && !tcp_ip4_spec->ip4dst)
@@ -958,24 +952,71 @@ ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
if (tcp_ip4_spec->tos)
return -EOPNOTSUPP;
+ return 0;
+}
+
+/**
+ * ice_ntuple_l4_proto_to_port - set src and dst port for given L4 protocol
+ * @l4_proto: Layer 4 protocol to program
+ * @src_port: source flow field value for provided l4 protocol
+ * @dst_port: destination flow field value for provided l4 protocol
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_ntuple_l4_proto_to_port(enum ice_flow_seg_hdr l4_proto,
+ enum ice_flow_field *src_port,
+ enum ice_flow_field *dst_port)
+{
if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
- src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
+ *src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
+ *dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
} else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
- src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
+ *src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
+ *dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
} else if (l4_proto == ICE_FLOW_SEG_HDR_SCTP) {
- src_port = ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_SCTP_DST_PORT;
+ *src_port = ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT;
+ *dst_port = ICE_FLOW_FIELD_IDX_SCTP_DST_PORT;
} else {
return -EOPNOTSUPP;
}
+ return 0;
+}
+
+/**
+ * ice_set_fdir_ip4_seg - setup flow segment based on IPv4 and L4 proto
+ * @seg: flow segment for programming
+ * @tcp_ip4_spec: mask data from ethtool
+ * @l4_proto: Layer 4 protocol to program
+ * @perfect_fltr: only valid on success; returns true if perfect filter,
+ * false if not
+ *
+ * Set the mask data into the flow segment to be used to program HW
+ * table based on provided L4 protocol for IPv4
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
+ struct ethtool_tcpip4_spec *tcp_ip4_spec,
+ enum ice_flow_seg_hdr l4_proto,
+ bool *perfect_fltr)
+{
+ enum ice_flow_field src_port, dst_port;
+ int err;
+
+ err = ice_ntuple_check_ip4_seg(tcp_ip4_spec);
+ if (err)
+ return err;
+
+ err = ice_ntuple_l4_proto_to_port(l4_proto, &src_port, &dst_port);
+ if (err)
+ return err;
+
*perfect_fltr = true;
ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4 | l4_proto);
/* IP source address */
- if (tcp_ip4_spec->ip4src == htonl(0xFFFFFFFF))
+ if (tcp_ip4_spec->ip4src == htonl(ICE_FULL_IPV4_ADDR_MASK))
ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_SA,
ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
ICE_FLOW_FLD_OFF_INVAL, false);
@@ -985,7 +1026,7 @@ ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
return -EOPNOTSUPP;
/* IP destination address */
- if (tcp_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
+ if (tcp_ip4_spec->ip4dst == htonl(ICE_FULL_IPV4_ADDR_MASK))
ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_DA,
ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
ICE_FLOW_FLD_OFF_INVAL, false);
@@ -995,7 +1036,7 @@ ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
return -EOPNOTSUPP;
/* Layer 4 source port */
- if (tcp_ip4_spec->psrc == htons(0xFFFF))
+ if (tcp_ip4_spec->psrc == htons(ICE_FULL_PORT_MASK))
ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
false);
@@ -1005,7 +1046,7 @@ ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
return -EOPNOTSUPP;
/* Layer 4 destination port */
- if (tcp_ip4_spec->pdst == htons(0xFFFF))
+ if (tcp_ip4_spec->pdst == htons(ICE_FULL_PORT_MASK))
ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
false);
@@ -1018,19 +1059,12 @@ ice_set_fdir_ip4_seg(struct ice_flow_seg_info *seg,
}
/**
- * ice_set_fdir_ip4_usr_seg
- * @seg: flow segment for programming
+ * ice_ntuple_check_ip4_usr_seg - Check valid fields are provided for filter
* @usr_ip4_spec: ethtool userdef packet offset
- * @perfect_fltr: only valid on success; returns true if perfect filter,
- * false if not
*
- * Set the offset data into the flow segment to be used to program HW
- * table for IPv4
+ * Return: 0 if fields valid, negative otherwise
*/
-static int
-ice_set_fdir_ip4_usr_seg(struct ice_flow_seg_info *seg,
- struct ethtool_usrip4_spec *usr_ip4_spec,
- bool *perfect_fltr)
+int ice_ntuple_check_ip4_usr_seg(struct ethtool_usrip4_spec *usr_ip4_spec)
{
/* first 4 bytes of Layer 4 header */
if (usr_ip4_spec->l4_4_bytes)
@@ -1046,11 +1080,36 @@ ice_set_fdir_ip4_usr_seg(struct ice_flow_seg_info *seg,
if (!usr_ip4_spec->ip4src && !usr_ip4_spec->ip4dst)
return -EINVAL;
+ return 0;
+}
+
+/**
+ * ice_set_fdir_ip4_usr_seg - setup flow segment based on IPv4
+ * @seg: flow segment for programming
+ * @usr_ip4_spec: ethtool userdef packet offset
+ * @perfect_fltr: only set on success; returns true if perfect filter, false if
+ * not
+ *
+ * Set the offset data into the flow segment to be used to program HW
+ * table for IPv4
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_set_fdir_ip4_usr_seg(struct ice_flow_seg_info *seg,
+ struct ethtool_usrip4_spec *usr_ip4_spec,
+ bool *perfect_fltr)
+{
+ int err;
+
+ err = ice_ntuple_check_ip4_usr_seg(usr_ip4_spec);
+ if (err)
+ return err;
+
*perfect_fltr = true;
ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4);
/* IP source address */
- if (usr_ip4_spec->ip4src == htonl(0xFFFFFFFF))
+ if (usr_ip4_spec->ip4src == htonl(ICE_FULL_IPV4_ADDR_MASK))
ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_SA,
ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
ICE_FLOW_FLD_OFF_INVAL, false);
@@ -1060,7 +1119,7 @@ ice_set_fdir_ip4_usr_seg(struct ice_flow_seg_info *seg,
return -EOPNOTSUPP;
/* IP destination address */
- if (usr_ip4_spec->ip4dst == htonl(0xFFFFFFFF))
+ if (usr_ip4_spec->ip4dst == htonl(ICE_FULL_IPV4_ADDR_MASK))
ice_flow_set_fld(seg, ICE_FLOW_FIELD_IDX_IPV4_DA,
ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
ICE_FLOW_FLD_OFF_INVAL, false);
@@ -1073,23 +1132,13 @@ ice_set_fdir_ip4_usr_seg(struct ice_flow_seg_info *seg,
}
/**
- * ice_set_fdir_ip6_seg
- * @seg: flow segment for programming
+ * ice_ntuple_check_ip6_seg - Check valid fields are provided for filter
* @tcp_ip6_spec: mask data from ethtool
- * @l4_proto: Layer 4 protocol to program
- * @perfect_fltr: only valid on success; returns true if perfect filter,
- * false if not
*
- * Set the mask data into the flow segment to be used to program HW
- * table based on provided L4 protocol for IPv6
+ * Return: 0 if fields valid, negative otherwise
*/
-static int
-ice_set_fdir_ip6_seg(struct ice_flow_seg_info *seg,
- struct ethtool_tcpip6_spec *tcp_ip6_spec,
- enum ice_flow_seg_hdr l4_proto, bool *perfect_fltr)
+static int ice_ntuple_check_ip6_seg(struct ethtool_tcpip6_spec *tcp_ip6_spec)
{
- enum ice_flow_field src_port, dst_port;
-
/* make sure we don't have any empty rule */
if (!memcmp(tcp_ip6_spec->ip6src, &zero_ipv6_addr_mask,
sizeof(struct in6_addr)) &&
@@ -1102,18 +1151,37 @@ ice_set_fdir_ip6_seg(struct ice_flow_seg_info *seg,
if (tcp_ip6_spec->tclass)
return -EOPNOTSUPP;
- if (l4_proto == ICE_FLOW_SEG_HDR_TCP) {
- src_port = ICE_FLOW_FIELD_IDX_TCP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_TCP_DST_PORT;
- } else if (l4_proto == ICE_FLOW_SEG_HDR_UDP) {
- src_port = ICE_FLOW_FIELD_IDX_UDP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_UDP_DST_PORT;
- } else if (l4_proto == ICE_FLOW_SEG_HDR_SCTP) {
- src_port = ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT;
- dst_port = ICE_FLOW_FIELD_IDX_SCTP_DST_PORT;
- } else {
- return -EINVAL;
- }
+ return 0;
+}
+
+/**
+ * ice_set_fdir_ip6_seg - setup flow segment based on IPv6 and L4 proto
+ * @seg: flow segment for programming
+ * @tcp_ip6_spec: mask data from ethtool
+ * @l4_proto: Layer 4 protocol to program
+ * @perfect_fltr: only valid on success; returns true if perfect filter,
+ * false if not
+ *
+ * Set the mask data into the flow segment to be used to program HW
+ * table based on provided L4 protocol for IPv6
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_set_fdir_ip6_seg(struct ice_flow_seg_info *seg,
+ struct ethtool_tcpip6_spec *tcp_ip6_spec,
+ enum ice_flow_seg_hdr l4_proto,
+ bool *perfect_fltr)
+{
+ enum ice_flow_field src_port, dst_port;
+ int err;
+
+ err = ice_ntuple_check_ip6_seg(tcp_ip6_spec);
+ if (err)
+ return err;
+
+ err = ice_ntuple_l4_proto_to_port(l4_proto, &src_port, &dst_port);
+ if (err)
+ return err;
*perfect_fltr = true;
ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV6 | l4_proto);
@@ -1141,7 +1209,7 @@ ice_set_fdir_ip6_seg(struct ice_flow_seg_info *seg,
return -EOPNOTSUPP;
/* Layer 4 source port */
- if (tcp_ip6_spec->psrc == htons(0xFFFF))
+ if (tcp_ip6_spec->psrc == htons(ICE_FULL_PORT_MASK))
ice_flow_set_fld(seg, src_port, ICE_FLOW_FLD_OFF_INVAL,
ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
false);
@@ -1151,7 +1219,7 @@ ice_set_fdir_ip6_seg(struct ice_flow_seg_info *seg,
return -EOPNOTSUPP;
/* Layer 4 destination port */
- if (tcp_ip6_spec->pdst == htons(0xFFFF))
+ if (tcp_ip6_spec->pdst == htons(ICE_FULL_PORT_MASK))
ice_flow_set_fld(seg, dst_port, ICE_FLOW_FLD_OFF_INVAL,
ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
false);
@@ -1164,19 +1232,13 @@ ice_set_fdir_ip6_seg(struct ice_flow_seg_info *seg,
}
/**
- * ice_set_fdir_ip6_usr_seg
- * @seg: flow segment for programming
+ * ice_ntuple_check_ip6_usr_seg - Check valid fields are provided for filter
* @usr_ip6_spec: ethtool userdef packet offset
- * @perfect_fltr: only valid on success; returns true if perfect filter,
- * false if not
*
- * Set the offset data into the flow segment to be used to program HW
- * table for IPv6
+ * Return: 0 if fields valid, negative otherwise
*/
static int
-ice_set_fdir_ip6_usr_seg(struct ice_flow_seg_info *seg,
- struct ethtool_usrip6_spec *usr_ip6_spec,
- bool *perfect_fltr)
+ice_ntuple_check_ip6_usr_seg(struct ethtool_usrip6_spec *usr_ip6_spec)
{
/* filtering on Layer 4 bytes not supported */
if (usr_ip6_spec->l4_4_bytes)
@@ -1194,6 +1256,31 @@ ice_set_fdir_ip6_usr_seg(struct ice_flow_seg_info *seg,
sizeof(struct in6_addr)))
return -EINVAL;
+ return 0;
+}
+
+/**
+ * ice_set_fdir_ip6_usr_seg - setup flow segment based on IPv6
+ * @seg: flow segment for programming
+ * @usr_ip6_spec: ethtool userdef packet offset
+ * @perfect_fltr: only set on success; returns true if perfect filter, false if
+ * not
+ *
+ * Set the offset data into the flow segment to be used to program HW
+ * table for IPv6
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_set_fdir_ip6_usr_seg(struct ice_flow_seg_info *seg,
+ struct ethtool_usrip6_spec *usr_ip6_spec,
+ bool *perfect_fltr)
+{
+ int err;
+
+ err = ice_ntuple_check_ip6_usr_seg(usr_ip6_spec);
+ if (err)
+ return err;
+
*perfect_fltr = true;
ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV6);
@@ -1813,6 +1900,60 @@ int ice_del_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
return val;
}
+/**
+ * ice_is_acl_filter - Check if it's a FD or ACL filter
+ * @fsp: pointer to ethtool Rx flow specification
+ *
+ * If any field of the provided filter is using a partial mask then this is
+ * an ACL filter.
+ *
+ * Return: true if ACL filter, false otherwise
+ */
+static bool ice_is_acl_filter(struct ethtool_rx_flow_spec *fsp)
+{
+ struct ethtool_tcpip4_spec *tcp_ip4_spec;
+ struct ethtool_usrip4_spec *usr_ip4_spec;
+
+ switch (fsp->flow_type & ~FLOW_EXT) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ tcp_ip4_spec = &fsp->m_u.tcp_ip4_spec;
+
+ if (tcp_ip4_spec->ip4src &&
+ tcp_ip4_spec->ip4src != htonl(ICE_FULL_IPV4_ADDR_MASK))
+ return true;
+
+ if (tcp_ip4_spec->ip4dst &&
+ tcp_ip4_spec->ip4dst != htonl(ICE_FULL_IPV4_ADDR_MASK))
+ return true;
+
+ if (tcp_ip4_spec->psrc &&
+ tcp_ip4_spec->psrc != htons(ICE_FULL_PORT_MASK))
+ return true;
+
+ if (tcp_ip4_spec->pdst &&
+ tcp_ip4_spec->pdst != htons(ICE_FULL_PORT_MASK))
+ return true;
+
+ break;
+ case IPV4_USER_FLOW:
+ usr_ip4_spec = &fsp->m_u.usr_ip4_spec;
+
+ if (usr_ip4_spec->ip4src &&
+ usr_ip4_spec->ip4src != htonl(ICE_FULL_IPV4_ADDR_MASK))
+ return true;
+
+ if (usr_ip4_spec->ip4dst &&
+ usr_ip4_spec->ip4dst != htonl(ICE_FULL_IPV4_ADDR_MASK))
+ return true;
+
+ break;
+ }
+
+ return false;
+}
+
/**
* ice_update_ring_dest_vsi - update dest ring and dest VSI
* @vsi: pointer to target VSI
@@ -2030,7 +2171,7 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
/* Do not program filters during reset */
if (ice_is_reset_in_progress(pf->state)) {
- dev_err(dev, "Device is resetting - adding Flow Director filters not supported during reset\n");
+ dev_err(dev, "Device is resetting - adding ntuple filters not supported during reset\n");
return -EBUSY;
}
@@ -2042,10 +2183,6 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
if (fsp->flow_type & FLOW_MAC_EXT)
return -EINVAL;
- ret = ice_cfg_fdir_xtrct_seq(pf, fsp, &userdata);
- if (ret)
- return ret;
-
max_location = ice_ntuple_get_max_fltr_cnt(hw);
if (fsp->location >= max_location) {
dev_err(dev, "Failed to add filter. The number of ntuple filters or provided location exceed max %d.\n",
@@ -2053,6 +2190,14 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
return -ENOSPC;
}
+ /* ACL filter */
+ if (pf->hw.acl_tbl && ice_is_acl_filter(fsp))
+ return ice_acl_add_rule_ethtool(vsi, cmd);
+
+ ret = ice_cfg_fdir_xtrct_seq(pf, fsp, &userdata);
+ if (ret)
+ return ret;
+
/* return error if not an update and no available filters */
fltrs_needed = ice_get_open_tunnel_port(hw, &tunnel_port, TNL_ALL) ? 2 : 1;
if (!ice_fdir_find_fltr_by_idx(hw, fsp->location) &&
diff --git a/drivers/net/ethernet/intel/ice/ice_flex_pipe.c b/drivers/net/ethernet/intel/ice/ice_flex_pipe.c
index bb1d12f952cf..d255ffcd5c86 100644
--- a/drivers/net/ethernet/intel/ice/ice_flex_pipe.c
+++ b/drivers/net/ethernet/intel/ice/ice_flex_pipe.c
@@ -1259,6 +1259,9 @@ ice_find_prof_id_with_mask(struct ice_hw *hw, enum ice_block blk,
static bool ice_prof_id_rsrc_type(enum ice_block blk, u16 *rsrc_type)
{
switch (blk) {
+ case ICE_BLK_ACL:
+ *rsrc_type = ICE_AQC_RES_TYPE_ACL_PROF_BLDR_PROFID;
+ break;
case ICE_BLK_FD:
*rsrc_type = ICE_AQC_RES_TYPE_FD_PROF_BLDR_PROFID;
break;
@@ -1279,6 +1282,9 @@ static bool ice_prof_id_rsrc_type(enum ice_block blk, u16 *rsrc_type)
static bool ice_tcam_ent_rsrc_type(enum ice_block blk, u16 *rsrc_type)
{
switch (blk) {
+ case ICE_BLK_ACL:
+ *rsrc_type = ICE_AQC_RES_TYPE_ACL_PROF_BLDR_TCAM;
+ break;
case ICE_BLK_FD:
*rsrc_type = ICE_AQC_RES_TYPE_FD_PROF_BLDR_TCAM;
break;
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.c b/drivers/net/ethernet/intel/ice/ice_flow.c
index 121552c644cd..864bbda7e880 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.c
+++ b/drivers/net/ethernet/intel/ice/ice_flow.c
@@ -3,6 +3,7 @@
#include "ice_common.h"
#include "ice_flow.h"
+#include "ice_acl.h"
#include <net/gre.h>
/* Size of known protocol header fields */
@@ -989,6 +990,43 @@ static int ice_flow_proc_seg_hdrs(struct ice_flow_prof_params *params)
return 0;
}
+/**
+ * ice_flow_xtract_pkt_flags - Create an extr sequence entry for packet flags
+ * @hw: pointer to the HW struct
+ * @params: information about the flow to be processed
+ * @flags: The value of pkt_flags[x:x] in Rx/Tx MDID metadata.
+ *
+ * Allocate an extraction sequence entries for a DWORD size chunk of the packet
+ * flags.
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_flow_xtract_pkt_flags(struct ice_hw *hw,
+ struct ice_flow_prof_params *params,
+ enum ice_flex_mdid_pkt_flags flags)
+{
+ u8 fv_words = hw->blk[params->blk].es.fvw;
+ u8 idx;
+
+ /* Make sure the number of extraction sequence entries required does not
+ * exceed the block's capacity.
+ */
+ if (params->es_cnt >= fv_words)
+ return -ENOSPC;
+
+ /* some blocks require a reversed field vector layout */
+ if (hw->blk[params->blk].es.reverse)
+ idx = fv_words - params->es_cnt - 1;
+ else
+ idx = params->es_cnt;
+
+ params->es[idx].prot_id = ICE_PROT_META_ID;
+ params->es[idx].off = flags;
+ params->es_cnt++;
+
+ return 0;
+}
+
/**
* ice_flow_xtract_fld - Create an extraction sequence entry for the given field
* @hw: pointer to the HW struct
@@ -1287,6 +1325,16 @@ ice_flow_create_xtrct_seq(struct ice_hw *hw,
int status = 0;
u8 i;
+ /* For ACL, we also need to extract the direction bit (Rx,Tx) data from
+ * packet flags
+ */
+ if (params->blk == ICE_BLK_ACL) {
+ status = ice_flow_xtract_pkt_flags(hw, params,
+ ICE_RX_MDID_PKT_FLAGS_15_0);
+ if (status)
+ return status;
+ }
+
for (i = 0; i < prof->segs_cnt; i++) {
u64 match = params->prof->segs[i].match;
enum ice_flow_field j;
@@ -1308,6 +1356,123 @@ ice_flow_create_xtrct_seq(struct ice_hw *hw,
return status;
}
+/**
+ * ice_flow_sel_acl_scen - return specific scenario
+ * @hw: pointer to the hardware structure
+ * @params: information about the flow to be processed
+ *
+ * Return (through @params) the specific scenario based on params.
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_flow_sel_acl_scen(struct ice_hw *hw,
+ struct ice_flow_prof_params *params)
+{
+ /* Find the best-fit scenario for the provided match width */
+ struct ice_acl_scen *cand_scen = NULL, *scen;
+
+ if (!hw->acl_tbl)
+ return -ENOENT;
+
+ /* Loop through each scenario and match against the scenario width
+ * to select the specific scenario
+ */
+ list_for_each_entry(scen, &hw->acl_tbl->scens, list_entry)
+ if (scen->eff_width >= params->entry_length &&
+ (!cand_scen || cand_scen->eff_width > scen->eff_width))
+ cand_scen = scen;
+ if (!cand_scen)
+ return -ENOENT;
+
+ params->prof->cfg.scen = cand_scen;
+
+ return 0;
+}
+
+/**
+ * ice_flow_acl_def_entry_frmt - Determine the layout of flow entries
+ * @params: information about the flow to be processed
+ *
+ * Return: 0 on success, negative on error
+ */
+static int
+ice_flow_acl_def_entry_frmt(struct ice_flow_prof_params *params)
+{
+ u16 index, range_idx = 0;
+
+ index = ICE_AQC_ACL_PROF_BYTE_SEL_START_IDX;
+
+ for (int i = 0; i < params->prof->segs_cnt; i++) {
+ struct ice_flow_seg_info *seg = ¶ms->prof->segs[i];
+ int j;
+
+ for_each_set_bit(j, (unsigned long *)&seg->match,
+ ICE_FLOW_FIELD_IDX_MAX) {
+ struct ice_flow_fld_info *fld = &seg->fields[j];
+
+ fld->entry.mask = ICE_FLOW_FLD_OFF_INVAL;
+
+ if (fld->type == ICE_FLOW_FLD_TYPE_RANGE) {
+ fld->entry.last = ICE_FLOW_FLD_OFF_INVAL;
+
+ /* Range checking only supported for single
+ * words
+ */
+ if (DIV_ROUND_UP(ice_flds_info[j].size +
+ fld->xtrct.disp,
+ BITS_PER_BYTE * 2) > 1)
+ return -EINVAL;
+
+ /* Ranges must define low and high values */
+ if (fld->src.val == ICE_FLOW_FLD_OFF_INVAL ||
+ fld->src.last == ICE_FLOW_FLD_OFF_INVAL)
+ return -EINVAL;
+
+ fld->entry.val = range_idx++;
+ } else {
+ /* Store adjusted byte-length of field for later
+ * use, taking into account potential
+ * non-byte-aligned displacement
+ */
+ fld->entry.last =
+ DIV_ROUND_UP(ice_flds_info[j].size +
+ (fld->xtrct.disp %
+ BITS_PER_BYTE),
+ BITS_PER_BYTE);
+ fld->entry.val = index;
+ index += fld->entry.last;
+ }
+ }
+
+ for (j = 0; j < seg->raws_cnt; j++) {
+ struct ice_flow_seg_fld_raw *raw = &seg->raws[j];
+
+ raw->info.entry.mask = ICE_FLOW_FLD_OFF_INVAL;
+ raw->info.entry.val = index;
+ raw->info.entry.last = raw->info.src.last;
+ index += raw->info.entry.last;
+ }
+ }
+
+ /* Currently only support using the byte selection base, which only
+ * allows for an effective entry size of 30 bytes. Reject anything
+ * larger.
+ */
+ if (index > ICE_AQC_ACL_PROF_BYTE_SEL_ELEMS)
+ return -EINVAL;
+
+ /* Only 8 range checkers per profile, reject anything trying to use
+ * more
+ */
+ if (range_idx > ICE_AQC_ACL_PROF_RANGES_NUM_CFG)
+ return -EINVAL;
+
+ /* Store # bytes required for entry for later use */
+ params->entry_length = index - ICE_AQC_ACL_PROF_BYTE_SEL_START_IDX;
+
+ return 0;
+}
+
/**
* ice_flow_proc_segs - process all packet segments associated with a profile
* @hw: pointer to the HW struct
@@ -1331,6 +1496,14 @@ ice_flow_proc_segs(struct ice_hw *hw, struct ice_flow_prof_params *params)
case ICE_BLK_RSS:
status = 0;
break;
+ case ICE_BLK_ACL:
+ status = ice_flow_acl_def_entry_frmt(params);
+ if (status)
+ return status;
+ status = ice_flow_sel_acl_scen(hw, params);
+ if (status)
+ return status;
+ break;
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 23d6b8311ff9..59036a22ba91 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4326,19 +4326,25 @@ static int ice_send_version(struct ice_pf *pf)
}
/**
- * ice_init_acl - Initializes the ACL block
+ * ice_init_acl - initialize the ACL block and allocate necessary structs
* @pf: ptr to PF device
*
* Return: 0 on success, negative on error
*/
static int ice_init_acl(struct ice_pf *pf)
{
+ struct device *dev = ice_pf_to_dev(pf);
struct ice_acl_tbl_params params = {};
struct ice_hw *hw = &pf->hw;
int divider;
u16 scen_id;
int err;
+ hw->acl_prof = devm_kcalloc(dev, ICE_FLTR_PTYPE_MAX,
+ sizeof(*hw->acl_prof), GFP_KERNEL);
+ if (!hw->acl_prof)
+ return -ENOMEM;
+
/* Creates a single ACL table that consist of src_ip(4 byte),
* dest_ip(4 byte), src_port(2 byte) and dst_port(2 byte) for a total
* of 12 bytes (96 bits), hence 120 bit wide keys, i.e. 3 TCAM slices.
@@ -4358,7 +4364,7 @@ static int ice_init_acl(struct ice_pf *pf)
err = ice_acl_create_tbl(hw, ¶ms);
if (err)
- return err;
+ goto free_prof;
err = ice_acl_create_scen(hw, params.width, params.depth, &scen_id);
if (err)
@@ -4368,17 +4374,36 @@ static int ice_init_acl(struct ice_pf *pf)
destroy_table:
ice_acl_destroy_tbl(hw);
+free_prof:
+ devm_kfree(dev, hw->acl_prof);
+ hw->acl_prof = NULL;
return err;
}
/**
- * ice_deinit_acl - Unroll the initialization of the ACL block
+ * ice_deinit_acl - unroll the initialization of the ACL block
* @pf: ptr to PF device
*/
static void ice_deinit_acl(struct ice_pf *pf)
{
- ice_acl_destroy_tbl(&pf->hw);
+ struct device *dev = ice_pf_to_dev(pf);
+ struct ice_hw *hw = &pf->hw;
+
+ ice_acl_destroy_tbl(hw);
+
+ for (int i = 0; i < ICE_FLTR_PTYPE_MAX; i++) {
+ struct ice_fd_hw_prof *hw_prof = hw->acl_prof[i];
+
+ if (!hw_prof)
+ continue;
+
+ kfree(hw_prof->fdir_seg[0]);
+ kfree(hw_prof);
+ }
+
+ devm_kfree(dev, hw->acl_prof);
+ hw->acl_prof = NULL;
}
/**
--
2.49.0
^ permalink raw reply related
* [PATCH iwl-next v2 08/10] ice: program ACL entry
From: Marcin Szycik @ 2026-04-09 12:00 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Chinh Cao, Tony Nguyen
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
From: Real Valiquette <real.valiquette@intel.com>
Complete the filter programming process; set the flow entry and action into
the scenario and write it to hardware. Configure the VSI for ACL filters.
Co-developed-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Real Valiquette <real.valiquette@intel.com>
Co-developed-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
---
v2:
* Use plain alloc instead of devm_ for ice_flow_entry::acts
* Use FIELD_PREP_CONST() for ICE_ACL_RX_*_MISS_CNTR
* Fix wrong struct ice_acl_act_entry alloc count in
ice_flow_acl_add_scen_entry_sync() - was e->entry_sz, which is an
unrelated value
* Only set acts_cnt after successful allocation in
ice_flow_acl_add_scen_entry_sync()
* Return -EINVAL instead of -ENOSPC on wrong index in
ice_acl_scen_free_entry_idx()
---
drivers/net/ethernet/intel/ice/ice.h | 2 +
drivers/net/ethernet/intel/ice/ice_acl.h | 21 +
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 2 +
drivers/net/ethernet/intel/ice/ice_flow.h | 3 +
drivers/net/ethernet/intel/ice/ice_acl.c | 53 ++-
drivers/net/ethernet/intel/ice/ice_acl_ctrl.c | 251 +++++++++++
drivers/net/ethernet/intel/ice/ice_acl_main.c | 4 +
.../ethernet/intel/ice/ice_ethtool_ntuple.c | 48 ++-
drivers/net/ethernet/intel/ice/ice_flow.c | 395 ++++++++++++++++++
drivers/net/ethernet/intel/ice/ice_lib.c | 10 +-
10 files changed, 782 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 9e6643931022..f9a43daf04fe 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -1061,6 +1061,8 @@ void ice_aq_prep_for_event(struct ice_pf *pf, struct ice_aq_task *task,
u16 opcode);
int ice_aq_wait_for_event(struct ice_pf *pf, struct ice_aq_task *task,
unsigned long timeout);
+int ice_ntuple_update_list_entry(struct ice_pf *pf,
+ struct ice_ntuple_fltr *input, int fltr_idx);
int ice_open(struct net_device *netdev);
int ice_open_internal(struct net_device *netdev);
int ice_stop(struct net_device *netdev);
diff --git a/drivers/net/ethernet/intel/ice/ice_acl.h b/drivers/net/ethernet/intel/ice/ice_acl.h
index 3a4adcf368cf..0b5651401eb7 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl.h
+++ b/drivers/net/ethernet/intel/ice/ice_acl.h
@@ -39,6 +39,7 @@ struct ice_acl_tbl {
DECLARE_BITMAP(avail, ICE_AQC_ACL_ALLOC_UNITS);
};
+#define ICE_MAX_ACL_TCAM_ENTRY (ICE_AQC_ACL_TCAM_DEPTH * ICE_AQC_ACL_SLICES)
enum ice_acl_entry_prio {
ICE_ACL_PRIO_LOW = 0,
ICE_ACL_PRIO_NORMAL,
@@ -65,6 +66,11 @@ struct ice_acl_scen {
* participate in this scenario
*/
DECLARE_BITMAP(act_mem_bitmap, ICE_AQC_MAX_ACTION_MEMORIES);
+
+ /* If nth bit of entry_bitmap is set, then nth entry will
+ * be available in this scenario
+ */
+ DECLARE_BITMAP(entry_bitmap, ICE_MAX_ACL_TCAM_ENTRY);
u16 first_idx[ICE_ACL_MAX_PRIO];
u16 last_idx[ICE_ACL_MAX_PRIO];
@@ -137,6 +143,12 @@ int ice_aq_alloc_acl_cntrs(struct ice_hw *hw, struct ice_acl_cntrs *cntrs,
struct ice_sq_cd *cd);
int ice_aq_dealloc_acl_cntrs(struct ice_hw *hw, struct ice_acl_cntrs *cntrs,
struct ice_sq_cd *cd);
+int ice_prog_acl_prof_ranges(struct ice_hw *hw, u8 prof_id,
+ struct ice_aqc_acl_profile_ranges *buf,
+ struct ice_sq_cd *cd);
+int ice_query_acl_prof_ranges(struct ice_hw *hw, u8 prof_id,
+ struct ice_aqc_acl_profile_ranges *buf,
+ struct ice_sq_cd *cd);
int ice_aq_alloc_acl_scen(struct ice_hw *hw, u16 *scen_id,
struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd);
int ice_aq_dealloc_acl_scen(struct ice_hw *hw, u16 scen_id,
@@ -145,5 +157,14 @@ int ice_aq_update_acl_scen(struct ice_hw *hw, u16 scen_id,
struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd);
int ice_aq_query_acl_scen(struct ice_hw *hw, u16 scen_id,
struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd);
+int ice_acl_add_entry(struct ice_hw *hw, struct ice_acl_scen *scen,
+ enum ice_acl_entry_prio prio, u8 *keys, u8 *inverts,
+ struct ice_acl_act_entry *acts, u8 acts_cnt,
+ u16 *entry_idx);
+int ice_acl_prog_act(struct ice_hw *hw, struct ice_acl_scen *scen,
+ struct ice_acl_act_entry *acts, u8 acts_cnt,
+ u16 entry_idx);
+int ice_acl_rem_entry(struct ice_hw *hw, struct ice_acl_scen *scen,
+ u16 entry_idx);
#endif /* _ICE_ACL_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index b494fa6e0943..d41b2427482d 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -3032,8 +3032,10 @@ enum ice_adminq_opc {
ice_aqc_opc_update_acl_scen = 0x0C1B,
ice_aqc_opc_program_acl_actpair = 0x0C1C,
ice_aqc_opc_program_acl_prof_extraction = 0x0C1D,
+ ice_aqc_opc_program_acl_prof_ranges = 0x0C1E,
ice_aqc_opc_program_acl_entry = 0x0C20,
ice_aqc_opc_query_acl_prof = 0x0C21,
+ ice_aqc_opc_query_acl_prof_ranges = 0x0C22,
ice_aqc_opc_query_acl_scen = 0x0C23,
/* Tx queue handling commands/events */
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.h b/drivers/net/ethernet/intel/ice/ice_flow.h
index 53456d48f6ae..fffd03c38d15 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.h
+++ b/drivers/net/ethernet/intel/ice/ice_flow.h
@@ -468,6 +468,8 @@ struct ice_flow_entry {
enum ice_flow_priority priority;
u16 vsi_handle;
u16 entry_sz;
+ /* Entry index in the ACL's scenario */
+ u16 scen_entry_idx;
u8 acts_cnt;
};
@@ -535,6 +537,7 @@ ice_flow_add_prof(struct ice_hw *hw, enum ice_block blk, enum ice_flow_dir dir,
struct ice_flow_seg_info *segs, u8 segs_cnt,
bool symm, struct ice_flow_prof **prof);
int ice_flow_rem_prof(struct ice_hw *hw, enum ice_block blk, u64 prof_id);
+u64 ice_flow_find_entry(struct ice_hw *hw, enum ice_block blk, u64 entry_id);
int
ice_flow_set_parser_prof(struct ice_hw *hw, u16 dest_vsi, u16 fdir_vsi,
struct ice_parser_profile *prof, enum ice_block blk);
diff --git a/drivers/net/ethernet/intel/ice/ice_acl.c b/drivers/net/ethernet/intel/ice/ice_acl.c
index 837adbda14e0..3ddb500ea1ec 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl.c
+++ b/drivers/net/ethernet/intel/ice/ice_acl.c
@@ -156,7 +156,8 @@ static int ice_acl_prof_aq_send(struct ice_hw *hw, u16 opc, u8 prof_id,
cmd = libie_aq_raw(&desc);
cmd->profile_id = prof_id;
- if (opc == ice_aqc_opc_program_acl_prof_extraction)
+ if (opc == ice_aqc_opc_program_acl_prof_extraction ||
+ opc == ice_aqc_opc_program_acl_prof_ranges)
desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD);
return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
@@ -318,6 +319,56 @@ int ice_aq_dealloc_acl_cntrs(struct ice_hw *hw, struct ice_acl_cntrs *cntrs,
return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
}
+/**
+ * ice_prog_acl_prof_ranges - program ACL profile ranges
+ * @hw: pointer to the HW struct
+ * @prof_id: programmed or updated profile ID
+ * @buf: pointer to input buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Program ACL profile ranges (indirect 0x0C1E)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_prog_acl_prof_ranges(struct ice_hw *hw, u8 prof_id,
+ struct ice_aqc_acl_profile_ranges *buf,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_profile *cmd;
+ struct libie_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc,
+ ice_aqc_opc_program_acl_prof_ranges);
+ cmd = libie_aq_raw(&desc);
+ cmd->profile_id = prof_id;
+ desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD);
+ return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
+}
+
+/**
+ * ice_query_acl_prof_ranges - query ACL profile ranges
+ * @hw: pointer to the HW struct
+ * @prof_id: programmed or updated profile ID
+ * @buf: pointer to response buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Query ACL profile ranges (indirect 0x0C22)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_query_acl_prof_ranges(struct ice_hw *hw, u8 prof_id,
+ struct ice_aqc_acl_profile_ranges *buf,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_profile *cmd;
+ struct libie_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_query_acl_prof_ranges);
+ cmd = libie_aq_raw(&desc);
+ cmd->profile_id = prof_id;
+ return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
+}
+
/**
* ice_aq_alloc_acl_scen - allocate ACL scenario
* @hw: pointer to the HW struct
diff --git a/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c b/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c
index c6148192dc6e..f136c998a85c 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c
+++ b/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c
@@ -6,6 +6,11 @@
/* Determine the TCAM index of entry 'e' within the ACL table */
#define ICE_ACL_TBL_TCAM_IDX(e) ((e) / ICE_AQC_ACL_TCAM_DEPTH)
+/* Determine the entry index within the TCAM */
+#define ICE_ACL_TBL_TCAM_ENTRY_IDX(e) ((e) % ICE_AQC_ACL_TCAM_DEPTH)
+
+#define ICE_ACL_SCEN_ENTRY_INVAL 0xFFFF
+
/**
* ice_acl_init_entry - initialize ACL entry
* @scen: pointer to the scenario struct
@@ -29,6 +34,51 @@ static void ice_acl_init_entry(struct ice_acl_scen *scen)
scen->last_idx[ICE_ACL_PRIO_HIGH] = scen->num_entry / 4 - 1;
}
+/**
+ * ice_acl_scen_assign_entry_idx - find index of an available entry in scenario
+ * @scen: pointer to the scenario struct
+ * @prio: the priority of the flow entry being allocated
+ *
+ * Return: entry index on success, ICE_ACL_SCEN_ENTRY_INVAL on error
+ */
+static u16 ice_acl_scen_assign_entry_idx(struct ice_acl_scen *scen,
+ enum ice_acl_entry_prio prio)
+{
+ u16 first_idx, last_idx, i;
+ s8 step;
+
+ if (prio >= ICE_ACL_MAX_PRIO)
+ return ICE_ACL_SCEN_ENTRY_INVAL;
+
+ first_idx = scen->first_idx[prio];
+ last_idx = scen->last_idx[prio];
+ step = first_idx <= last_idx ? 1 : -1;
+
+ for (i = first_idx; i != last_idx + step; i += step)
+ if (!test_and_set_bit(i, scen->entry_bitmap))
+ return i;
+
+ return ICE_ACL_SCEN_ENTRY_INVAL;
+}
+
+/**
+ * ice_acl_scen_free_entry_idx - mark an entry as available in a scenario
+ * @scen: pointer to the scenario struct
+ * @idx: the index of the flow entry being de-allocated
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_acl_scen_free_entry_idx(struct ice_acl_scen *scen, u16 idx)
+{
+ if (idx >= scen->num_entry)
+ return -EINVAL;
+
+ if (!test_and_clear_bit(idx, scen->entry_bitmap))
+ return -ENOENT;
+
+ return 0;
+}
+
/**
* ice_acl_tbl_calc_end_idx - get end ACL entry index
* @start: start index of the TCAM entry of this partition
@@ -858,3 +908,204 @@ int ice_acl_destroy_tbl(struct ice_hw *hw)
return 0;
}
+
+/**
+ * ice_acl_add_entry - Add a flow entry to ACL scenario
+ * @hw: pointer to the HW struct
+ * @scen: scenario to add the entry to
+ * @prio: priority level of the entry being added
+ * @keys: buffer of the value of the key to be programmed to the ACL entry
+ * @inverts: buffer of the value of the key inverts to be programmed
+ * @acts: pointer to a buffer containing formatted actions
+ * @acts_cnt: indicates the number of actions stored in "acts"
+ * @entry_idx: returned scenario relative index of the added flow entry
+ *
+ * Given an ACL table and a scenario, to add the specified key and key invert
+ * to an available entry in the specified scenario.
+ * The "keys" and "inverts" buffers must be of the size which is the same as
+ * the scenario's width
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_acl_add_entry(struct ice_hw *hw, struct ice_acl_scen *scen,
+ enum ice_acl_entry_prio prio, u8 *keys, u8 *inverts,
+ struct ice_acl_act_entry *acts, u8 acts_cnt,
+ u16 *entry_idx)
+{
+ u8 entry_tcam, num_cscd, offset;
+ struct ice_aqc_acl_data buf = {};
+ int err = 0;
+ u16 idx;
+
+ if (!scen)
+ return -ENOENT;
+
+ *entry_idx = ice_acl_scen_assign_entry_idx(scen, prio);
+ if (*entry_idx >= scen->num_entry) {
+ *entry_idx = 0;
+ return -ENOSPC;
+ }
+
+ /* Determine number of cascaded TCAMs */
+ num_cscd = DIV_ROUND_UP(scen->width, ICE_AQC_ACL_KEY_WIDTH_BYTES);
+
+ entry_tcam = ICE_ACL_TBL_TCAM_IDX(scen->start);
+ idx = ICE_ACL_TBL_TCAM_ENTRY_IDX(scen->start + *entry_idx);
+
+ for (u8 i = 0; i < num_cscd; i++) {
+ /* If the key spans more than one TCAM in the case of cascaded
+ * TCAMs, the key and key inverts need to be properly split
+ * among TCAMs.E.g.bytes 0 - 4 go to an index in the first TCAM
+ * and bytes 5 - 9 go to the same index in the next TCAM, etc.
+ * If the entry spans more than one TCAM in a cascaded TCAM
+ * mode, the programming of the entries in the TCAMs must be in
+ * reversed order - the TCAM entry of the rightmost TCAM should
+ * be programmed first; the TCAM entry of the leftmost TCAM
+ * should be programmed last.
+ */
+ offset = num_cscd - i - 1;
+ memcpy(&buf.entry_key.val,
+ &keys[offset * sizeof(buf.entry_key.val)],
+ sizeof(buf.entry_key.val));
+ memcpy(&buf.entry_key_invert.val,
+ &inverts[offset * sizeof(buf.entry_key_invert.val)],
+ sizeof(buf.entry_key_invert.val));
+ err = ice_aq_program_acl_entry(hw, entry_tcam + offset, idx,
+ &buf, NULL);
+ if (err) {
+ ice_debug(hw, ICE_DBG_ACL, "aq program acl entry failed status: %d\n",
+ err);
+ goto out;
+ }
+ }
+
+ err = ice_acl_prog_act(hw, scen, acts, acts_cnt, *entry_idx);
+
+out:
+ if (err) {
+ ice_acl_rem_entry(hw, scen, *entry_idx);
+ *entry_idx = 0;
+ }
+
+ return err;
+}
+
+/**
+ * ice_acl_prog_act - Program a scenario's action memory
+ * @hw: pointer to the HW struct
+ * @scen: scenario to add the entry to
+ * @acts: pointer to a buffer containing formatted actions
+ * @acts_cnt: indicates the number of actions stored in "acts"
+ * @entry_idx: scenario relative index of the added flow entry
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_acl_prog_act(struct ice_hw *hw, struct ice_acl_scen *scen,
+ struct ice_acl_act_entry *acts, u8 acts_cnt, u16 entry_idx)
+{
+ u8 entry_tcam, num_cscd, i, actx_idx = 0;
+ struct ice_aqc_actpair act_buf = {};
+ int err = 0;
+ u16 idx;
+
+ if (entry_idx >= scen->num_entry)
+ return -ENOSPC;
+
+ /* Determine number of cascaded TCAMs */
+ num_cscd = DIV_ROUND_UP(scen->width, ICE_AQC_ACL_KEY_WIDTH_BYTES);
+
+ entry_tcam = ICE_ACL_TBL_TCAM_IDX(scen->start);
+ idx = ICE_ACL_TBL_TCAM_ENTRY_IDX(scen->start + entry_idx);
+
+ for_each_set_bit(i, scen->act_mem_bitmap, ICE_AQC_MAX_ACTION_MEMORIES) {
+ struct ice_acl_act_mem *mem = &hw->acl_tbl->act_mems[i];
+
+ if (actx_idx >= acts_cnt)
+ break;
+ if (mem->member_of_tcam >= entry_tcam &&
+ mem->member_of_tcam < entry_tcam + num_cscd) {
+ memcpy(&act_buf.act[0], &acts[actx_idx],
+ sizeof(struct ice_acl_act_entry));
+
+ if (++actx_idx < acts_cnt) {
+ memcpy(&act_buf.act[1], &acts[actx_idx],
+ sizeof(struct ice_acl_act_entry));
+ }
+
+ err = ice_aq_program_actpair(hw, i, idx, &act_buf,
+ NULL);
+ if (err) {
+ ice_debug(hw, ICE_DBG_ACL, "program actpair failed status: %d\n",
+ err);
+ break;
+ }
+ actx_idx++;
+ }
+ }
+
+ if (!err && actx_idx < acts_cnt)
+ err = -ENOSPC;
+
+ return err;
+}
+
+/**
+ * ice_acl_rem_entry - Remove a flow entry from an ACL scenario
+ * @hw: pointer to the HW struct
+ * @scen: scenario to remove the entry from
+ * @entry_idx: the scenario-relative index of the flow entry being removed
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_acl_rem_entry(struct ice_hw *hw, struct ice_acl_scen *scen,
+ u16 entry_idx)
+{
+ struct ice_aqc_actpair act_buf = {};
+ struct ice_aqc_acl_data buf;
+ u8 entry_tcam, num_cscd, i;
+ int err = 0;
+ u16 idx;
+
+ if (!scen)
+ return -ENOENT;
+
+ if (entry_idx >= scen->num_entry)
+ return -ENOSPC;
+
+ if (!test_bit(entry_idx, scen->entry_bitmap))
+ return -ENOENT;
+
+ /* Determine number of cascaded TCAMs */
+ num_cscd = DIV_ROUND_UP(scen->width, ICE_AQC_ACL_KEY_WIDTH_BYTES);
+
+ entry_tcam = ICE_ACL_TBL_TCAM_IDX(scen->start);
+ idx = ICE_ACL_TBL_TCAM_ENTRY_IDX(scen->start + entry_idx);
+
+ /* invalidate the flow entry */
+ memset(&buf, 0, sizeof(buf));
+ for (i = 0; i < num_cscd; i++) {
+ err = ice_aq_program_acl_entry(hw, entry_tcam + i, idx, &buf,
+ NULL);
+ if (err)
+ ice_debug(hw, ICE_DBG_ACL, "AQ program ACL entry failed status: %d\n",
+ err);
+ }
+
+ for_each_set_bit(i, scen->act_mem_bitmap, ICE_AQC_MAX_ACTION_MEMORIES) {
+ struct ice_acl_act_mem *mem = &hw->acl_tbl->act_mems[i];
+
+ if (mem->member_of_tcam >= entry_tcam &&
+ mem->member_of_tcam < entry_tcam + num_cscd) {
+ /* Invalidate allocated action pairs */
+ err = ice_aq_program_actpair(hw, i, idx, &act_buf,
+ NULL);
+ if (err)
+ ice_debug(hw, ICE_DBG_ACL, "program actpair failed status: %d\n",
+ err);
+ }
+ }
+
+ ice_acl_scen_free_entry_idx(scen, entry_idx);
+
+ return err;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_acl_main.c b/drivers/net/ethernet/intel/ice/ice_acl_main.c
index 53cca0526756..16228be574ed 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_acl_main.c
@@ -280,6 +280,10 @@ int ice_acl_add_rule_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
hw_prof->entry_h[hw_prof->cnt++][0] = entry_h;
}
+ input->acl_fltr = true;
+ /* input struct is added to the HW filter list */
+ ice_ntuple_update_list_entry(pf, input, fsp->location);
+
return 0;
free_input:
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
index 3e79c0bf40f4..21d4f4e3a1d0 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
@@ -1791,6 +1791,21 @@ void ice_vsi_manage_fdir(struct ice_vsi *vsi, bool ena)
mutex_unlock(&hw->fdir_fltr_lock);
}
+/**
+ * ice_del_acl_ethtool - delete an ACL rule entry
+ * @hw: pointer to HW instance
+ * @fltr: filter structure
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_del_acl_ethtool(struct ice_hw *hw, struct ice_ntuple_fltr *fltr)
+{
+ u64 entry;
+
+ entry = ice_flow_find_entry(hw, ICE_BLK_ACL, fltr->fltr_id);
+ return ice_flow_rem_entry(hw, ICE_BLK_ACL, entry);
+}
+
/**
* ice_fdir_do_rem_flow - delete flow and possibly add perfect flow
* @pf: PF structure
@@ -1824,7 +1839,7 @@ ice_fdir_do_rem_flow(struct ice_pf *pf, enum ice_fltr_ptype flow_type)
*
* Return: 0 on success and negative on errors
*/
-static int
+int
ice_ntuple_update_list_entry(struct ice_pf *pf, struct ice_ntuple_fltr *input,
int fltr_idx)
{
@@ -1843,13 +1858,36 @@ ice_ntuple_update_list_entry(struct ice_pf *pf, struct ice_ntuple_fltr *input,
old_fltr = ice_fdir_find_fltr_by_idx(hw, fltr_idx);
if (old_fltr) {
- err = ice_fdir_write_all_fltr(pf, old_fltr, false);
- if (err)
- return err;
+ if (old_fltr->acl_fltr) {
+ /* ACL filter - if the input buffer is present
+ * then this is an update and we don't want to
+ * delete the filter from the HW. We've already
+ * written the change to the HW at this point, so
+ * just update the SW structures to make sure
+ * everything is hunky-dory. If no input then this
+ * is a delete so we should delete the filter from
+ * the HW and clean up our SW structures.
+ */
+ if (!input) {
+ err = ice_del_acl_ethtool(hw, old_fltr);
+ if (err)
+ return err;
+ }
+ } else {
+ /* FD filter */
+ err = ice_fdir_write_all_fltr(pf, old_fltr, false);
+ if (err)
+ return err;
+ }
+
ice_fdir_update_cntrs(hw, old_fltr->flow_type, false, false);
/* update sb-filters count, specific to ring->channel */
ice_update_per_q_fltr(vsi, old_fltr->orig_q_index, false);
- if (!input && !hw->fdir_fltr_cnt[old_fltr->flow_type])
+ /* Also delete the HW filter info if we have just deleted the
+ * last filter of flow_type.
+ */
+ if (!old_fltr->acl_fltr && !input &&
+ !hw->fdir_fltr_cnt[old_fltr->flow_type])
/* we just deleted the last filter of flow_type so we
* should also delete the HW filter info.
*/
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.c b/drivers/net/ethernet/intel/ice/ice_flow.c
index dce6d2ffcb15..144d8326d4f9 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.c
+++ b/drivers/net/ethernet/intel/ice/ice_flow.c
@@ -1744,6 +1744,16 @@ static int ice_flow_rem_entry_sync(struct ice_hw *hw, enum ice_block blk,
return -EINVAL;
if (blk == ICE_BLK_ACL) {
+ int err;
+
+ if (!entry->prof)
+ return -EINVAL;
+
+ err = ice_acl_rem_entry(hw, entry->prof->cfg.scen,
+ entry->scen_entry_idx);
+ if (err)
+ return err;
+
if (entry->acts_cnt && entry->acts)
ice_flow_acl_free_act_cntr(hw, entry->acts,
entry->acts_cnt);
@@ -1879,10 +1889,34 @@ ice_flow_rem_prof_sync(struct ice_hw *hw, enum ice_block blk,
}
if (blk == ICE_BLK_ACL) {
+ struct ice_aqc_acl_prof_generic_frmt buf;
+ u8 prof_id = 0;
+
/* Disassociate the scenario from the profile for the PF */
status = ice_flow_acl_disassoc_scen(hw, prof);
if (status)
return status;
+
+ status = ice_flow_get_hw_prof(hw, blk, prof->id, &prof_id);
+ if (status)
+ return status;
+
+ status = ice_query_acl_prof(hw, prof_id, &buf, NULL);
+ if (status)
+ return status;
+
+ /* Clear the range-checker if the profile ID is no longer
+ * used by any PF
+ */
+ if (!ice_flow_acl_is_prof_in_use(&buf)) {
+ /* Clear the range-checker value for profile ID */
+ struct ice_aqc_acl_profile_ranges query_rng_buf = {};
+
+ status = ice_prog_acl_prof_ranges(hw, prof_id,
+ &query_rng_buf, NULL);
+ if (status)
+ return status;
+ }
}
/* Remove all hardware profiles associated with this flow profile */
@@ -2214,6 +2248,44 @@ int ice_flow_rem_prof(struct ice_hw *hw, enum ice_block blk, u64 prof_id)
return status;
}
+/**
+ * ice_flow_find_entry - look for a flow entry using its unique ID
+ * @hw: pointer to the HW struct
+ * @blk: classification stage
+ * @entry_id: unique ID to identify this flow entry
+ *
+ * Look for the flow entry with the specified unique ID in all flow profiles of
+ * the specified classification stage.
+ *
+ * Return: flow entry handle if entry found, ICE_FLOW_ENTRY_ID_INVAL otherwise
+ */
+u64 ice_flow_find_entry(struct ice_hw *hw, enum ice_block blk, u64 entry_id)
+{
+ struct ice_flow_entry *found = NULL;
+ struct ice_flow_prof *p;
+
+ mutex_lock(&hw->fl_profs_locks[blk]);
+
+ list_for_each_entry(p, &hw->fl_profs[blk], l_entry) {
+ struct ice_flow_entry *e;
+
+ mutex_lock(&p->entries_lock);
+ list_for_each_entry(e, &p->entries, l_entry)
+ if (e->id == entry_id) {
+ found = e;
+ break;
+ }
+ mutex_unlock(&p->entries_lock);
+
+ if (found)
+ break;
+ }
+
+ mutex_unlock(&hw->fl_profs_locks[blk]);
+
+ return found ? ICE_FLOW_ENTRY_HNDL(found) : ICE_FLOW_ENTRY_HANDLE_INVAL;
+}
+
/**
* ice_flow_acl_check_actions - Checks the ACL rule's actions
* @hw: pointer to the hardware structure
@@ -2541,6 +2613,325 @@ static int ice_flow_acl_frmt_entry(struct ice_hw *hw,
return err;
}
+
+/**
+ * ice_flow_acl_find_scen_entry_cond - Find an ACL scenario entry that matches
+ * the compared data
+ * @prof: pointer to flow profile
+ * @e: pointer to the comparing flow entry
+ * @do_chg_action: decide if we want to change the ACL action
+ * @do_add_entry: decide if we want to add the new ACL entry
+ * @do_rem_entry: decide if we want to remove the current ACL entry
+ *
+ * Find an ACL scenario entry that matches the compared data. Also figure out:
+ * a) If we want to change the ACL action
+ * b) If we want to add the new ACL entry
+ * c) If we want to remove the current ACL entry
+ *
+ * Return: ACL scenario entry, or NULL if not found
+ */
+static struct ice_flow_entry *
+ice_flow_acl_find_scen_entry_cond(struct ice_flow_prof *prof,
+ struct ice_flow_entry *e, bool *do_chg_action,
+ bool *do_add_entry, bool *do_rem_entry)
+{
+ struct ice_flow_entry *p, *return_entry = NULL;
+
+ /* Check if:
+ * a) There exists an entry with same matching data, but different
+ * priority, then we remove this existing ACL entry. Then, we
+ * will add the new entry to the ACL scenario.
+ * b) There exists an entry with same matching data, priority, and
+ * result action, then we do nothing
+ * c) There exists an entry with same matching data, priority, but
+ * different, action, then do only change the action's entry.
+ * d) Else, we add this new entry to the ACL scenario.
+ */
+ *do_chg_action = false;
+ *do_add_entry = true;
+ *do_rem_entry = false;
+ list_for_each_entry(p, &prof->entries, l_entry) {
+ if (memcmp(p->entry, e->entry, p->entry_sz))
+ continue;
+
+ /* From this point, we have the same matching_data. */
+ *do_add_entry = false;
+ return_entry = p;
+
+ if (p->priority != e->priority) {
+ /* matching data && !priority */
+ *do_add_entry = true;
+ *do_rem_entry = true;
+ break;
+ }
+
+ /* From this point, we will have matching_data && priority */
+ if (p->acts_cnt != e->acts_cnt)
+ *do_chg_action = true;
+ for (int i = 0; i < p->acts_cnt; i++) {
+ bool found_not_match = false;
+
+ for (int j = 0; j < e->acts_cnt; j++)
+ if (memcmp(&p->acts[i], &e->acts[j],
+ sizeof(struct ice_flow_action))) {
+ found_not_match = true;
+ break;
+ }
+
+ if (found_not_match) {
+ *do_chg_action = true;
+ break;
+ }
+ }
+
+ /* (do_chg_action = true) means :
+ * matching_data && priority && !result_action
+ * (do_chg_action = false) means :
+ * matching_data && priority && result_action
+ */
+ break;
+ }
+
+ return return_entry;
+}
+
+/**
+ * ice_flow_acl_convert_to_acl_prio - convert flow priority to ACL priority
+ * @p: flow priority
+ *
+ * Return: ACL priority
+ */
+static enum ice_acl_entry_prio
+ice_flow_acl_convert_to_acl_prio(enum ice_flow_priority p)
+{
+ switch (p) {
+ case ICE_FLOW_PRIO_LOW:
+ return ICE_ACL_PRIO_LOW;
+ case ICE_FLOW_PRIO_NORMAL:
+ return ICE_ACL_PRIO_NORMAL;
+ case ICE_FLOW_PRIO_HIGH:
+ return ICE_ACL_PRIO_HIGH;
+ default:
+ return ICE_ACL_PRIO_NORMAL;
+ }
+}
+
+/**
+ * ice_flow_acl_union_rng_chk - Perform union operation between two range-range
+ * checker buffers
+ * @dst_buf: pointer to destination range checker buffer
+ * @src_buf: pointer to source range checker buffer
+ *
+ * Do the union between dst_buf and src_buf range checker buffer, and save the
+ * result back to dst_buf.
+ *
+ * Return: 0 on success, negative on error
+ */
+static int
+ice_flow_acl_union_rng_chk(struct ice_aqc_acl_profile_ranges *dst_buf,
+ struct ice_aqc_acl_profile_ranges *src_buf)
+{
+ if (!dst_buf || !src_buf)
+ return -EINVAL;
+
+ for (int i = 0; i < ICE_AQC_ACL_PROF_RANGES_NUM_CFG; i++) {
+ struct ice_acl_rng_data *cfg_data = NULL, *in_data;
+ bool will_populate = false;
+
+ in_data = &src_buf->checker_cfg[i];
+
+ if (!in_data->mask)
+ break;
+
+ for (int j = 0; j < ICE_AQC_ACL_PROF_RANGES_NUM_CFG; j++) {
+ cfg_data = &dst_buf->checker_cfg[j];
+
+ if (!cfg_data->mask ||
+ !memcmp(cfg_data, in_data,
+ sizeof(struct ice_acl_rng_data))) {
+ will_populate = true;
+ break;
+ }
+ }
+
+ if (will_populate) {
+ memcpy(cfg_data, in_data,
+ sizeof(struct ice_acl_rng_data));
+ } else {
+ /* No available slot left to program range checker */
+ return -ENOSPC;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_flow_acl_add_scen_entry_sync - add entry to ACL scenario sync
+ * @hw: pointer to the hardware structure
+ * @prof: pointer to flow profile
+ * @entry: double pointer to the flow entry
+ *
+ * Look at the current added entries in the corresponding ACL scenario and
+ * perform matching logic to see if we want to add/modify/do nothing with this
+ * new entry.
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_flow_acl_add_scen_entry_sync(struct ice_hw *hw,
+ struct ice_flow_prof *prof,
+ struct ice_flow_entry **entry)
+{
+ bool do_add_entry, do_rem_entry, do_chg_action, do_chg_rng_chk;
+ struct ice_aqc_acl_profile_ranges query_rng_buf, cfg_rng_buf;
+ struct ice_acl_act_entry *acts = NULL;
+ struct ice_flow_entry *exist;
+ struct ice_flow_entry *e;
+ int err = 0;
+
+ e = *entry;
+
+ do_chg_rng_chk = false;
+ if (e->range_buf) {
+ u8 prof_id = 0;
+
+ err = ice_flow_get_hw_prof(hw, ICE_BLK_ACL, prof->id, &prof_id);
+ if (err)
+ return err;
+
+ /* Query the current range-checker value in FW */
+ err = ice_query_acl_prof_ranges(hw, prof_id, &query_rng_buf,
+ NULL);
+ if (err)
+ return err;
+ memcpy(&cfg_rng_buf, &query_rng_buf,
+ sizeof(struct ice_aqc_acl_profile_ranges));
+
+ /* Generate the new range-checker value */
+ err = ice_flow_acl_union_rng_chk(&cfg_rng_buf, e->range_buf);
+ if (err)
+ return err;
+
+ /* Reconfigure the range check if the buffer is changed. */
+ do_chg_rng_chk = false;
+ if (memcmp(&query_rng_buf, &cfg_rng_buf,
+ sizeof(struct ice_aqc_acl_profile_ranges))) {
+ err = ice_prog_acl_prof_ranges(hw, prof_id,
+ &cfg_rng_buf, NULL);
+ if (err)
+ return err;
+
+ do_chg_rng_chk = true;
+ }
+ }
+
+ /* Figure out if we want to (change the ACL action) and/or
+ * (Add the new ACL entry) and/or (Remove the current ACL entry)
+ */
+ exist = ice_flow_acl_find_scen_entry_cond(prof, e, &do_chg_action,
+ &do_add_entry, &do_rem_entry);
+
+ if (do_rem_entry) {
+ err = ice_flow_rem_entry_sync(hw, ICE_BLK_ACL, exist);
+ if (err)
+ return err;
+ }
+
+ /* Prepare the result action buffer */
+ acts = kzalloc_objs(*acts, e->acts_cnt);
+ if (!acts)
+ return -ENOMEM;
+
+ for (int i = 0; i < e->acts_cnt; i++)
+ memcpy(&acts[i], &e->acts[i].data.acl_act,
+ sizeof(struct ice_acl_act_entry));
+
+ if (do_add_entry) {
+ enum ice_acl_entry_prio prio;
+ u8 *keys, *inverts;
+ u16 entry_idx;
+
+ keys = (u8 *)e->entry;
+ inverts = keys + (e->entry_sz / 2);
+ prio = ice_flow_acl_convert_to_acl_prio(e->priority);
+
+ err = ice_acl_add_entry(hw, prof->cfg.scen, prio, keys,
+ inverts, acts, e->acts_cnt,
+ &entry_idx);
+ if (err)
+ goto out;
+
+ e->scen_entry_idx = entry_idx;
+ list_add(&e->l_entry, &prof->entries);
+ } else {
+ if (do_chg_action) {
+ /* For the action memory info, update the SW's copy of
+ * exist entry with e's action memory info
+ */
+ kfree(exist->acts);
+ exist->acts = kzalloc_objs(*exist->acts, e->acts_cnt);
+ if (!exist->acts) {
+ err = -ENOMEM;
+ goto out;
+ }
+ exist->acts_cnt = e->acts_cnt;
+
+ memcpy(exist->acts, e->acts,
+ sizeof(struct ice_flow_action) * e->acts_cnt);
+
+ err = ice_acl_prog_act(hw, prof->cfg.scen, acts,
+ e->acts_cnt,
+ exist->scen_entry_idx);
+ if (err)
+ goto out;
+ }
+
+ if (do_chg_rng_chk) {
+ /* In this case, we want to update the range checker
+ * information of the exist entry
+ */
+ err = ice_flow_acl_union_rng_chk(exist->range_buf,
+ e->range_buf);
+ if (err)
+ goto out;
+ }
+
+ /* As we don't add the new entry to our SW DB, deallocate its
+ * memories, and return the exist entry to the caller
+ */
+ kfree(e->entry);
+ kfree(e->range_buf);
+ kfree(e->acts);
+ devm_kfree(ice_hw_to_dev(hw), e);
+ *entry = exist;
+ }
+out:
+ kfree(acts);
+
+ return err;
+}
+
+/**
+ * ice_flow_acl_add_scen_entry - Add entry to ACL scenario
+ * @hw: pointer to the hardware structure
+ * @prof: pointer to flow profile
+ * @e: double pointer to the flow entry
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_flow_acl_add_scen_entry(struct ice_hw *hw,
+ struct ice_flow_prof *prof,
+ struct ice_flow_entry **e)
+{
+ int err;
+
+ mutex_lock(&prof->entries_lock);
+ err = ice_flow_acl_add_scen_entry_sync(hw, prof, e);
+ mutex_unlock(&prof->entries_lock);
+
+ return err;
+}
+
/**
* ice_flow_add_entry - Add a flow entry
* @hw: pointer to the HW struct
@@ -2609,6 +3000,10 @@ int ice_flow_add_entry(struct ice_hw *hw, enum ice_block blk, u64 prof_id,
acts_cnt);
if (status)
goto out;
+
+ status = ice_flow_acl_add_scen_entry(hw, prof, &e);
+ if (status)
+ goto out;
break;
default:
status = -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 0fcd3675d0e2..39e0c4c0fd51 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -1118,7 +1118,7 @@ static void ice_set_fd_vsi_ctx(struct ice_vsi_ctx *ctxt, struct ice_vsi *vsi)
vsi->type != ICE_VSI_VF && vsi->type != ICE_VSI_CHNL)
return;
- val = ICE_AQ_VSI_PROP_FLOW_DIR_VALID;
+ val = ICE_AQ_VSI_PROP_FLOW_DIR_VALID | ICE_AQ_VSI_PROP_ACL_VALID;
ctxt->info.valid_sections |= cpu_to_le16(val);
dflt_q = 0;
dflt_q_group = 0;
@@ -1144,6 +1144,14 @@ static void ice_set_fd_vsi_ctx(struct ice_vsi_ctx *ctxt, struct ice_vsi *vsi)
/* priority of the default qindex action */
val |= FIELD_PREP(ICE_AQ_VSI_FD_DEF_PRIORITY_M, dflt_q_prio);
ctxt->info.fd_report_opt = cpu_to_le16(val);
+
+#define ICE_ACL_RX_PROF_MISS_CNTR \
+ FIELD_PREP_CONST(ICE_AQ_VSI_ACL_DEF_RX_PROF_M, 2)
+#define ICE_ACL_RX_TBL_MISS_CNTR \
+ FIELD_PREP_CONST(ICE_AQ_VSI_ACL_DEF_RX_TABLE_M, 3)
+
+ val = ICE_ACL_RX_PROF_MISS_CNTR | ICE_ACL_RX_TBL_MISS_CNTR;
+ ctxt->info.acl_def_act = cpu_to_le16(val);
}
/**
--
2.49.0
^ permalink raw reply related
* [PATCH iwl-next v2 03/10] ice: initialize ACL scenario
From: Marcin Szycik @ 2026-04-09 11:59 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Chinh Cao, Tony Nguyen, Aleksandr Loktionov
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
From: Real Valiquette <real.valiquette@intel.com>
Complete initialization of the ACL table by programming the table with an
initial scenario. The scenario stores the data for the filtering rules.
Adjust reporting of ntuple filters to include ACL filters.
Co-developed-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Real Valiquette <real.valiquette@intel.com>
Co-developed-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
---
v2:
* Add unroll in ice_init_acl() in case of ice_acl_create_scen() failure
---
drivers/net/ethernet/intel/ice/ice.h | 1 +
drivers/net/ethernet/intel/ice/ice_acl.h | 8 +
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 29 +
drivers/net/ethernet/intel/ice/ice_fdir.h | 6 +-
drivers/net/ethernet/intel/ice/ice_flow.h | 7 +
drivers/net/ethernet/intel/ice/ice_type.h | 2 +
drivers/net/ethernet/intel/ice/ice_acl.c | 116 ++++
drivers/net/ethernet/intel/ice/ice_acl_ctrl.c | 558 ++++++++++++++++++
drivers/net/ethernet/intel/ice/ice_ethtool.c | 4 +-
.../ethernet/intel/ice/ice_ethtool_ntuple.c | 45 +-
drivers/net/ethernet/intel/ice/ice_fdir.c | 12 +-
drivers/net/ethernet/intel/ice/ice_main.c | 17 +-
12 files changed, 789 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 3a51a033296c..e064323d983c 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -1024,6 +1024,7 @@ void ice_vsi_manage_fdir(struct ice_vsi *vsi, bool ena);
int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
int ice_del_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
int ice_get_ethtool_fdir_entry(struct ice_hw *hw, struct ethtool_rxnfc *cmd);
+u32 ice_ntuple_get_max_fltr_cnt(struct ice_hw *hw);
int
ice_get_fdir_fltr_ids(struct ice_hw *hw, struct ethtool_rxnfc *cmd,
u32 *rule_locs);
diff --git a/drivers/net/ethernet/intel/ice/ice_acl.h b/drivers/net/ethernet/intel/ice/ice_acl.h
index bb836f23d65e..d4e6f0e25a12 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl.h
+++ b/drivers/net/ethernet/intel/ice/ice_acl.h
@@ -101,6 +101,8 @@ struct ice_acl_alloc_tbl {
int ice_acl_create_tbl(struct ice_hw *hw, struct ice_acl_tbl_params *params);
int ice_acl_destroy_tbl(struct ice_hw *hw);
+int ice_acl_create_scen(struct ice_hw *hw, u16 match_width, u16 num_entries,
+ u16 *scen_id);
int ice_aq_alloc_acl_tbl(struct ice_hw *hw, struct ice_acl_alloc_tbl *tbl,
struct ice_sq_cd *cd);
int ice_aq_dealloc_acl_tbl(struct ice_hw *hw, u16 alloc_id,
@@ -113,5 +115,11 @@ int ice_aq_program_actpair(struct ice_hw *hw, u8 act_mem_idx, u16 act_entry_idx,
struct ice_aqc_actpair *buf, struct ice_sq_cd *cd);
int ice_aq_alloc_acl_scen(struct ice_hw *hw, u16 *scen_id,
struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd);
+int ice_aq_dealloc_acl_scen(struct ice_hw *hw, u16 scen_id,
+ struct ice_sq_cd *cd);
+int ice_aq_update_acl_scen(struct ice_hw *hw, u16 scen_id,
+ struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd);
+int ice_aq_query_acl_scen(struct ice_hw *hw, u16 scen_id,
+ struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd);
#endif /* _ICE_ACL_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 87f215f47072..46d2675baa4e 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -2070,6 +2070,33 @@ struct ice_aqc_acl_generic {
u8 act_mem[ICE_AQC_MAX_ACTION_MEMORIES];
};
+/* Allocate ACL scenario (indirect 0x0C14). This command doesn't have separate
+ * response buffer since original command buffer gets updated with
+ * 'scen_id' in case of success
+ */
+struct ice_aqc_acl_alloc_scen {
+ union {
+ struct {
+ u8 reserved[8];
+ } cmd;
+ struct {
+ __le16 scen_id;
+ u8 reserved[6];
+ } resp;
+ } ops;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* De-allocate ACL scenario (direct 0x0C15). This command doesn't need
+ * separate response buffer since nothing to be returned as a response
+ * except status.
+ */
+struct ice_aqc_acl_dealloc_scen {
+ __le16 scen_id;
+ u8 reserved[14];
+};
+
/* Update ACL scenario (direct 0x0C1B)
* Query ACL scenario (direct 0x0C23)
*/
@@ -2850,6 +2877,8 @@ enum ice_adminq_opc {
/* ACL commands */
ice_aqc_opc_alloc_acl_tbl = 0x0C10,
ice_aqc_opc_dealloc_acl_tbl = 0x0C11,
+ ice_aqc_opc_alloc_acl_scen = 0x0C14,
+ ice_aqc_opc_dealloc_acl_scen = 0x0C15,
ice_aqc_opc_update_acl_scen = 0x0C1B,
ice_aqc_opc_program_acl_actpair = 0x0C1C,
ice_aqc_opc_program_acl_entry = 0x0C20,
diff --git a/drivers/net/ethernet/intel/ice/ice_fdir.h b/drivers/net/ethernet/intel/ice/ice_fdir.h
index 26d79b1364e7..4d802b6be0ee 100644
--- a/drivers/net/ethernet/intel/ice/ice_fdir.h
+++ b/drivers/net/ethernet/intel/ice/ice_fdir.h
@@ -198,6 +198,8 @@ struct ice_ntuple_fltr {
u32 fltr_id;
u8 fdid_prio;
u8 comp_report;
+
+ bool acl_fltr;
};
/* Dummy packet filter definition structure */
@@ -227,7 +229,7 @@ bool ice_fdir_is_dup_fltr(struct ice_hw *hw, struct ice_ntuple_fltr *input);
bool ice_fdir_has_frag(enum ice_fltr_ptype flow);
struct ice_ntuple_fltr *
ice_fdir_find_fltr_by_idx(struct ice_hw *hw, u32 fltr_idx);
-void
-ice_fdir_update_cntrs(struct ice_hw *hw, enum ice_fltr_ptype flow, bool add);
+void ice_fdir_update_cntrs(struct ice_hw *hw, enum ice_fltr_ptype flow,
+ bool acl_fltr, bool add);
void ice_fdir_list_add_fltr(struct ice_hw *hw, struct ice_ntuple_fltr *input);
#endif /* _ICE_FDIR_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.h b/drivers/net/ethernet/intel/ice/ice_flow.h
index 7323e26afc0b..a20ef320e1f9 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.h
+++ b/drivers/net/ethernet/intel/ice/ice_flow.h
@@ -482,6 +482,13 @@ struct ice_flow_prof {
DECLARE_BITMAP(vsis, ICE_MAX_VSI);
bool symm; /* Symmetric Hash for RSS */
+
+ union {
+ /* struct sw_recipe */
+ struct ice_acl_scen *scen;
+ /* struct fd */
+ u32 data;
+ } cfg;
};
struct ice_rss_raw_cfg {
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index 161acd1cf095..8ee8eeb7679b 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -1011,6 +1011,8 @@ struct ice_hw {
struct udp_tunnel_nic_info udp_tunnel_nic;
struct ice_acl_tbl *acl_tbl;
+ struct ice_fd_hw_prof **acl_prof;
+ u16 acl_fltr_cnt[ICE_FLTR_PTYPE_MAX];
/* dvm boost update information */
struct ice_dvm_table dvm_upd;
diff --git a/drivers/net/ethernet/intel/ice/ice_acl.c b/drivers/net/ethernet/intel/ice/ice_acl.c
index 3d963c6071dc..81bddac8d0a2 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl.c
+++ b/drivers/net/ethernet/intel/ice/ice_acl.c
@@ -134,3 +134,119 @@ int ice_aq_program_actpair(struct ice_hw *hw, u8 act_mem_idx, u16 act_entry_idx,
return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
}
+
+/**
+ * ice_aq_alloc_acl_scen - allocate ACL scenario
+ * @hw: pointer to the HW struct
+ * @scen_id: memory location to receive allocated scenario ID
+ * @buf: address of indirect data buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Allocate ACL scenario (indirect 0x0C14)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_alloc_acl_scen(struct ice_hw *hw, u16 *scen_id,
+ struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_alloc_scen *cmd;
+ struct libie_aq_desc desc;
+ int err;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_alloc_acl_scen);
+ desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD);
+ cmd = libie_aq_raw(&desc);
+
+ err = ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
+ if (!err)
+ *scen_id = le16_to_cpu(cmd->ops.resp.scen_id);
+
+ return err;
+}
+
+/**
+ * ice_aq_dealloc_acl_scen - deallocate ACL scenario
+ * @hw: pointer to the HW struct
+ * @scen_id: scen_id to be deallocated (input and output field)
+ * @cd: pointer to command details structure or NULL
+ *
+ * Deallocate ACL scenario (direct 0x0C15)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_dealloc_acl_scen(struct ice_hw *hw, u16 scen_id,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_dealloc_scen *cmd;
+ struct libie_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_dealloc_acl_scen);
+ cmd = libie_aq_raw(&desc);
+ cmd->scen_id = cpu_to_le16(scen_id);
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+}
+
+/**
+ * ice_aq_update_query_scen - update or query ACL scenario
+ * @hw: pointer to the HW struct
+ * @opcode: AQ command opcode for either query or update scenario
+ * @scen_id: scen_id to be updated or queried
+ * @buf: address of indirect data buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Calls update or query ACL scenario
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_aq_update_query_scen(struct ice_hw *hw, u16 opcode, u16 scen_id,
+ struct ice_aqc_acl_scen *buf,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_update_query_scen *cmd;
+ struct libie_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, opcode);
+ if (opcode == ice_aqc_opc_update_acl_scen)
+ desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD);
+ cmd = libie_aq_raw(&desc);
+ cmd->scen_id = cpu_to_le16(scen_id);
+
+ return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
+}
+
+/**
+ * ice_aq_update_acl_scen - update ACL scenario
+ * @hw: pointer to the HW struct
+ * @scen_id: scen_id to be updated
+ * @buf: address of indirect data buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Update ACL scenario (indirect 0x0C1B)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_update_acl_scen(struct ice_hw *hw, u16 scen_id,
+ struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd)
+{
+ return ice_aq_update_query_scen(hw, ice_aqc_opc_update_acl_scen,
+ scen_id, buf, cd);
+}
+
+/**
+ * ice_aq_query_acl_scen - query ACL scenario
+ * @hw: pointer to the HW struct
+ * @scen_id: scen_id to be queried
+ * @buf: address of indirect data buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Query ACL scenario (indirect 0x0C23)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_query_acl_scen(struct ice_hw *hw, u16 scen_id,
+ struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd)
+{
+ return ice_aq_update_query_scen(hw, ice_aqc_opc_query_acl_scen,
+ scen_id, buf, cd);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c b/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c
index d821b2c923d5..c6148192dc6e 100644
--- a/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c
+++ b/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c
@@ -6,6 +6,80 @@
/* Determine the TCAM index of entry 'e' within the ACL table */
#define ICE_ACL_TBL_TCAM_IDX(e) ((e) / ICE_AQC_ACL_TCAM_DEPTH)
+/**
+ * ice_acl_init_entry - initialize ACL entry
+ * @scen: pointer to the scenario struct
+ *
+ * Initialize the scenario control structure.
+ */
+static void ice_acl_init_entry(struct ice_acl_scen *scen)
+{
+ /* low priority: start from the highest index, 25% of total entries
+ * normal priority: start from the highest index, 50% of total entries
+ * high priority: start from the lowest index, 25% of total entries
+ */
+ scen->first_idx[ICE_ACL_PRIO_LOW] = scen->num_entry - 1;
+ scen->first_idx[ICE_ACL_PRIO_NORMAL] = scen->num_entry -
+ scen->num_entry / 4 - 1;
+ scen->first_idx[ICE_ACL_PRIO_HIGH] = 0;
+
+ scen->last_idx[ICE_ACL_PRIO_LOW] = scen->num_entry -
+ scen->num_entry / 4;
+ scen->last_idx[ICE_ACL_PRIO_NORMAL] = scen->num_entry / 4;
+ scen->last_idx[ICE_ACL_PRIO_HIGH] = scen->num_entry / 4 - 1;
+}
+
+/**
+ * ice_acl_tbl_calc_end_idx - get end ACL entry index
+ * @start: start index of the TCAM entry of this partition
+ * @num_entries: number of entries in this partition
+ * @width: width of a partition in number of TCAMs
+ *
+ * Calculate the end entry index for a partition with starting entry index
+ * 'start', entries 'num_entries', and width 'width'.
+ *
+ * Returns: end entry index
+ */
+static u16 ice_acl_tbl_calc_end_idx(u16 start, u16 num_entries, u16 width)
+{
+ u16 end_idx, add_entries = 0;
+
+ end_idx = start + (num_entries - 1);
+
+ /* In case that our ACL partition requires cascading TCAMs */
+ if (width > 1) {
+ u16 num_stack_level;
+
+ /* Figure out the TCAM stacked level in this ACL scenario */
+ num_stack_level = (start % ICE_AQC_ACL_TCAM_DEPTH) +
+ num_entries;
+ num_stack_level = DIV_ROUND_UP(num_stack_level,
+ ICE_AQC_ACL_TCAM_DEPTH);
+
+ /* In this case, each entries in our ACL partition span
+ * multiple TCAMs. Thus, we will need to add
+ * ((width - 1) * num_stack_level) TCAM's entries to
+ * end_idx.
+ *
+ * For example : In our case, our scenario is 2x2:
+ * [TCAM 0] [TCAM 1]
+ * [TCAM 2] [TCAM 3]
+ * Assuming that a TCAM will have 512 entries. If "start"
+ * is 500, "num_entries" is 3 and "width" = 2, then end_idx
+ * should be 1024 (belongs to TCAM 2).
+ * Before going to this if statement, end_idx will have the
+ * value of 512. If "width" is 1, then the final value of
+ * end_idx is 512. However, in our case, width is 2, then we
+ * will need add (2 - 1) * 1 * 512. As result, end_idx will
+ * have the value of 1024.
+ */
+ add_entries = (width - 1) * num_stack_level *
+ ICE_AQC_ACL_TCAM_DEPTH;
+ }
+
+ return end_idx + add_entries;
+}
+
/**
* ice_acl_init_tbl - initialize ACL table
* @hw: pointer to the hardware structure
@@ -274,6 +348,452 @@ ice_acl_create_tbl(struct ice_hw *hw, struct ice_acl_tbl_params *params)
return 0;
}
+/**
+ * ice_acl_alloc_partition - Allocate a partition from the ACL table
+ * @hw: pointer to the hardware structure
+ * @req: info of partition being allocated
+ *
+ * Returns: 0 on success, negative on error
+ */
+static int ice_acl_alloc_partition(struct ice_hw *hw, struct ice_acl_scen *req)
+{
+ u16 start = 0, cnt = 0, off = 0;
+ u16 width, r_entries, row;
+ bool done = false;
+ int dir;
+
+ /* Determine the number of TCAMs each entry overlaps */
+ width = DIV_ROUND_UP(req->width, ICE_AQC_ACL_KEY_WIDTH_BYTES);
+
+ /* Check if we have enough TCAMs to accommodate the width */
+ if (width > hw->acl_tbl->last_tcam - hw->acl_tbl->first_tcam + 1)
+ return -ENOSPC;
+
+ /* Number of entries must be multiple of ICE_ACL_ENTRY_ALLOC_UNIT's */
+ r_entries = ALIGN(req->num_entry, ICE_ACL_ENTRY_ALLOC_UNIT);
+
+ /* To look for an available partition that can accommodate the request,
+ * the process first logically arranges available TCAMs in rows such
+ * that each row produces entries with the requested width. It then
+ * scans the TCAMs' available bitmap, one bit at a time, and
+ * accumulates contiguous available 64-entry chunks until there are
+ * enough of them or when all TCAM configurations have been checked.
+ *
+ * For width of 1 TCAM, the scanning process starts from the top most
+ * TCAM, and goes downward. Available bitmaps are examined from LSB
+ * to MSB.
+ *
+ * For width of multiple TCAMs, the process starts from the bottom-most
+ * row of TCAMs, and goes upward. Available bitmaps are examined from
+ * the MSB to the LSB.
+ *
+ * To make sure that adjacent TCAMs can be logically arranged in the
+ * same row, the scanning process may have multiple passes. In each
+ * pass, the first TCAM of the bottom-most row is displaced by one
+ * additional TCAM. The width of the row and the number of the TCAMs
+ * available determine the number of passes. When the displacement is
+ * more than the size of width, the TCAM row configurations will
+ * repeat. The process will terminate when the configurations repeat.
+ *
+ * Available partitions can span more than one row of TCAMs.
+ */
+ if (width == 1) {
+ row = hw->acl_tbl->first_tcam;
+ dir = 1;
+ } else {
+ /* Start with the bottom-most row, and scan for available
+ * entries upward
+ */
+ row = hw->acl_tbl->last_tcam + 1 - width;
+ dir = -1;
+ }
+
+ do {
+ /* Scan all 64-entry chunks, one chunk at a time, in the
+ * current TCAM row
+ */
+ for (u16 i = 0;
+ i < ICE_AQC_MAX_TCAM_ALLOC_UNITS && cnt < r_entries;
+ i++) {
+ bool avail = true;
+ u16 p;
+
+ /* Compute the cumulative available mask across the
+ * TCAM row to determine if the current 64-entry chunk
+ * is available.
+ */
+ p = dir > 0 ? i : ICE_AQC_MAX_TCAM_ALLOC_UNITS - i - 1;
+ for (u16 w = row; w < row + width && avail; w++) {
+ u16 b;
+
+ b = (w * ICE_AQC_MAX_TCAM_ALLOC_UNITS) + p;
+ avail &= test_bit(b, hw->acl_tbl->avail);
+ }
+
+ if (!avail) {
+ cnt = 0;
+ } else {
+ /* Compute the starting index of the newly
+ * found partition. When 'dir' is negative, the
+ * scan processes is going upward. If so, the
+ * starting index needs to be updated for every
+ * available 64-entry chunk found.
+ */
+ if (!cnt || dir < 0)
+ start = (row * ICE_AQC_ACL_TCAM_DEPTH) +
+ (p * ICE_ACL_ENTRY_ALLOC_UNIT);
+ cnt += ICE_ACL_ENTRY_ALLOC_UNIT;
+ }
+ }
+
+ if (cnt >= r_entries) {
+ req->start = start;
+ req->num_entry = r_entries;
+ req->end = ice_acl_tbl_calc_end_idx(start, r_entries,
+ width);
+ break;
+ }
+
+ row = dir > 0 ? row + width : row - width;
+ if (row > hw->acl_tbl->last_tcam ||
+ row < hw->acl_tbl->first_tcam) {
+ /* All rows have been checked. Increment 'off' that
+ * will help yield a different TCAM configuration in
+ * which adjacent TCAMs can be alternatively in the
+ * same row.
+ */
+ off++;
+
+ /* However, if the new 'off' value yields previously
+ * checked configurations, then exit.
+ */
+ if (off >= width)
+ done = true;
+ else
+ row = dir > 0 ? off :
+ hw->acl_tbl->last_tcam + 1 - off -
+ width;
+ }
+ } while (!done);
+
+ return cnt >= r_entries ? 0 : -ENOSPC;
+}
+
+/**
+ * ice_acl_fill_tcam_select - fill key byte selection for scenario's TCAM
+ * @scen_buf: Pointer to the scenario buffer that needs to be populated
+ * @scen: Pointer to the available space for the scenario
+ * @tcam_idx: Index of the TCAM used for this scenario
+ * @tcam_idx_in_cascade: Local index of the TCAM in the cascade scenario
+ *
+ * For all TCAM that participate in this scenario, fill out the tcam_select
+ * value.
+ */
+static void ice_acl_fill_tcam_select(struct ice_aqc_acl_scen *scen_buf,
+ struct ice_acl_scen *scen, u16 tcam_idx,
+ u16 tcam_idx_in_cascade)
+{
+ u16 cascade_cnt, idx;
+
+ idx = tcam_idx_in_cascade * ICE_AQC_ACL_KEY_WIDTH_BYTES;
+ cascade_cnt = DIV_ROUND_UP(scen->width, ICE_AQC_ACL_KEY_WIDTH_BYTES);
+
+ /* For each scenario, we reserved last three bytes of scenario width for
+ * profile ID, range checker, and packet direction. Thus, the last three
+ * bytes of the last cascaded TCAMs will have value of 1st, 31st and
+ * 32nd byte location of BYTE selection base.
+ *
+ * For other bytes in the TCAMs:
+ * For non-cascade mode (1 TCAM wide) scenario, TCAM[x]'s Select {0-1}
+ * select indices 0-1 of the Byte Selection Base
+ * For cascade mode, the leftmost TCAM of the first cascade row selects
+ * indices 0-4 of the Byte Selection Base; the second TCAM in the
+ * cascade row selects indices starting with 5-n
+ */
+ for (int j = 0; j < ICE_AQC_ACL_KEY_WIDTH_BYTES; j++) {
+ /* PKT DIR uses the 1st location of Byte Selection Base: + 1 */
+ u8 val = ICE_AQC_ACL_BYTE_SEL_BASE + 1 + idx;
+
+ if (tcam_idx_in_cascade == cascade_cnt - 1) {
+ if (j == ICE_ACL_SCEN_RNG_CHK_IDX_IN_TCAM)
+ val = ICE_AQC_ACL_BYTE_SEL_BASE_RNG_CHK;
+ else if (j == ICE_ACL_SCEN_PID_IDX_IN_TCAM)
+ val = ICE_AQC_ACL_BYTE_SEL_BASE_PID;
+ else if (j == ICE_ACL_SCEN_PKT_DIR_IDX_IN_TCAM)
+ val = ICE_AQC_ACL_BYTE_SEL_BASE_PKT_DIR;
+ }
+
+ /* In case that scenario's width is greater than the width of
+ * the Byte selection base, we will not assign a value to the
+ * tcam_select[j]. As a result, the tcam_select[j] will have
+ * default value which is zero.
+ */
+ if (val > ICE_AQC_ACL_BYTE_SEL_BASE_RNG_CHK)
+ continue;
+
+ scen_buf->tcam_cfg[tcam_idx].tcam_select[j] = val;
+
+ idx++;
+ }
+}
+
+/**
+ * ice_acl_set_scen_chnk_msk - set entries chunk masks
+ * @scen_buf: Pointer to the scenario buffer that needs to be populated
+ * @scen: pointer to the available space for the scenario
+ *
+ * Set the chunk mask for the entries that will be used by this scenario
+ */
+static void ice_acl_set_scen_chnk_msk(struct ice_aqc_acl_scen *scen_buf,
+ struct ice_acl_scen *scen)
+{
+ u16 tcam_idx, num_cscd, units;
+ u8 chnk_offst;
+
+ /* Determine the starting TCAM index and offset of the start entry */
+ tcam_idx = ICE_ACL_TBL_TCAM_IDX(scen->start);
+ chnk_offst = (u8)((scen->start % ICE_AQC_ACL_TCAM_DEPTH) /
+ ICE_ACL_ENTRY_ALLOC_UNIT);
+
+ /* Entries are allocated and tracked in multiple of 64's */
+ units = scen->num_entry / ICE_ACL_ENTRY_ALLOC_UNIT;
+
+ /* Determine number of cascaded TCAMs */
+ num_cscd = scen->width / ICE_AQC_ACL_KEY_WIDTH_BYTES;
+
+ for (u16 cnt = 0; cnt < units; cnt++) {
+ /* Set the corresponding bitmap of individual 64-entry
+ * chunk spans across a cascade of 1 or more TCAMs
+ * For each TCAM, there will be (ICE_AQC_ACL_TCAM_DEPTH
+ * / ICE_ACL_ENTRY_ALLOC_UNIT) or 8 chunks.
+ */
+ for (u16 i = tcam_idx; i < tcam_idx + num_cscd; i++)
+ scen_buf->tcam_cfg[i].chnk_msk |= BIT(chnk_offst);
+
+ chnk_offst = (chnk_offst + 1) % ICE_AQC_MAX_TCAM_ALLOC_UNITS;
+ if (!chnk_offst)
+ tcam_idx += num_cscd;
+ }
+}
+
+/**
+ * ice_acl_assign_act_mem_for_scen - associate action memories to new TCAM
+ * @tbl: pointer to ACL table structure
+ * @scen: pointer to the scenario struct
+ * @scen_buf: pointer to the available space for the scenario
+ * @current_tcam_idx: theoretical index of the TCAM that we associated those
+ * action memory banks with, at the table creation time
+ * @target_tcam_idx: index of the TCAM that we want to associate those action
+ * memory banks with
+ */
+static void ice_acl_assign_act_mem_for_scen(struct ice_acl_tbl *tbl,
+ struct ice_acl_scen *scen,
+ struct ice_aqc_acl_scen *scen_buf,
+ u8 current_tcam_idx,
+ u8 target_tcam_idx)
+{
+ for (int i = 0; i < ICE_AQC_MAX_ACTION_MEMORIES; i++) {
+ struct ice_acl_act_mem *p_mem = &tbl->act_mems[i];
+
+ if (p_mem->act_mem == ICE_ACL_ACT_MEM_ACT_MEM_INVAL ||
+ p_mem->member_of_tcam != current_tcam_idx)
+ continue;
+
+ scen_buf->act_mem_cfg[i] = target_tcam_idx;
+ scen_buf->act_mem_cfg[i] |= ICE_AQC_ACL_SCE_ACT_MEM_EN;
+ set_bit(i, scen->act_mem_bitmap);
+ }
+}
+
+/**
+ * ice_acl_commit_partition - Indicate if the specified partition is active
+ * @hw: pointer to the hardware structure
+ * @scen: pointer to the scenario struct
+ * @commit: true if the partition is being commit
+ */
+static void ice_acl_commit_partition(struct ice_hw *hw,
+ struct ice_acl_scen *scen, bool commit)
+{
+ u16 tcam_idx, off, num_cscd, units;
+
+ /* Determine the starting TCAM index and offset of the start entry */
+ tcam_idx = ICE_ACL_TBL_TCAM_IDX(scen->start);
+ off = (scen->start % ICE_AQC_ACL_TCAM_DEPTH) /
+ ICE_ACL_ENTRY_ALLOC_UNIT;
+
+ /* Entries are allocated and tracked in multiple of 64's */
+ units = scen->num_entry / ICE_ACL_ENTRY_ALLOC_UNIT;
+
+ /* Determine number of cascaded TCAM */
+ num_cscd = scen->width / ICE_AQC_ACL_KEY_WIDTH_BYTES;
+
+ for (u16 cnt = 0; cnt < units; cnt++) {
+ /* Set/clear the corresponding bitmap of individual 64-entry
+ * chunk spans across a row of 1 or more TCAMs
+ */
+ for (u16 w = 0; w < num_cscd; w++) {
+ u16 b;
+
+ b = ((tcam_idx + w) * ICE_AQC_MAX_TCAM_ALLOC_UNITS) +
+ off;
+ if (commit)
+ set_bit(b, hw->acl_tbl->avail);
+ else
+ clear_bit(b, hw->acl_tbl->avail);
+ }
+
+ off = (off + 1) % ICE_AQC_MAX_TCAM_ALLOC_UNITS;
+ if (!off)
+ tcam_idx += num_cscd;
+ }
+}
+
+/**
+ * ice_acl_create_scen - create ACL scenario
+ * @hw: pointer to the hardware structure
+ * @match_width: number of bytes to be matched in this scenario
+ * @num_entries: number of entries to be allocated for the scenario
+ * @scen_id: holds returned scenario ID if successful
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_acl_create_scen(struct ice_hw *hw, u16 match_width, u16 num_entries,
+ u16 *scen_id)
+{
+ u8 cascade_cnt, first_tcam, last_tcam, i, k;
+ struct ice_aqc_acl_scen scen_buf = {};
+ struct ice_acl_scen *scen;
+ int err;
+
+ scen = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*scen), GFP_KERNEL);
+ if (!scen)
+ return -ENOMEM;
+
+ scen->start = hw->acl_tbl->first_entry;
+ scen->width = ICE_AQC_ACL_KEY_WIDTH_BYTES *
+ DIV_ROUND_UP(match_width, ICE_AQC_ACL_KEY_WIDTH_BYTES);
+ scen->num_entry = num_entries;
+
+ err = ice_acl_alloc_partition(hw, scen);
+ if (err)
+ goto out;
+
+ /* Determine the number of cascade TCAMs, given the scenario's width */
+ cascade_cnt = DIV_ROUND_UP(scen->width, ICE_AQC_ACL_KEY_WIDTH_BYTES);
+ first_tcam = ICE_ACL_TBL_TCAM_IDX(scen->start);
+ last_tcam = ICE_ACL_TBL_TCAM_IDX(scen->end);
+
+ /* For each scenario, we reserved last three bytes of scenario width for
+ * packet direction flag, profile ID and range checker. Thus, we want to
+ * return back to the caller the eff_width, pkt_dir_idx, rng_chk_idx and
+ * pid_idx.
+ */
+ scen->eff_width = cascade_cnt * ICE_AQC_ACL_KEY_WIDTH_BYTES -
+ ICE_ACL_SCEN_MIN_WIDTH;
+ scen->rng_chk_idx = (cascade_cnt - 1) * ICE_AQC_ACL_KEY_WIDTH_BYTES +
+ ICE_ACL_SCEN_RNG_CHK_IDX_IN_TCAM;
+ scen->pid_idx = (cascade_cnt - 1) * ICE_AQC_ACL_KEY_WIDTH_BYTES +
+ ICE_ACL_SCEN_PID_IDX_IN_TCAM;
+ scen->pkt_dir_idx = (cascade_cnt - 1) * ICE_AQC_ACL_KEY_WIDTH_BYTES +
+ ICE_ACL_SCEN_PKT_DIR_IDX_IN_TCAM;
+
+ /* set the chunk mask for the tcams */
+ ice_acl_set_scen_chnk_msk(&scen_buf, scen);
+
+ /* set the TCAM select and start_cmp and start_set bits */
+ k = first_tcam;
+ /* set the START_SET bit at the beginning of the stack */
+ scen_buf.tcam_cfg[k].start_cmp_set |= ICE_AQC_ACL_ALLOC_SCE_START_SET;
+ while (k <= last_tcam) {
+ u8 last_tcam_idx_cascade = cascade_cnt + k - 1;
+
+ /* set start_cmp for the first cascaded TCAM */
+ scen_buf.tcam_cfg[k].start_cmp_set |=
+ ICE_AQC_ACL_ALLOC_SCE_START_CMP;
+
+ /* cascade TCAMs up to the width of the scenario */
+ for (i = k; i < cascade_cnt + k; i++) {
+ ice_acl_fill_tcam_select(&scen_buf, scen, i, i - k);
+ ice_acl_assign_act_mem_for_scen(hw->acl_tbl, scen,
+ &scen_buf, i,
+ last_tcam_idx_cascade);
+ }
+
+ k = i;
+ }
+
+ /* We need to set the start_cmp bit for the unused TCAMs. */
+ i = 0;
+ while (i < first_tcam)
+ scen_buf.tcam_cfg[i++].start_cmp_set =
+ ICE_AQC_ACL_ALLOC_SCE_START_CMP;
+
+ i = last_tcam + 1;
+ while (i < ICE_AQC_ACL_SLICES)
+ scen_buf.tcam_cfg[i++].start_cmp_set =
+ ICE_AQC_ACL_ALLOC_SCE_START_CMP;
+
+ err = ice_aq_alloc_acl_scen(hw, scen_id, &scen_buf, NULL);
+ if (err) {
+ ice_debug(hw, ICE_DBG_ACL, "AQ allocation of ACL scenario failed. status: %d\n",
+ err);
+ goto out;
+ }
+
+ scen->id = *scen_id;
+ ice_acl_commit_partition(hw, scen, false);
+ ice_acl_init_entry(scen);
+ list_add(&scen->list_entry, &hw->acl_tbl->scens);
+
+out:
+ if (err)
+ devm_kfree(ice_hw_to_dev(hw), scen);
+
+ return err;
+}
+
+/**
+ * ice_acl_destroy_scen - destroy an ACL scenario
+ * @hw: pointer to the HW struct
+ * @scen_id: ID of the remove scenario
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_acl_destroy_scen(struct ice_hw *hw, u16 scen_id)
+{
+ struct ice_acl_scen *scen, *tmp_scen;
+ struct ice_flow_prof *p, *tmp;
+ int err;
+
+ /* Remove profiles that use "scen_id" scenario */
+ list_for_each_entry_safe(p, tmp, &hw->fl_profs[ICE_BLK_ACL], l_entry)
+ if (p->cfg.scen && p->cfg.scen->id == scen_id) {
+ err = ice_flow_rem_prof(hw, ICE_BLK_ACL, p->id);
+ if (err) {
+ ice_debug(hw, ICE_DBG_ACL, "ice_flow_rem_prof failed. status: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ err = ice_aq_dealloc_acl_scen(hw, scen_id, NULL);
+ if (err) {
+ ice_debug(hw, ICE_DBG_ACL, "AQ de-allocation of scenario failed. status: %d\n",
+ err);
+ return err;
+ }
+
+ /* Remove scenario from hw->acl_tbl->scens */
+ list_for_each_entry_safe(scen, tmp_scen, &hw->acl_tbl->scens,
+ list_entry)
+ if (scen->id == scen_id) {
+ list_del(&scen->list_entry);
+ devm_kfree(ice_hw_to_dev(hw), scen);
+ }
+
+ return 0;
+}
+
/**
* ice_acl_destroy_tbl - Destroy a previously created LEM table for ACL
* @hw: pointer to the HW struct
@@ -282,12 +802,50 @@ ice_acl_create_tbl(struct ice_hw *hw, struct ice_acl_tbl_params *params)
*/
int ice_acl_destroy_tbl(struct ice_hw *hw)
{
+ struct ice_acl_scen *pos_scen, *tmp_scen;
struct ice_aqc_acl_generic resp_buf;
+ struct ice_aqc_acl_scen buf;
int err;
if (!hw->acl_tbl)
return -ENOENT;
+ /* Mark all the created scenario's TCAM to stop the packet lookup and
+ * delete them afterward
+ */
+ list_for_each_entry_safe(pos_scen, tmp_scen, &hw->acl_tbl->scens,
+ list_entry) {
+ err = ice_aq_query_acl_scen(hw, pos_scen->id, &buf, NULL);
+ if (err) {
+ ice_debug(hw, ICE_DBG_ACL, "ice_aq_query_acl_scen() failed. status: %d\n",
+ err);
+ return err;
+ }
+
+ for (int i = 0; i < ICE_AQC_ACL_SLICES; i++) {
+ buf.tcam_cfg[i].chnk_msk = 0;
+ buf.tcam_cfg[i].start_cmp_set =
+ ICE_AQC_ACL_ALLOC_SCE_START_CMP;
+ }
+
+ for (int i = 0; i < ICE_AQC_MAX_ACTION_MEMORIES; i++)
+ buf.act_mem_cfg[i] = 0;
+
+ err = ice_aq_update_acl_scen(hw, pos_scen->id, &buf, NULL);
+ if (err) {
+ ice_debug(hw, ICE_DBG_ACL, "ice_aq_update_acl_scen() failed. status: %d\n",
+ err);
+ return err;
+ }
+
+ err = ice_acl_destroy_scen(hw, pos_scen->id);
+ if (err) {
+ ice_debug(hw, ICE_DBG_ACL, "deletion of scenario failed. status: %d\n",
+ err);
+ return err;
+ }
+ }
+
err = ice_aq_dealloc_acl_tbl(hw, hw->acl_tbl->id, &resp_buf, NULL);
if (err) {
ice_debug(hw, ICE_DBG_ACL, "AQ de-allocation of ACL failed. status: %d\n",
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
index 1495d96b5c98..6683a16c888a 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -3151,8 +3151,8 @@ ice_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
switch (cmd->cmd) {
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = hw->fdir_active_fltr;
- /* report total rule count */
- cmd->data = ice_get_fdir_cnt_all(hw);
+ /* report max rule count */
+ cmd->data = ice_ntuple_get_max_fltr_cnt(hw);
ret = 0;
break;
case ETHTOOL_GRXCLSRULE:
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
index a6136e640418..053d6b7a66bd 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
@@ -228,6 +228,24 @@ int ice_get_ethtool_fdir_entry(struct ice_hw *hw, struct ethtool_rxnfc *cmd)
return ret;
}
+/**
+ * ice_ntuple_get_max_fltr_cnt - get max number of allowed filters
+ * @hw: hardware structure containing filter information
+ *
+ * Return: maximum number of allowed filters
+ */
+u32 ice_ntuple_get_max_fltr_cnt(struct ice_hw *hw)
+{
+ int acl_cnt;
+
+ if (hw->dev_caps.num_funcs < 8)
+ acl_cnt = ICE_AQC_ACL_TCAM_DEPTH / ICE_ACL_ENTIRE_SLICE;
+ else
+ acl_cnt = ICE_AQC_ACL_TCAM_DEPTH / ICE_ACL_HALF_SLICE;
+
+ return ice_get_fdir_cnt_all(hw) + acl_cnt;
+}
+
/**
* ice_get_fdir_fltr_ids - fill buffer with filter IDs of active filters
* @hw: hardware structure containing the filter list
@@ -244,8 +262,8 @@ ice_get_fdir_fltr_ids(struct ice_hw *hw, struct ethtool_rxnfc *cmd,
unsigned int cnt = 0;
int val = 0;
- /* report total rule count */
- cmd->data = ice_get_fdir_cnt_all(hw);
+ /* report max rule count */
+ cmd->data = ice_ntuple_get_max_fltr_cnt(hw);
mutex_lock(&hw->fdir_fltr_lock);
@@ -346,6 +364,9 @@ void ice_fdir_rem_adq_chnl(struct ice_hw *hw, u16 vsi_idx)
static struct ice_fd_hw_prof *
ice_fdir_get_hw_prof(struct ice_hw *hw, enum ice_block blk, int flow)
{
+ if (blk == ICE_BLK_ACL && hw->acl_prof)
+ return hw->acl_prof[flow];
+
if (blk == ICE_BLK_FD && hw->fdir_prof)
return hw->fdir_prof[flow];
@@ -1635,8 +1656,10 @@ void ice_fdir_del_all_fltrs(struct ice_vsi *vsi)
struct ice_hw *hw = &pf->hw;
list_for_each_entry_safe(f_rule, tmp, &hw->fdir_list_head, fltr_node) {
- ice_fdir_write_all_fltr(pf, f_rule, false);
- ice_fdir_update_cntrs(hw, f_rule->flow_type, false);
+ if (!f_rule->acl_fltr)
+ ice_fdir_write_all_fltr(pf, f_rule, false);
+ ice_fdir_update_cntrs(hw, f_rule->flow_type, f_rule->acl_fltr,
+ false);
list_del(&f_rule->fltr_node);
devm_kfree(ice_pf_to_dev(pf), f_rule);
}
@@ -1671,6 +1694,12 @@ void ice_vsi_manage_fdir(struct ice_vsi *vsi, bool ena)
if (hw->fdir_prof[flow])
ice_fdir_rem_flow(hw, ICE_BLK_FD, flow);
+ if (hw->acl_prof)
+ for (flow = ICE_FLTR_PTYPE_NONF_NONE; flow < ICE_FLTR_PTYPE_MAX;
+ flow++)
+ if (hw->acl_prof[flow])
+ ice_fdir_rem_flow(hw, ICE_BLK_ACL, flow);
+
release_lock:
mutex_unlock(&hw->fdir_fltr_lock);
}
@@ -1730,7 +1759,7 @@ ice_ntuple_update_list_entry(struct ice_pf *pf, struct ice_ntuple_fltr *input,
err = ice_fdir_write_all_fltr(pf, old_fltr, false);
if (err)
return err;
- ice_fdir_update_cntrs(hw, old_fltr->flow_type, false);
+ ice_fdir_update_cntrs(hw, old_fltr->flow_type, false, false);
/* update sb-filters count, specific to ring->channel */
ice_update_per_q_fltr(vsi, old_fltr->orig_q_index, false);
if (!input && !hw->fdir_fltr_cnt[old_fltr->flow_type])
@@ -1746,7 +1775,7 @@ ice_ntuple_update_list_entry(struct ice_pf *pf, struct ice_ntuple_fltr *input,
ice_fdir_list_add_fltr(hw, input);
/* update sb-filters count, specific to ring->channel */
ice_update_per_q_fltr(vsi, input->orig_q_index, true);
- ice_fdir_update_cntrs(hw, input->flow_type, true);
+ ice_fdir_update_cntrs(hw, input->flow_type, input->acl_fltr, true);
return 0;
}
@@ -2017,7 +2046,7 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
if (ret)
return ret;
- max_location = ice_get_fdir_cnt_all(hw);
+ max_location = ice_ntuple_get_max_fltr_cnt(hw);
if (fsp->location >= max_location) {
dev_err(dev, "Failed to add filter. The number of ntuple filters or provided location exceed max %d.\n",
max_location);
@@ -2068,7 +2097,7 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
goto release_lock;
remove_sw_rule:
- ice_fdir_update_cntrs(hw, input->flow_type, false);
+ ice_fdir_update_cntrs(hw, input->flow_type, false, false);
/* update sb-filters count, specific to ring->channel */
ice_update_per_q_fltr(vsi, input->orig_q_index, false);
list_del(&input->fltr_node);
diff --git a/drivers/net/ethernet/intel/ice/ice_fdir.c b/drivers/net/ethernet/intel/ice/ice_fdir.c
index 5b25f6414b58..e55d6a91f03a 100644
--- a/drivers/net/ethernet/intel/ice/ice_fdir.c
+++ b/drivers/net/ethernet/intel/ice/ice_fdir.c
@@ -1179,18 +1179,24 @@ void ice_fdir_list_add_fltr(struct ice_hw *hw, struct ice_ntuple_fltr *fltr)
* ice_fdir_update_cntrs - increment / decrement filter counter
* @hw: pointer to hardware structure
* @flow: filter flow type
+ * @acl_fltr: true indicates an ACL filter
* @add: true implies filters added
*/
-void
-ice_fdir_update_cntrs(struct ice_hw *hw, enum ice_fltr_ptype flow, bool add)
+void ice_fdir_update_cntrs(struct ice_hw *hw, enum ice_fltr_ptype flow,
+ bool acl_fltr, bool add)
{
int incr;
incr = add ? 1 : -1;
hw->fdir_active_fltr += incr;
- if (flow == ICE_FLTR_PTYPE_NONF_NONE || flow >= ICE_FLTR_PTYPE_MAX)
+ if (flow == ICE_FLTR_PTYPE_NONF_NONE || flow >= ICE_FLTR_PTYPE_MAX) {
ice_debug(hw, ICE_DBG_SW, "Unknown filter type %d\n", flow);
+ return;
+ }
+
+ if (acl_fltr)
+ hw->acl_fltr_cnt[flow] += incr;
else
hw->fdir_fltr_cnt[flow] += incr;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 1cdb49cd42c3..23d6b8311ff9 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4336,6 +4336,8 @@ static int ice_init_acl(struct ice_pf *pf)
struct ice_acl_tbl_params params = {};
struct ice_hw *hw = &pf->hw;
int divider;
+ u16 scen_id;
+ int err;
/* Creates a single ACL table that consist of src_ip(4 byte),
* dest_ip(4 byte), src_port(2 byte) and dst_port(2 byte) for a total
@@ -4354,7 +4356,20 @@ static int ice_init_acl(struct ice_pf *pf)
params.entry_act_pairs = 1;
params.concurr = false;
- return ice_acl_create_tbl(hw, ¶ms);
+ err = ice_acl_create_tbl(hw, ¶ms);
+ if (err)
+ return err;
+
+ err = ice_acl_create_scen(hw, params.width, params.depth, &scen_id);
+ if (err)
+ goto destroy_table;
+
+ return 0;
+
+destroy_table:
+ ice_acl_destroy_tbl(hw);
+
+ return err;
}
/**
--
2.49.0
^ permalink raw reply related
* [PATCH iwl-next v2 02/10] ice: initialize ACL table
From: Marcin Szycik @ 2026-04-09 11:59 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Chinh Cao, Tony Nguyen, Aleksandr Loktionov
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
From: Real Valiquette <real.valiquette@intel.com>
E8xx hardware provides a Ternary Classifier block for implementing
functions such as ACL (Access Control List). In this series it's simply
referred to as "ACL".
ACL filtering can be utilized to expand support of ntuple rules by allowing
mask values to be specified for redirect to queue or drop.
Implement support for specifying the 'm' value of ethtool ntuple command
for currently supported fields (src-ip, dst-ip, src-port, and dst-port).
For example:
ethtool -N eth0 flow-type tcp4 dst-port 8880 m 0x00ff action 10
or
ethtool -N eth0 flow-type tcp4 src-ip 192.168.0.55 m 0.0.0.255 action -1
At this time the following flow-types support mask values: tcp4, udp4,
sctp4, and ip4.
Begin implementation of ACL filters by setting up structures, AdminQ
commands, and allocation of the ACL table in the hardware.
Co-developed-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Chinh Cao <chinh.t.cao@intel.com>
Signed-off-by: Real Valiquette <real.valiquette@intel.com>
Co-developed-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Co-developed-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
v2:
* Return -ERANGE in one branch in ice_aq_alloc_acl_tbl() to differenciate error
codes
* Use GENMASK() for ICE_AQ_VSI_ACL_DEF_RX_*_M
* Use plain alloc/kfree for hw->acl_tbl
* Call ice_deinit_acl() unconditionally because ICE_FLAG_FD_ENA can be
disabled during operation
* ice_acl_init_tbl(): remove first/last variables
* Merge ice_aq_acl_entry() into ice_aq_program_acl_entry() and
ice_aq_actpair_p_q() into ice_aq_program_actpair() - wrappers with one user
make no sense
* Rename ICE_AQC_ALLOC_ID_LESS_THAN_4K to more sensible ICE_AQC_ALLOC_ID_4K
* Reorder members of struct ice_acl_tbl to minimize holes
* Remove ICE_AQ_VSI_ACL_DEF_RX_*_S - will be unused after switching to
FIELD_PREP() in "ice: program ACL entry"
* Replace memset() with = {} in ice_init_acl()
---
drivers/net/ethernet/intel/ice/Makefile | 2 +
drivers/net/ethernet/intel/ice/ice.h | 3 +
drivers/net/ethernet/intel/ice/ice_acl.h | 117 +++++++
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 208 +++++++++++-
drivers/net/ethernet/intel/ice/ice_type.h | 3 +
drivers/net/ethernet/intel/ice/ice_acl.c | 136 ++++++++
drivers/net/ethernet/intel/ice/ice_acl_ctrl.c | 302 ++++++++++++++++++
drivers/net/ethernet/intel/ice/ice_main.c | 49 +++
8 files changed, 818 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/ethernet/intel/ice/ice_acl.h
create mode 100644 drivers/net/ethernet/intel/ice/ice_acl.c
create mode 100644 drivers/net/ethernet/intel/ice/ice_acl_ctrl.c
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index c310c209bc7d..6afe7be056ba 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -25,6 +25,8 @@ ice-y := ice_main.o \
ice_vsi_vlan_lib.o \
ice_fdir.o \
ice_ethtool_ntuple.o \
+ ice_acl.o \
+ ice_acl_ctrl.o \
ice_vlan_mode.o \
ice_flex_pipe.o \
ice_flow.o \
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index ea1bddfa739d..3a51a033296c 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -157,6 +157,9 @@
#define ICE_SWITCH_FLTR_PRIO_VSI 5
#define ICE_SWITCH_FLTR_PRIO_QGRP ICE_SWITCH_FLTR_PRIO_VSI
+#define ICE_ACL_ENTIRE_SLICE 1
+#define ICE_ACL_HALF_SLICE 2
+
/* Macro for each VSI in a PF */
#define ice_for_each_vsi(pf, i) \
for ((i) = 0; (i) < (pf)->num_alloc_vsi; (i)++)
diff --git a/drivers/net/ethernet/intel/ice/ice_acl.h b/drivers/net/ethernet/intel/ice/ice_acl.h
new file mode 100644
index 000000000000..bb836f23d65e
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_acl.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2018-2026, Intel Corporation. */
+
+#ifndef _ICE_ACL_H_
+#define _ICE_ACL_H_
+
+#include "ice_common.h"
+
+#define ICE_ACL_TBL_PARAMS_DEP_TBLS_MAX 15
+struct ice_acl_tbl_params {
+ u16 width; /* Select/match bytes */
+ u16 depth; /* Number of entries */
+ u16 dep_tbls[ICE_ACL_TBL_PARAMS_DEP_TBLS_MAX];
+ u8 entry_act_pairs; /* Action pairs per entry */
+ u8 concurr; /* Concurrent table lookup enable */
+};
+
+#define ICE_ACL_ACT_MEM_ACT_MEM_INVAL 0xff
+struct ice_acl_act_mem {
+ u8 act_mem;
+ u8 member_of_tcam;
+};
+
+struct ice_acl_tbl {
+ /* TCAM configuration */
+ u8 first_tcam;
+ u8 last_tcam;
+ u16 first_entry; /* Index of the first entry in the first TCAM */
+ u16 last_entry; /* Index of the last entry in the last TCAM */
+ u16 id;
+
+ /* List of active scenarios */
+ struct list_head scens;
+
+ struct ice_acl_tbl_params info;
+ struct ice_acl_act_mem act_mems[ICE_AQC_MAX_ACTION_MEMORIES];
+
+ /* Keep track of available 64-entry chunks in TCAMs */
+ DECLARE_BITMAP(avail, ICE_AQC_ACL_ALLOC_UNITS);
+};
+
+enum ice_acl_entry_prio {
+ ICE_ACL_PRIO_LOW = 0,
+ ICE_ACL_PRIO_NORMAL,
+ ICE_ACL_PRIO_HIGH,
+ ICE_ACL_MAX_PRIO
+};
+
+#define ICE_ACL_SCEN_MIN_WIDTH 0x3
+#define ICE_ACL_SCEN_PKT_DIR_IDX_IN_TCAM 0x2
+#define ICE_ACL_SCEN_PID_IDX_IN_TCAM 0x3
+#define ICE_ACL_SCEN_RNG_CHK_IDX_IN_TCAM 0x4
+/* Scenario structure
+ * A scenario is a logical partition within an ACL table. It can span more
+ * than one TCAM in cascade mode to support select/mask key widths larger
+ * than the width of a TCAM. It can also span more than one TCAM in stacked
+ * mode to support larger number of entries than what a TCAM can hold. It is
+ * used to select values from selection bases (field vectors holding extract
+ * protocol header fields) to form lookup keys, and to associate action memory
+ * banks to the TCAMs used.
+ */
+struct ice_acl_scen {
+ struct list_head list_entry;
+ /* If nth bit of act_mem_bitmap is set, then nth action memory will
+ * participate in this scenario
+ */
+ DECLARE_BITMAP(act_mem_bitmap, ICE_AQC_MAX_ACTION_MEMORIES);
+ u16 first_idx[ICE_ACL_MAX_PRIO];
+ u16 last_idx[ICE_ACL_MAX_PRIO];
+
+ u16 id;
+ u16 start; /* Number of entry from the start of the parent table */
+ u16 width; /* Number of select/mask bytes */
+ u16 num_entry; /* Number of scenario entry */
+ u16 end; /* Last addressable entry from start of table */
+ u8 eff_width; /* Available width in bytes to match */
+ u8 pid_idx; /* Byte index used to match profile ID */
+ u8 rng_chk_idx; /* Byte index used to match range checkers result */
+ u8 pkt_dir_idx; /* Byte index used to match packet direction */
+};
+
+/* Input fields needed to allocate ACL table */
+struct ice_acl_alloc_tbl {
+ /* Table's width in number of bytes matched */
+ u16 width;
+ /* Table's depth in number of entries. */
+ u16 depth;
+ u8 num_dependent_alloc_ids;
+ /* true for concurrent table type */
+ u8 concurr;
+
+ /* Amount of action pairs per table entry. Minimal valid
+ * value for this field is 1 (e.g. single pair of actions)
+ */
+ u8 act_pairs_per_entry;
+ union {
+ struct ice_aqc_acl_alloc_table_data data_buf;
+ struct ice_aqc_acl_generic resp_buf;
+ } buf;
+};
+
+int ice_acl_create_tbl(struct ice_hw *hw, struct ice_acl_tbl_params *params);
+int ice_acl_destroy_tbl(struct ice_hw *hw);
+int ice_aq_alloc_acl_tbl(struct ice_hw *hw, struct ice_acl_alloc_tbl *tbl,
+ struct ice_sq_cd *cd);
+int ice_aq_dealloc_acl_tbl(struct ice_hw *hw, u16 alloc_id,
+ struct ice_aqc_acl_generic *buf,
+ struct ice_sq_cd *cd);
+int ice_aq_program_acl_entry(struct ice_hw *hw, u8 tcam_idx, u16 entry_idx,
+ struct ice_aqc_acl_data *buf,
+ struct ice_sq_cd *cd);
+int ice_aq_program_actpair(struct ice_hw *hw, u8 act_mem_idx, u16 act_entry_idx,
+ struct ice_aqc_actpair *buf, struct ice_sq_cd *cd);
+int ice_aq_alloc_acl_scen(struct ice_hw *hw, u16 *scen_id,
+ struct ice_aqc_acl_scen *buf, struct ice_sq_cd *cd);
+
+#endif /* _ICE_ACL_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 07fc72da347c..87f215f47072 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -303,6 +303,7 @@ struct ice_aqc_vsi_props {
#define ICE_AQ_VSI_PROP_RXQ_MAP_VALID BIT(6)
#define ICE_AQ_VSI_PROP_Q_OPT_VALID BIT(7)
#define ICE_AQ_VSI_PROP_OUTER_UP_VALID BIT(8)
+#define ICE_AQ_VSI_PROP_ACL_VALID BIT(10)
#define ICE_AQ_VSI_PROP_FLOW_DIR_VALID BIT(11)
#define ICE_AQ_VSI_PROP_PASID_VALID BIT(12)
/* switch section */
@@ -423,8 +424,10 @@ struct ice_aqc_vsi_props {
u8 q_opt_reserved[3];
/* outer up section */
__le32 outer_up_table; /* same structure and defines as ingress tbl */
- /* section 10 */
- __le16 sect_10_reserved;
+ /* ACL section */
+ __le16 acl_def_act;
+#define ICE_AQ_VSI_ACL_DEF_RX_PROF_M GENMASK(3, 0)
+#define ICE_AQ_VSI_ACL_DEF_RX_TABLE_M GENMASK(7, 4)
/* flow director section */
__le16 fd_options;
#define ICE_AQ_VSI_FD_ENABLE BIT(0)
@@ -1976,6 +1979,199 @@ struct ice_aqc_neigh_dev_req {
__le32 addr_low;
};
+/* Allocate ACL table (indirect 0x0C10) */
+#define ICE_AQC_ACL_KEY_WIDTH_BYTES 5
+#define ICE_AQC_ACL_TCAM_DEPTH 512
+#define ICE_ACL_ENTRY_ALLOC_UNIT 64
+#define ICE_AQC_MAX_CONCURRENT_ACL_TBL 15
+#define ICE_AQC_MAX_ACTION_MEMORIES 20
+#define ICE_AQC_ACL_SLICES 16
+#define ICE_AQC_ALLOC_ID_4K 0x1000
+/* The ACL block supports up to 8 actions per a single output. */
+#define ICE_AQC_TBL_MAX_ACTION_PAIRS 4
+
+#define ICE_AQC_MAX_TCAM_ALLOC_UNITS (ICE_AQC_ACL_TCAM_DEPTH / \
+ ICE_ACL_ENTRY_ALLOC_UNIT)
+#define ICE_AQC_ACL_ALLOC_UNITS (ICE_AQC_ACL_SLICES * \
+ ICE_AQC_MAX_TCAM_ALLOC_UNITS)
+
+struct ice_aqc_acl_alloc_table {
+ __le16 table_width;
+ __le16 table_depth;
+ u8 act_pairs_per_entry;
+ u8 table_type;
+ __le16 reserved;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+#define ICE_AQC_CONCURR_ID_INVALID 0xffff
+/* Allocate ACL table command buffer format */
+struct ice_aqc_acl_alloc_table_data {
+ /* Dependent table AllocIDs. Each word in this 15 word array specifies
+ * a dependent table AllocID according to the amount specified in the
+ * "table_type" field. All unused words shall be set to
+ * ICE_AQC_CONCURR_ID_INVALID
+ */
+ __le16 alloc_ids[ICE_AQC_MAX_CONCURRENT_ACL_TBL];
+};
+
+/* Deallocate ACL table (indirect 0x0C11) */
+
+/* Following structure is common and used in case of deallocation
+ * of ACL table and action-pair
+ */
+struct ice_aqc_acl_tbl_actpair {
+ __le16 alloc_id;
+ u8 reserved[6];
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* This response structure is same in case of alloc/dealloc table,
+ * alloc/dealloc action-pair
+ */
+struct ice_aqc_acl_generic {
+ /* if alloc_id is below 0x1000 then allocation failed due to
+ * unavailable resources, else this is set by FW to identify
+ * table allocation
+ */
+ __le16 alloc_id;
+
+ union {
+ /* to be used only in case of alloc/dealloc table */
+ struct {
+ /* Set to 0xFF for a failed allocation */
+ u8 first_tcam;
+ /* This index shall be set to the value of first_tcam
+ * for single TCAM block allocation, otherwise set to
+ * 0xFF for a failed allocation.
+ */
+ u8 last_tcam;
+ } table;
+ /* reserved in case of alloc/dealloc action-pair */
+ struct {
+ __le16 reserved;
+ } act_pair;
+ } ops;
+
+ /* index of first entry (in both TCAM and action memories),
+ * otherwise set to 0xFF for a failed allocation
+ */
+ __le16 first_entry;
+ /* index of last entry (in both TCAM and action memories),
+ * otherwise set to 0xFF for a failed allocation
+ */
+ __le16 last_entry;
+
+ /* Each act_mem element specifies the order of the memory
+ * otherwise 0xFF
+ */
+ u8 act_mem[ICE_AQC_MAX_ACTION_MEMORIES];
+};
+
+/* Update ACL scenario (direct 0x0C1B)
+ * Query ACL scenario (direct 0x0C23)
+ */
+struct ice_aqc_acl_update_query_scen {
+ __le16 scen_id;
+ u8 reserved[6];
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+#define ICE_AQC_ACL_BYTE_SEL_BASE 0x20
+#define ICE_AQC_ACL_BYTE_SEL_BASE_PID 0x3E
+#define ICE_AQC_ACL_BYTE_SEL_BASE_PKT_DIR ICE_AQC_ACL_BYTE_SEL_BASE
+#define ICE_AQC_ACL_BYTE_SEL_BASE_RNG_CHK 0x3F
+
+#define ICE_AQC_ACL_ALLOC_SCE_START_CMP BIT(0)
+#define ICE_AQC_ACL_ALLOC_SCE_START_SET BIT(1)
+
+#define ICE_AQC_ACL_SCE_ACT_MEM_EN BIT(7)
+
+/* Input buffer format in case allocate/update ACL scenario and same format
+ * is used for response buffer in case of query ACL scenario.
+ * NOTE: de-allocate ACL scenario is direct command and doesn't require
+ * "buffer", hence no buffer format.
+ */
+struct ice_aqc_acl_scen {
+ struct {
+ /* Byte [x] selection for the TCAM key. This value must be set
+ * to 0x0 for unused TCAM.
+ * Only Bit 6..0 is used in each byte and MSB is reserved
+ */
+ u8 tcam_select[5];
+ /* TCAM Block entry masking. This value should be set to 0x0 for
+ * unused TCAM
+ */
+ u8 chnk_msk;
+ /* Bit 0 : masks TCAM entries 0-63
+ * Bit 1 : masks TCAM entries 64-127
+ * Bit 2 to 7 : follow the pattern of bit 0 and 1
+ */
+ u8 start_cmp_set;
+ } tcam_cfg[ICE_AQC_ACL_SLICES];
+
+ /* Each byte, Bit 6..0: action memory association to a TCAM block,
+ * otherwise it shall be set to 0x0 for disabled memory action.
+ * Bit 7 (ICE_AQC_ACL_SCE_ACT_MEM_EN): action memory enable for this
+ * scenario
+ */
+ u8 act_mem_cfg[ICE_AQC_MAX_ACTION_MEMORIES];
+};
+
+/* Program ACL actionpair (indirect 0x0C1C) */
+struct ice_aqc_acl_actpair {
+ u8 act_mem_index;
+ u8 reserved;
+ /* Entry index in action memory */
+ __le16 act_entry_index;
+ __le32 reserved2;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Input buffer format for program/query action-pair admin command */
+struct ice_acl_act_entry {
+ /* Action priority, values must be between 0..7 */
+ u8 prio;
+ /* Action meta-data identifier. This field should be set to 0x0
+ * for a NOP action
+ */
+ u8 mdid;
+ __le16 value;
+};
+
+#define ICE_ACL_NUM_ACT_PER_ACT_PAIR 2
+struct ice_aqc_actpair {
+ struct ice_acl_act_entry act[ICE_ACL_NUM_ACT_PER_ACT_PAIR];
+};
+
+/* Program ACL entry (indirect 0x0C20) */
+struct ice_aqc_acl_entry {
+ u8 tcam_index;
+ u8 reserved;
+ __le16 entry_index;
+ __le32 reserved2;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Input buffer format in case of program ACL entry and response buffer format
+ * in case of query ACL entry
+ */
+struct ice_aqc_acl_data {
+ /* Entry key and entry key invert are 40 bits wide.
+ * Byte 0..4 : entry key and Byte 5..7 are reserved
+ * Byte 8..12: entry key invert and Byte 13..15 are reserved
+ */
+ struct {
+ u8 val[5];
+ u8 reserved[3];
+ } entry_key, entry_key_invert;
+};
+
/* Add Tx LAN Queues (indirect 0x0C30) */
struct ice_aqc_add_txqs {
u8 num_qgrps;
@@ -2651,6 +2847,14 @@ enum ice_adminq_opc {
/* Sideband Control Interface commands */
ice_aqc_opc_neighbour_device_request = 0x0C00,
+ /* ACL commands */
+ ice_aqc_opc_alloc_acl_tbl = 0x0C10,
+ ice_aqc_opc_dealloc_acl_tbl = 0x0C11,
+ ice_aqc_opc_update_acl_scen = 0x0C1B,
+ ice_aqc_opc_program_acl_actpair = 0x0C1C,
+ ice_aqc_opc_program_acl_entry = 0x0C20,
+ ice_aqc_opc_query_acl_scen = 0x0C23,
+
/* Tx queue handling commands/events */
ice_aqc_opc_add_txqs = 0x0C30,
ice_aqc_opc_dis_txqs = 0x0C31,
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index 8492df497340..161acd1cf095 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -54,6 +54,7 @@ static inline u32 ice_round_to_num(u32 N, u32 R)
#define ICE_DBG_RDMA BIT_ULL(15)
#define ICE_DBG_PKG BIT_ULL(16)
#define ICE_DBG_RES BIT_ULL(17)
+#define ICE_DBG_ACL BIT_ULL(18)
#define ICE_DBG_PTP BIT_ULL(19)
#define ICE_DBG_AQ_MSG BIT_ULL(24)
#define ICE_DBG_AQ_DESC BIT_ULL(25)
@@ -1009,6 +1010,8 @@ struct ice_hw {
struct udp_tunnel_nic_shared udp_tunnel_shared;
struct udp_tunnel_nic_info udp_tunnel_nic;
+ struct ice_acl_tbl *acl_tbl;
+
/* dvm boost update information */
struct ice_dvm_table dvm_upd;
diff --git a/drivers/net/ethernet/intel/ice/ice_acl.c b/drivers/net/ethernet/intel/ice/ice_acl.c
new file mode 100644
index 000000000000..3d963c6071dc
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_acl.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2018-2026, Intel Corporation. */
+
+#include "ice_acl.h"
+
+/**
+ * ice_aq_alloc_acl_tbl - allocate ACL table
+ * @hw: pointer to the HW struct
+ * @tbl: pointer to ice_acl_alloc_tbl struct
+ * @cd: pointer to command details structure or NULL
+ *
+ * Allocate ACL table (indirect 0x0C10)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_alloc_acl_tbl(struct ice_hw *hw, struct ice_acl_alloc_tbl *tbl,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_alloc_table *cmd;
+ struct libie_aq_desc desc;
+
+ if (!tbl->act_pairs_per_entry)
+ return -EINVAL;
+
+ if (tbl->act_pairs_per_entry > ICE_AQC_MAX_ACTION_MEMORIES)
+ return -ENOSPC;
+
+ /* If this is concurrent table, then alloc_ids buffer shall be valid and
+ * contain AllocIDs of dependent tables. 'num_dependent_alloc_ids'
+ * should be non-zero and within limit.
+ */
+ if (tbl->concurr) {
+ if (!tbl->num_dependent_alloc_ids)
+ return -EINVAL;
+ if (tbl->num_dependent_alloc_ids >
+ ICE_AQC_MAX_CONCURRENT_ACL_TBL)
+ return -ERANGE;
+ }
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_alloc_acl_tbl);
+ desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD);
+
+ cmd = libie_aq_raw(&desc);
+ cmd->table_width = cpu_to_le16(tbl->width * BITS_PER_BYTE);
+ cmd->table_depth = cpu_to_le16(tbl->depth);
+ cmd->act_pairs_per_entry = tbl->act_pairs_per_entry;
+ if (tbl->concurr)
+ cmd->table_type = tbl->num_dependent_alloc_ids;
+
+ return ice_aq_send_cmd(hw, &desc, &tbl->buf, sizeof(tbl->buf), cd);
+}
+
+/**
+ * ice_aq_dealloc_acl_tbl - deallocate ACL table
+ * @hw: pointer to the HW struct
+ * @alloc_id: allocation ID of the table being released
+ * @buf: address of indirect data buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Deallocate ACL table (indirect 0x0C11)
+ *
+ * NOTE: This command has no buffer format for command itself but response
+ * format is 'struct ice_aqc_acl_generic', pass ptr to that struct
+ * as 'buf' and its size as 'buf_size'
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_dealloc_acl_tbl(struct ice_hw *hw, u16 alloc_id,
+ struct ice_aqc_acl_generic *buf,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_tbl_actpair *cmd;
+ struct libie_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_dealloc_acl_tbl);
+ cmd = libie_aq_raw(&desc);
+ cmd->alloc_id = cpu_to_le16(alloc_id);
+
+ return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
+}
+
+/**
+ * ice_aq_program_acl_entry - program ACL entry
+ * @hw: pointer to the HW struct
+ * @tcam_idx: Updated TCAM block index
+ * @entry_idx: updated entry index
+ * @buf: address of indirect data buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Program ACL entry (direct 0x0C20)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_program_acl_entry(struct ice_hw *hw, u8 tcam_idx, u16 entry_idx,
+ struct ice_aqc_acl_data *buf, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_entry *cmd;
+ struct libie_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_program_acl_entry);
+ desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD);
+
+ cmd = libie_aq_raw(&desc);
+ cmd->tcam_index = tcam_idx;
+ cmd->entry_index = cpu_to_le16(entry_idx);
+
+ return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
+}
+
+/**
+ * ice_aq_program_actpair - program ACL action pair
+ * @hw: pointer to the HW struct
+ * @act_mem_idx: action memory index to program/update/query
+ * @act_entry_idx: the entry index in action memory to be programmed/updated
+ * @buf: address of indirect data buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Program action entries (indirect 0x0C1C)
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_aq_program_actpair(struct ice_hw *hw, u8 act_mem_idx, u16 act_entry_idx,
+ struct ice_aqc_actpair *buf, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_acl_actpair *cmd;
+ struct libie_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_program_acl_actpair);
+ desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD);
+
+ cmd = libie_aq_raw(&desc);
+ cmd->act_mem_index = act_mem_idx;
+ cmd->act_entry_index = cpu_to_le16(act_entry_idx);
+
+ return ice_aq_send_cmd(hw, &desc, buf, sizeof(*buf), cd);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c b/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c
new file mode 100644
index 000000000000..d821b2c923d5
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_acl_ctrl.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2018-2026, Intel Corporation. */
+
+#include "ice_acl.h"
+
+/* Determine the TCAM index of entry 'e' within the ACL table */
+#define ICE_ACL_TBL_TCAM_IDX(e) ((e) / ICE_AQC_ACL_TCAM_DEPTH)
+
+/**
+ * ice_acl_init_tbl - initialize ACL table
+ * @hw: pointer to the hardware structure
+ *
+ * Invalidate TCAM entries and action pairs.
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_acl_init_tbl(struct ice_hw *hw)
+{
+ struct ice_aqc_actpair act_buf = {};
+ struct ice_aqc_acl_data buf = {};
+ struct ice_acl_tbl *tbl;
+ u8 tcam_idx;
+ int err = 0;
+ u16 idx;
+
+ tbl = hw->acl_tbl;
+
+ tcam_idx = tbl->first_tcam;
+ idx = tbl->first_entry;
+ while (tcam_idx < tbl->last_tcam ||
+ (tcam_idx == tbl->last_tcam && idx <= tbl->last_entry)) {
+ /* Use the same value for entry_key and entry_key_inv since
+ * we are initializing the fields to 0
+ */
+ err = ice_aq_program_acl_entry(hw, tcam_idx, idx, &buf, NULL);
+ if (err)
+ return err;
+
+ if (++idx > tbl->last_entry) {
+ tcam_idx++;
+ idx = tbl->first_entry;
+ }
+ }
+
+ for (int i = 0; i < ICE_AQC_MAX_ACTION_MEMORIES; i++) {
+ u16 act_entry_idx;
+
+ if (tbl->act_mems[i].act_mem == ICE_ACL_ACT_MEM_ACT_MEM_INVAL)
+ continue;
+
+ for (act_entry_idx = tbl->first_entry;
+ act_entry_idx <= tbl->last_entry; act_entry_idx++) {
+ /* Invalidate all allocated action pairs */
+ err = ice_aq_program_actpair(hw, i, act_entry_idx,
+ &act_buf, NULL);
+ if (err)
+ return err;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * ice_acl_assign_act_mems_to_tcam - assign number of action memories to TCAM
+ * @tbl: pointer to ACL table structure
+ * @cur_tcam: Index of current TCAM. Value = 0 to (ICE_AQC_ACL_SLICES - 1)
+ * @cur_mem_idx: Index of current action memory bank. Value = 0 to
+ * (ICE_AQC_MAX_ACTION_MEMORIES - 1)
+ * @num_mem: Number of action memory banks for this TCAM
+ *
+ * Assign "num_mem" valid action memory banks from "curr_mem_idx" to
+ * "curr_tcam" TCAM.
+ */
+static void
+ice_acl_assign_act_mems_to_tcam(struct ice_acl_tbl *tbl, u8 cur_tcam,
+ u8 *cur_mem_idx, u8 num_mem)
+{
+ u8 mem_cnt;
+
+ for (mem_cnt = 0;
+ *cur_mem_idx < ICE_AQC_MAX_ACTION_MEMORIES && mem_cnt < num_mem;
+ (*cur_mem_idx)++) {
+ struct ice_acl_act_mem *p_mem = &tbl->act_mems[*cur_mem_idx];
+
+ if (p_mem->act_mem == ICE_ACL_ACT_MEM_ACT_MEM_INVAL)
+ continue;
+
+ p_mem->member_of_tcam = cur_tcam;
+
+ mem_cnt++;
+ }
+}
+
+/**
+ * ice_acl_divide_act_mems_to_tcams - assign action memory banks to TCAMs
+ * @tbl: pointer to ACL table structure
+ *
+ * Figure out how to divide given action memory banks to given TCAMs. This
+ * division is for SW book keeping. In the time when scenario is created,
+ * an action memory bank can be used for different TCAM.
+ *
+ * For example, given that we have 2x2 ACL table with each table entry has
+ * 2 action memory pairs. As the result, we will have 4 TCAMs (T1,T2,T3,T4)
+ * and 4 action memory banks (A1,A2,A3,A4)
+ * [T1 - T2] { A1 - A2 }
+ * [T3 - T4] { A3 - A4 }
+ * In the time when we need to create a scenario, for example, 2x1 scenario,
+ * we will use [T3,T4] in a cascaded layout. As it is a requirement that all
+ * action memory banks in a cascaded TCAM's row will need to associate with
+ * the last TCAM. Thus, we will associate action memory banks [A3] and [A4]
+ * for TCAM [T4].
+ * For SW book-keeping purpose, we will keep theoretical maps between TCAM
+ * [Tn] to action memory bank [An].
+ */
+static void ice_acl_divide_act_mems_to_tcams(struct ice_acl_tbl *tbl)
+{
+ u16 num_cscd, stack_level, stack_idx, min_act_mem;
+ u8 tcam_idx = tbl->first_tcam;
+ u16 max_idx_to_get_extra;
+ u8 mem_idx = 0;
+
+ /* Determine number of stacked TCAMs */
+ stack_level = DIV_ROUND_UP(tbl->info.depth, ICE_AQC_ACL_TCAM_DEPTH);
+
+ /* Determine number of cascaded TCAMs */
+ num_cscd = DIV_ROUND_UP(tbl->info.width, ICE_AQC_ACL_KEY_WIDTH_BYTES);
+
+ /* In a line of cascaded TCAM, given the number of action memory
+ * banks per ACL table entry, we want to fairly divide these action
+ * memory banks between these TCAMs.
+ *
+ * For example, there are 3 TCAMs (TCAM 3,4,5) in a line of
+ * cascaded TCAM, and there are 7 act_mems for each ACL table entry.
+ * The result is:
+ * [TCAM_3 will have 3 act_mems]
+ * [TCAM_4 will have 2 act_mems]
+ * [TCAM_5 will have 2 act_mems]
+ */
+ min_act_mem = tbl->info.entry_act_pairs / num_cscd;
+ max_idx_to_get_extra = tbl->info.entry_act_pairs % num_cscd;
+
+ for (stack_idx = 0; stack_idx < stack_level; stack_idx++) {
+ u16 i;
+
+ for (i = 0; i < num_cscd; i++) {
+ u8 total_act_mem = min_act_mem;
+
+ if (i < max_idx_to_get_extra)
+ total_act_mem++;
+
+ ice_acl_assign_act_mems_to_tcam(tbl, tcam_idx,
+ &mem_idx,
+ total_act_mem);
+
+ tcam_idx++;
+ }
+ }
+}
+
+/**
+ * ice_acl_create_tbl - create ACL table
+ * @hw: pointer to the HW struct
+ * @params: parameters for the table to be created
+ *
+ * Create a LEM table for ACL usage. We are currently starting with some fixed
+ * values for the size of the table, but this will need to grow as more flow
+ * entries are added by the user level.
+ *
+ * Return: 0 on success, negative on error
+ */
+int
+ice_acl_create_tbl(struct ice_hw *hw, struct ice_acl_tbl_params *params)
+{
+ struct ice_acl_alloc_tbl tbl_alloc = {};
+ struct ice_aqc_acl_generic *resp_buf;
+ u16 width, depth, first_e, last_e;
+ struct ice_acl_tbl *tbl;
+ int err;
+
+ if (hw->acl_tbl)
+ return -EEXIST;
+
+ /* round up the width to the next TCAM width boundary. */
+ width = roundup(params->width, (u16)ICE_AQC_ACL_KEY_WIDTH_BYTES);
+ /* depth should be provided in chunk (64 entry) increments */
+ depth = ALIGN(params->depth, ICE_ACL_ENTRY_ALLOC_UNIT);
+
+ if (params->entry_act_pairs < width / ICE_AQC_ACL_KEY_WIDTH_BYTES) {
+ params->entry_act_pairs = width / ICE_AQC_ACL_KEY_WIDTH_BYTES;
+
+ if (params->entry_act_pairs > ICE_AQC_TBL_MAX_ACTION_PAIRS)
+ params->entry_act_pairs = ICE_AQC_TBL_MAX_ACTION_PAIRS;
+ }
+
+ /* Validate that width*depth will not exceed the TCAM limit */
+ if ((DIV_ROUND_UP(depth, ICE_AQC_ACL_TCAM_DEPTH) *
+ (width / ICE_AQC_ACL_KEY_WIDTH_BYTES)) > ICE_AQC_ACL_SLICES)
+ return -ENOSPC;
+
+ tbl_alloc.width = width;
+ tbl_alloc.depth = depth;
+ tbl_alloc.act_pairs_per_entry = params->entry_act_pairs;
+ tbl_alloc.concurr = params->concurr;
+
+ if (params->concurr) {
+ tbl_alloc.num_dependent_alloc_ids =
+ ICE_AQC_MAX_CONCURRENT_ACL_TBL;
+
+ for (int i = 0; i < ICE_AQC_MAX_CONCURRENT_ACL_TBL; i++)
+ tbl_alloc.buf.data_buf.alloc_ids[i] =
+ cpu_to_le16(params->dep_tbls[i]);
+ }
+
+ err = ice_aq_alloc_acl_tbl(hw, &tbl_alloc, NULL);
+ if (err) {
+ if (le16_to_cpu(tbl_alloc.buf.resp_buf.alloc_id) <
+ ICE_AQC_ALLOC_ID_4K)
+ dev_err(ice_hw_to_dev(hw), "Alloc ACL table failed. Unavailable resource.\n");
+ else
+ dev_err(ice_hw_to_dev(hw), "AQ allocation of ACL failed with error. status: %d\n",
+ err);
+ return err;
+ }
+
+ tbl = kzalloc_obj(*tbl);
+ if (!tbl)
+ return -ENOMEM;
+
+ resp_buf = &tbl_alloc.buf.resp_buf;
+
+ /* Retrieve information of the allocated table */
+ tbl->id = le16_to_cpu(resp_buf->alloc_id);
+ tbl->first_tcam = resp_buf->ops.table.first_tcam;
+ tbl->last_tcam = resp_buf->ops.table.last_tcam;
+ tbl->first_entry = le16_to_cpu(resp_buf->first_entry);
+ tbl->last_entry = le16_to_cpu(resp_buf->last_entry);
+
+ tbl->info = *params;
+ tbl->info.width = width;
+ tbl->info.depth = depth;
+ hw->acl_tbl = tbl;
+
+ for (int i = 0; i < ICE_AQC_MAX_ACTION_MEMORIES; i++)
+ tbl->act_mems[i].act_mem = resp_buf->act_mem[i];
+
+ /* Figure out which TCAMs that these newly allocated action memories
+ * belong to.
+ */
+ ice_acl_divide_act_mems_to_tcams(tbl);
+
+ /* Initialize the resources allocated by invalidating all TCAM entries
+ * and all the action pairs
+ */
+ err = ice_acl_init_tbl(hw);
+ if (err) {
+ kfree(tbl);
+ hw->acl_tbl = NULL;
+ ice_debug(hw, ICE_DBG_ACL, "Initialization of TCAM entries failed. status: %d\n",
+ err);
+ return err;
+ }
+
+ first_e = (tbl->first_tcam * ICE_AQC_MAX_TCAM_ALLOC_UNITS) +
+ (tbl->first_entry / ICE_ACL_ENTRY_ALLOC_UNIT);
+ last_e = (tbl->last_tcam * ICE_AQC_MAX_TCAM_ALLOC_UNITS) +
+ (tbl->last_entry / ICE_ACL_ENTRY_ALLOC_UNIT);
+
+ /* Indicate available entries in the table */
+ bitmap_set(tbl->avail, first_e, last_e - first_e + 1);
+
+ INIT_LIST_HEAD(&tbl->scens);
+
+ return 0;
+}
+
+/**
+ * ice_acl_destroy_tbl - Destroy a previously created LEM table for ACL
+ * @hw: pointer to the HW struct
+ *
+ * Return: 0 on success, negative on error
+ */
+int ice_acl_destroy_tbl(struct ice_hw *hw)
+{
+ struct ice_aqc_acl_generic resp_buf;
+ int err;
+
+ if (!hw->acl_tbl)
+ return -ENOENT;
+
+ err = ice_aq_dealloc_acl_tbl(hw, hw->acl_tbl->id, &resp_buf, NULL);
+ if (err) {
+ ice_debug(hw, ICE_DBG_ACL, "AQ de-allocation of ACL failed. status: %d\n",
+ err);
+ return err;
+ }
+
+ kfree(hw->acl_tbl);
+ hw->acl_tbl = NULL;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index ee604b49dd47..1cdb49cd42c3 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -17,6 +17,7 @@
#include "devlink/port.h"
#include "ice_sf_eth.h"
#include "ice_hwmon.h"
+#include "ice_acl.h"
/* Including ice_trace.h with CREATE_TRACE_POINTS defined will generate the
* ice tracepoint functions. This must be done exactly once across the
* ice driver.
@@ -4324,6 +4325,47 @@ static int ice_send_version(struct ice_pf *pf)
return ice_aq_send_driver_ver(&pf->hw, &dv, NULL);
}
+/**
+ * ice_init_acl - Initializes the ACL block
+ * @pf: ptr to PF device
+ *
+ * Return: 0 on success, negative on error
+ */
+static int ice_init_acl(struct ice_pf *pf)
+{
+ struct ice_acl_tbl_params params = {};
+ struct ice_hw *hw = &pf->hw;
+ int divider;
+
+ /* Creates a single ACL table that consist of src_ip(4 byte),
+ * dest_ip(4 byte), src_port(2 byte) and dst_port(2 byte) for a total
+ * of 12 bytes (96 bits), hence 120 bit wide keys, i.e. 3 TCAM slices.
+ * If the given hardware card contains less than 8 PFs (ports) then
+ * each PF will have its own TCAM slices. For 8 PFs, a given slice will
+ * be shared by 2 different PFs.
+ */
+ if (hw->dev_caps.num_funcs < 8)
+ divider = ICE_ACL_ENTIRE_SLICE;
+ else
+ divider = ICE_ACL_HALF_SLICE;
+
+ params.width = ICE_AQC_ACL_KEY_WIDTH_BYTES * 3;
+ params.depth = ICE_AQC_ACL_TCAM_DEPTH / divider;
+ params.entry_act_pairs = 1;
+ params.concurr = false;
+
+ return ice_acl_create_tbl(hw, ¶ms);
+}
+
+/**
+ * ice_deinit_acl - Unroll the initialization of the ACL block
+ * @pf: ptr to PF device
+ */
+static void ice_deinit_acl(struct ice_pf *pf)
+{
+ ice_acl_destroy_tbl(&pf->hw);
+}
+
/**
* ice_init_fdir - Initialize flow director VSI and configuration
* @pf: pointer to the PF instance
@@ -4745,6 +4787,12 @@ static void ice_init_features(struct ice_pf *pf)
if (ice_init_fdir(pf))
dev_err(dev, "could not initialize flow director\n");
+ if (test_bit(ICE_FLAG_FD_ENA, pf->flags)) {
+ /* Note: ACL init failure is non-fatal to load */
+ if (ice_init_acl(pf))
+ dev_err(dev, "Failed to initialize ACL\n");
+ }
+
/* Note: DCB init failure is non-fatal to load */
if (ice_init_pf_dcb(pf, false)) {
clear_bit(ICE_FLAG_DCB_CAPABLE, pf->flags);
@@ -4767,6 +4815,7 @@ static void ice_deinit_features(struct ice_pf *pf)
ice_deinit_lag(pf);
if (test_bit(ICE_FLAG_DCB_CAPABLE, pf->flags))
ice_cfg_lldp_mib_change(&pf->hw, false);
+ ice_deinit_acl(pf);
ice_deinit_fdir(pf);
if (ice_is_feature_supported(pf, ICE_F_GNSS))
ice_gnss_exit(pf);
--
2.49.0
^ permalink raw reply related
* [PATCH iwl-next v2 09/10] ice: re-introduce ice_dealloc_flow_entry() helper
From: Marcin Szycik @ 2026-04-09 12:00 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Aleksandr Loktionov, Przemek Kitszel
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
It was removed in commit ad667d626825 ("ice: remove null checks before
devm_kfree() calls"). Now it's useful again.
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
---
v2:
* Add this patch
---
drivers/net/ethernet/intel/ice/ice_flow.c | 33 ++++++++++++++---------
1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.c b/drivers/net/ethernet/intel/ice/ice_flow.c
index 144d8326d4f9..20ee85b0bcf0 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.c
+++ b/drivers/net/ethernet/intel/ice/ice_flow.c
@@ -1589,6 +1589,23 @@ ice_flow_find_prof_id(struct ice_hw *hw, enum ice_block blk, u64 prof_id)
return NULL;
}
+/**
+ * ice_dealloc_flow_entry - Deallocate flow entry memory
+ * @hw: pointer to the HW struct
+ * @entry: flow entry to be removed
+ */
+static void
+ice_dealloc_flow_entry(struct ice_hw *hw, struct ice_flow_entry *entry)
+{
+ if (!entry)
+ return;
+
+ kfree(entry->entry);
+ kfree(entry->range_buf);
+ kfree(entry->acts);
+ devm_kfree(ice_hw_to_dev(hw), entry);
+}
+
/**
* ice_flow_get_hw_prof - return the HW profile for a specific profile ID handle
* @hw: pointer to the HW struct
@@ -1760,11 +1777,7 @@ static int ice_flow_rem_entry_sync(struct ice_hw *hw, enum ice_block blk,
}
list_del(&entry->l_entry);
-
- kfree(entry->entry);
- kfree(entry->range_buf);
- kfree(entry->acts);
- devm_kfree(ice_hw_to_dev(hw), entry);
+ ice_dealloc_flow_entry(hw, entry);
return 0;
}
@@ -2899,10 +2912,7 @@ static int ice_flow_acl_add_scen_entry_sync(struct ice_hw *hw,
/* As we don't add the new entry to our SW DB, deallocate its
* memories, and return the exist entry to the caller
*/
- kfree(e->entry);
- kfree(e->range_buf);
- kfree(e->acts);
- devm_kfree(ice_hw_to_dev(hw), e);
+ ice_dealloc_flow_entry(hw, e);
*entry = exist;
}
out:
@@ -3021,10 +3031,7 @@ int ice_flow_add_entry(struct ice_hw *hw, enum ice_block blk, u64 prof_id,
out:
if (status && e) {
- kfree(e->entry);
- kfree(e->range_buf);
- kfree(e->acts);
- devm_kfree(ice_hw_to_dev(hw), e);
+ ice_dealloc_flow_entry(hw, e);
}
return status;
--
2.49.0
^ permalink raw reply related
* [PATCH iwl-next v2 06/10] ice: use plain alloc/dealloc for ice_ntuple_fltr
From: Marcin Szycik @ 2026-04-09 11:59 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Aleksandr Loktionov
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
Change struct ice_ntuple_fltr allocation from devm_ to plain alloc,
since its lifetime is not tied to the device. All such objects are being
removed on device remove via ice_deinit_features() -> ice_deinit_fdir()
-> ice_vsi_manage_fdir() -> ice_fdir_del_all_fltrs()
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
v2:
* Add this patch
---
drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
index eca15cb2665e..b5a841732b58 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
@@ -1748,7 +1748,7 @@ void ice_fdir_del_all_fltrs(struct ice_vsi *vsi)
ice_fdir_update_cntrs(hw, f_rule->flow_type, f_rule->acl_fltr,
false);
list_del(&f_rule->fltr_node);
- devm_kfree(ice_pf_to_dev(pf), f_rule);
+ kfree(f_rule);
}
}
@@ -1855,7 +1855,7 @@ ice_ntuple_update_list_entry(struct ice_pf *pf, struct ice_ntuple_fltr *input,
*/
ice_fdir_do_rem_flow(pf, old_fltr->flow_type);
list_del(&old_fltr->fltr_node);
- devm_kfree(ice_hw_to_dev(hw), old_fltr);
+ kfree(old_fltr);
}
if (!input)
return err;
@@ -2206,7 +2206,7 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
return -ENOSPC;
}
- input = devm_kzalloc(dev, sizeof(*input), GFP_KERNEL);
+ input = kzalloc_obj(*input);
if (!input)
return -ENOMEM;
@@ -2250,7 +2250,7 @@ int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
mutex_unlock(&hw->fdir_fltr_lock);
free_input:
if (ret)
- devm_kfree(dev, input);
+ kfree(input);
return ret;
}
--
2.49.0
^ permalink raw reply related
* [PATCH iwl-next v2 05/10] Revert "ice: remove unused ice_flow_entry fields"
From: Marcin Szycik @ 2026-04-09 11:59 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Aleksandr Loktionov, Przemek Kitszel
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
This reverts commit 4cd7bc7144ec2c0bb27208c3bb1f153dfd44b1c7.
These fields will be needed in the following commits.
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
---
v2:
* Add this patch
---
drivers/net/ethernet/intel/ice/ice_flow.h | 3 +++
drivers/net/ethernet/intel/ice/ice_flow.c | 5 ++++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.h b/drivers/net/ethernet/intel/ice/ice_flow.h
index bbfc7b4a432e..ff6af6589862 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.h
+++ b/drivers/net/ethernet/intel/ice/ice_flow.h
@@ -458,8 +458,11 @@ struct ice_flow_entry {
u64 id;
struct ice_flow_prof *prof;
+ /* Flow entry's content */
+ void *entry;
enum ice_flow_priority priority;
u16 vsi_handle;
+ u16 entry_sz;
};
#define ICE_FLOW_ENTRY_HNDL(e) ((u64)(uintptr_t)e)
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.c b/drivers/net/ethernet/intel/ice/ice_flow.c
index 864bbda7e880..440e9fdb6b5b 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.c
+++ b/drivers/net/ethernet/intel/ice/ice_flow.c
@@ -1604,6 +1604,7 @@ ice_flow_rem_entry_sync(struct ice_hw *hw, enum ice_block __always_unused blk,
list_del(&entry->l_entry);
+ devm_kfree(ice_hw_to_dev(hw), entry->entry);
devm_kfree(ice_hw_to_dev(hw), entry);
return 0;
@@ -2024,8 +2025,10 @@ ice_flow_add_entry(struct ice_hw *hw, enum ice_block blk, u64 prof_id,
*entry_h = ICE_FLOW_ENTRY_HNDL(e);
out:
- if (status)
+ if (status && e) {
+ devm_kfree(ice_hw_to_dev(hw), e->entry);
devm_kfree(ice_hw_to_dev(hw), e);
+ }
return status;
}
--
2.49.0
^ permalink raw reply related
* [PATCH iwl-next v2 01/10] ice: rename shared Flow Director functions and structs
From: Marcin Szycik @ 2026-04-09 11:59 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, sandeep.penigalapati, ananth.s, alexander.duyck,
Marcin Szycik, Tony Nguyen, Aleksandr Loktionov
In-Reply-To: <20260409120003.2719-1-marcin.szycik@linux.intel.com>
From: Tony Nguyen <anthony.l.nguyen@intel.com>
Rename shared Flow Director functions and structs. These entities are
currently used to add Flow Director filters, however, they will be
expanded to also add ACL filters. Rename the functions and struct,
replacing 'fdir' to 'ntuple', to reflect that they are being used for
ntuple filters and are not solely used for Flow Director.
Rename the file to also reflect this change.
Co-developed-by: Paul M Stillwell Jr <paul.m.stillwell.jr@intel.com>
Signed-off-by: Paul M Stillwell Jr <paul.m.stillwell.jr@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Co-developed-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
---
v2:
* Also rename struct ice_fdir_fltr and file
---
drivers/net/ethernet/intel/ice/Makefile | 2 +-
drivers/net/ethernet/intel/ice/ice.h | 6 +-
drivers/net/ethernet/intel/ice/ice_arfs.h | 2 +-
drivers/net/ethernet/intel/ice/ice_fdir.h | 12 ++--
drivers/net/ethernet/intel/ice/ice_arfs.c | 8 +--
drivers/net/ethernet/intel/ice/ice_ethtool.c | 4 +-
...ce_ethtool_fdir.c => ice_ethtool_ntuple.c} | 58 ++++++++++---------
drivers/net/ethernet/intel/ice/ice_fdir.c | 18 +++---
drivers/net/ethernet/intel/ice/virt/fdir.c | 28 ++++-----
9 files changed, 70 insertions(+), 68 deletions(-)
rename drivers/net/ethernet/intel/ice/{ice_ethtool_fdir.c => ice_ethtool_ntuple.c} (97%)
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index 5b2c666496e7..c310c209bc7d 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -24,7 +24,7 @@ ice-y := ice_main.o \
ice_vsi_vlan_ops.o \
ice_vsi_vlan_lib.o \
ice_fdir.o \
- ice_ethtool_fdir.o \
+ ice_ethtool_ntuple.o \
ice_vlan_mode.o \
ice_flex_pipe.o \
ice_flow.o \
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 804f5aa8e9f5..ea1bddfa739d 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -1015,11 +1015,11 @@ void ice_deinit_rdma(struct ice_pf *pf);
bool ice_is_wol_supported(struct ice_hw *hw);
void ice_fdir_del_all_fltrs(struct ice_vsi *vsi);
int
-ice_fdir_write_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input, bool add,
+ice_fdir_write_fltr(struct ice_pf *pf, struct ice_ntuple_fltr *input, bool add,
bool is_tun);
void ice_vsi_manage_fdir(struct ice_vsi *vsi, bool ena);
-int ice_add_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
-int ice_del_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
+int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
+int ice_del_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd);
int ice_get_ethtool_fdir_entry(struct ice_hw *hw, struct ethtool_rxnfc *cmd);
int
ice_get_fdir_fltr_ids(struct ice_hw *hw, struct ethtool_rxnfc *cmd,
diff --git a/drivers/net/ethernet/intel/ice/ice_arfs.h b/drivers/net/ethernet/intel/ice/ice_arfs.h
index 9706293128c3..7393254b7e0a 100644
--- a/drivers/net/ethernet/intel/ice/ice_arfs.h
+++ b/drivers/net/ethernet/intel/ice/ice_arfs.h
@@ -13,7 +13,7 @@ enum ice_arfs_fltr_state {
};
struct ice_arfs_entry {
- struct ice_fdir_fltr fltr_info;
+ struct ice_ntuple_fltr fltr_info;
struct hlist_node list_entry;
u64 time_activated; /* only valid for UDP flows */
u32 flow_id;
diff --git a/drivers/net/ethernet/intel/ice/ice_fdir.h b/drivers/net/ethernet/intel/ice/ice_fdir.h
index 820023c0271f..26d79b1364e7 100644
--- a/drivers/net/ethernet/intel/ice/ice_fdir.h
+++ b/drivers/net/ethernet/intel/ice/ice_fdir.h
@@ -160,7 +160,7 @@ struct ice_fdir_extra {
__be16 vlan_tag; /* VLAN tag info */
};
-struct ice_fdir_fltr {
+struct ice_ntuple_fltr {
struct list_head fltr_node;
enum ice_fltr_ptype flow_type;
@@ -216,18 +216,18 @@ int ice_free_fd_res_cntr(struct ice_hw *hw, u16 cntr_id);
int ice_alloc_fd_guar_item(struct ice_hw *hw, u16 *cntr_id, u16 num_fltr);
int ice_alloc_fd_shrd_item(struct ice_hw *hw, u16 *cntr_id, u16 num_fltr);
void
-ice_fdir_get_prgm_desc(struct ice_hw *hw, struct ice_fdir_fltr *input,
+ice_fdir_get_prgm_desc(struct ice_hw *hw, struct ice_ntuple_fltr *input,
struct ice_fltr_desc *fdesc, bool add);
int
-ice_fdir_get_gen_prgm_pkt(struct ice_hw *hw, struct ice_fdir_fltr *input,
+ice_fdir_get_gen_prgm_pkt(struct ice_hw *hw, struct ice_ntuple_fltr *input,
u8 *pkt, bool frag, bool tun);
int ice_get_fdir_cnt_all(struct ice_hw *hw);
int ice_fdir_num_avail_fltr(struct ice_hw *hw, struct ice_vsi *vsi);
-bool ice_fdir_is_dup_fltr(struct ice_hw *hw, struct ice_fdir_fltr *input);
+bool ice_fdir_is_dup_fltr(struct ice_hw *hw, struct ice_ntuple_fltr *input);
bool ice_fdir_has_frag(enum ice_fltr_ptype flow);
-struct ice_fdir_fltr *
+struct ice_ntuple_fltr *
ice_fdir_find_fltr_by_idx(struct ice_hw *hw, u32 fltr_idx);
void
ice_fdir_update_cntrs(struct ice_hw *hw, enum ice_fltr_ptype flow, bool add);
-void ice_fdir_list_add_fltr(struct ice_hw *hw, struct ice_fdir_fltr *input);
+void ice_fdir_list_add_fltr(struct ice_hw *hw, struct ice_ntuple_fltr *input);
#endif /* _ICE_FDIR_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_arfs.c b/drivers/net/ethernet/intel/ice/ice_arfs.c
index 53b6e2b09eb9..e0335f5e18fe 100644
--- a/drivers/net/ethernet/intel/ice/ice_arfs.c
+++ b/drivers/net/ethernet/intel/ice/ice_arfs.c
@@ -302,7 +302,7 @@ ice_arfs_build_entry(struct ice_vsi *vsi, const struct flow_keys *fk,
u16 rxq_idx, u32 flow_id)
{
struct ice_arfs_entry *arfs_entry;
- struct ice_fdir_fltr *fltr_info;
+ struct ice_ntuple_fltr *fltr_info;
u8 ip_proto;
arfs_entry = devm_kzalloc(ice_pf_to_dev(vsi->back),
@@ -392,8 +392,8 @@ ice_arfs_is_perfect_flow_set(struct ice_hw *hw, __be16 l3_proto, u8 l4_proto)
* * false - fltr_info and fk refer to different flows.
*/
static bool
-ice_arfs_cmp(const struct ice_fdir_fltr *fltr_info, const struct flow_keys *fk,
- __be16 n_proto, u8 ip_proto)
+ice_arfs_cmp(const struct ice_ntuple_fltr *fltr_info,
+ const struct flow_keys *fk, __be16 n_proto, u8 ip_proto)
{
/* Determine if the filter is for IPv4 or IPv6 based on flow_type,
* which is one of ICE_FLTR_PTYPE_NONF_IPV{4,6}_{TCP,UDP}.
@@ -485,7 +485,7 @@ ice_rx_flow_steer(struct net_device *netdev, const struct sk_buff *skb,
spin_lock_bh(&vsi->arfs_lock);
hlist_for_each_entry(arfs_entry, &vsi->arfs_fltr_list[idx],
list_entry) {
- struct ice_fdir_fltr *fltr_info;
+ struct ice_ntuple_fltr *fltr_info;
/* keep searching for the already existing arfs_entry flow */
if (arfs_entry->flow_id != flow_id)
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
index ba4def92c3e8..1495d96b5c98 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -3106,9 +3106,9 @@ static int ice_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
switch (cmd->cmd) {
case ETHTOOL_SRXCLSRLINS:
- return ice_add_fdir_ethtool(vsi, cmd);
+ return ice_add_ntuple_ethtool(vsi, cmd);
case ETHTOOL_SRXCLSRLDEL:
- return ice_del_fdir_ethtool(vsi, cmd);
+ return ice_del_ntuple_ethtool(vsi, cmd);
default:
break;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool_fdir.c b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
similarity index 97%
rename from drivers/net/ethernet/intel/ice/ice_ethtool_fdir.c
rename to drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
index aceec184e89b..a6136e640418 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool_fdir.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool_ntuple.c
@@ -120,7 +120,7 @@ static bool ice_is_mask_valid(u64 mask, u64 field)
int ice_get_ethtool_fdir_entry(struct ice_hw *hw, struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp;
- struct ice_fdir_fltr *rule;
+ struct ice_ntuple_fltr *rule;
int ret = 0;
u16 idx;
@@ -240,7 +240,7 @@ int
ice_get_fdir_fltr_ids(struct ice_hw *hw, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
- struct ice_fdir_fltr *f_rule;
+ struct ice_ntuple_fltr *f_rule;
unsigned int cnt = 0;
int val = 0;
@@ -1487,10 +1487,10 @@ static void ice_update_per_q_fltr(struct ice_vsi *vsi, u32 q_index, bool inc)
* @add: true adds filter and false removed filter
* @is_tun: true adds inner filter on tunnel and false outer headers
*
- * returns 0 on success and negative value on error
+ * Return: 0 on success and negative value on error
*/
int
-ice_fdir_write_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input, bool add,
+ice_fdir_write_fltr(struct ice_pf *pf, struct ice_ntuple_fltr *input, bool add,
bool is_tun)
{
struct device *dev = ice_pf_to_dev(pf);
@@ -1557,10 +1557,10 @@ ice_fdir_write_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input, bool add,
* @input: filter structure
* @add: true adds filter and false removed filter
*
- * returns 0 on success and negative value on error
+ * Return: 0 on success and negative value on error
*/
static int
-ice_fdir_write_all_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input,
+ice_fdir_write_all_fltr(struct ice_pf *pf, struct ice_ntuple_fltr *input,
bool add)
{
u16 port_num;
@@ -1585,7 +1585,7 @@ ice_fdir_write_all_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input,
*/
void ice_fdir_replay_fltrs(struct ice_pf *pf)
{
- struct ice_fdir_fltr *f_rule;
+ struct ice_ntuple_fltr *f_rule;
struct ice_hw *hw = &pf->hw;
list_for_each_entry(f_rule, &hw->fdir_list_head, fltr_node) {
@@ -1630,7 +1630,7 @@ int ice_fdir_create_dflt_rules(struct ice_pf *pf)
*/
void ice_fdir_del_all_fltrs(struct ice_vsi *vsi)
{
- struct ice_fdir_fltr *f_rule, *tmp;
+ struct ice_ntuple_fltr *f_rule, *tmp;
struct ice_pf *pf = vsi->back;
struct ice_hw *hw = &pf->hw;
@@ -1701,18 +1701,18 @@ ice_fdir_do_rem_flow(struct ice_pf *pf, enum ice_fltr_ptype flow_type)
}
/**
- * ice_fdir_update_list_entry - add or delete a filter from the filter list
+ * ice_ntuple_update_list_entry - add or delete a filter from the filter list
* @pf: PF structure
* @input: filter structure
* @fltr_idx: ethtool index of filter to modify
*
- * returns 0 on success and negative on errors
+ * Return: 0 on success and negative on errors
*/
static int
-ice_fdir_update_list_entry(struct ice_pf *pf, struct ice_fdir_fltr *input,
- int fltr_idx)
+ice_ntuple_update_list_entry(struct ice_pf *pf, struct ice_ntuple_fltr *input,
+ int fltr_idx)
{
- struct ice_fdir_fltr *old_fltr;
+ struct ice_ntuple_fltr *old_fltr;
struct ice_hw *hw = &pf->hw;
struct ice_vsi *vsi;
int err = -ENOENT;
@@ -1751,13 +1751,13 @@ ice_fdir_update_list_entry(struct ice_pf *pf, struct ice_fdir_fltr *input,
}
/**
- * ice_del_fdir_ethtool - delete Flow Director filter
+ * ice_del_ntuple_ethtool - delete Flow Director or ACL filter
* @vsi: pointer to target VSI
- * @cmd: command to add or delete Flow Director filter
+ * @cmd: command to add or delete the filter
*
- * Returns 0 on success and negative values for failure
+ * Return: 0 on success and negative values for failure
*/
-int ice_del_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
+int ice_del_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
@@ -1778,7 +1778,7 @@ int ice_del_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
return -EBUSY;
mutex_lock(&hw->fdir_fltr_lock);
- val = ice_fdir_update_list_entry(pf, NULL, fsp->location);
+ val = ice_ntuple_update_list_entry(pf, NULL, fsp->location);
mutex_unlock(&hw->fdir_fltr_lock);
return val;
@@ -1818,14 +1818,16 @@ ice_update_ring_dest_vsi(struct ice_vsi *vsi, u16 *dest_vsi, u32 *ring)
}
/**
- * ice_set_fdir_input_set - Set the input set for Flow Director
+ * ice_ntuple_set_input_set - Set the input set for Flow Director
* @vsi: pointer to target VSI
* @fsp: pointer to ethtool Rx flow specification
* @input: filter structure
+ *
+ * Return: 0 on success, negative on failure
*/
static int
-ice_set_fdir_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
- struct ice_fdir_fltr *input)
+ice_ntuple_set_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
+ struct ice_ntuple_fltr *input)
{
s16 q_index = ICE_FDIR_NO_QUEUE_IDX;
u16 orig_q_index = 0;
@@ -1968,17 +1970,17 @@ ice_set_fdir_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
}
/**
- * ice_add_fdir_ethtool - Add/Remove Flow Director filter
+ * ice_add_ntuple_ethtool - Add/Remove Flow Director or ACL filter
* @vsi: pointer to target VSI
- * @cmd: command to add or delete Flow Director filter
+ * @cmd: command to add or delete the filter
*
- * Returns 0 on success and negative values for failure
+ * Return: 0 on success and negative values for failure
*/
-int ice_add_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
+int ice_add_ntuple_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
{
struct ice_rx_flow_userdef userdata;
struct ethtool_rx_flow_spec *fsp;
- struct ice_fdir_fltr *input;
+ struct ice_ntuple_fltr *input;
struct device *dev;
struct ice_pf *pf;
struct ice_hw *hw;
@@ -2034,7 +2036,7 @@ int ice_add_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
if (!input)
return -ENOMEM;
- ret = ice_set_fdir_input_set(vsi, fsp, input);
+ ret = ice_ntuple_set_input_set(vsi, fsp, input);
if (ret)
goto free_input;
@@ -2055,7 +2057,7 @@ int ice_add_fdir_ethtool(struct ice_vsi *vsi, struct ethtool_rxnfc *cmd)
input->comp_report = ICE_FXD_FLTR_QW0_COMP_REPORT_SW_FAIL;
/* input struct is added to the HW filter list */
- ret = ice_fdir_update_list_entry(pf, input, fsp->location);
+ ret = ice_ntuple_update_list_entry(pf, input, fsp->location);
if (ret)
goto release_lock;
diff --git a/drivers/net/ethernet/intel/ice/ice_fdir.c b/drivers/net/ethernet/intel/ice/ice_fdir.c
index b29fbdec9442..5b25f6414b58 100644
--- a/drivers/net/ethernet/intel/ice/ice_fdir.c
+++ b/drivers/net/ethernet/intel/ice/ice_fdir.c
@@ -648,7 +648,7 @@ ice_set_fd_desc_val(struct ice_fd_fltr_desc_ctx *ctx,
* @add: if add is true, this is an add operation, false implies delete
*/
void
-ice_fdir_get_prgm_desc(struct ice_hw *hw, struct ice_fdir_fltr *input,
+ice_fdir_get_prgm_desc(struct ice_hw *hw, struct ice_ntuple_fltr *input,
struct ice_fltr_desc *fdesc, bool add)
{
struct ice_fd_fltr_desc_ctx fdir_fltr_ctx = { 0 };
@@ -855,7 +855,7 @@ static void ice_pkt_insert_mac_addr(u8 *pkt, u8 *addr)
* @tun: true implies generate a tunnel packet
*/
int
-ice_fdir_get_gen_prgm_pkt(struct ice_hw *hw, struct ice_fdir_fltr *input,
+ice_fdir_get_gen_prgm_pkt(struct ice_hw *hw, struct ice_ntuple_fltr *input,
u8 *pkt, bool frag, bool tun)
{
enum ice_fltr_ptype flow;
@@ -1138,10 +1138,10 @@ bool ice_fdir_has_frag(enum ice_fltr_ptype flow)
*
* Returns pointer to filter if found or null
*/
-struct ice_fdir_fltr *
+struct ice_ntuple_fltr *
ice_fdir_find_fltr_by_idx(struct ice_hw *hw, u32 fltr_idx)
{
- struct ice_fdir_fltr *rule;
+ struct ice_ntuple_fltr *rule;
list_for_each_entry(rule, &hw->fdir_list_head, fltr_node) {
/* rule ID found in the list */
@@ -1158,9 +1158,9 @@ ice_fdir_find_fltr_by_idx(struct ice_hw *hw, u32 fltr_idx)
* @hw: hardware structure
* @fltr: filter node to add to structure
*/
-void ice_fdir_list_add_fltr(struct ice_hw *hw, struct ice_fdir_fltr *fltr)
+void ice_fdir_list_add_fltr(struct ice_hw *hw, struct ice_ntuple_fltr *fltr)
{
- struct ice_fdir_fltr *rule, *parent = NULL;
+ struct ice_ntuple_fltr *rule, *parent = NULL;
list_for_each_entry(rule, &hw->fdir_list_head, fltr_node) {
/* rule ID found or pass its spot in the list */
@@ -1215,7 +1215,7 @@ static int ice_cmp_ipv6_addr(__be32 *a, __be32 *b)
* Returns true if the filters match
*/
static bool
-ice_fdir_comp_rules(struct ice_fdir_fltr *a, struct ice_fdir_fltr *b)
+ice_fdir_comp_rules(struct ice_ntuple_fltr *a, struct ice_ntuple_fltr *b)
{
enum ice_fltr_ptype flow_type = a->flow_type;
@@ -1275,9 +1275,9 @@ ice_fdir_comp_rules(struct ice_fdir_fltr *a, struct ice_fdir_fltr *b)
*
* Returns true if the filter is found in the list
*/
-bool ice_fdir_is_dup_fltr(struct ice_hw *hw, struct ice_fdir_fltr *input)
+bool ice_fdir_is_dup_fltr(struct ice_hw *hw, struct ice_ntuple_fltr *input)
{
- struct ice_fdir_fltr *rule;
+ struct ice_ntuple_fltr *rule;
bool ret = false;
list_for_each_entry(rule, &hw->fdir_list_head, fltr_node) {
diff --git a/drivers/net/ethernet/intel/ice/virt/fdir.c b/drivers/net/ethernet/intel/ice/virt/fdir.c
index 4f1f3442e52c..eca9eda04f31 100644
--- a/drivers/net/ethernet/intel/ice/virt/fdir.c
+++ b/drivers/net/ethernet/intel/ice/virt/fdir.c
@@ -38,7 +38,7 @@ enum ice_fdir_tunnel_type {
};
struct virtchnl_fdir_fltr_conf {
- struct ice_fdir_fltr input;
+ struct ice_ntuple_fltr input;
enum ice_fdir_tunnel_type ttype;
u64 inset_flag;
u32 flow_id;
@@ -567,12 +567,12 @@ static bool
ice_vc_fdir_has_prof_conflict(struct ice_vf *vf,
struct virtchnl_fdir_fltr_conf *conf)
{
- struct ice_fdir_fltr *desc;
+ struct ice_ntuple_fltr *desc;
list_for_each_entry(desc, &vf->fdir.fdir_rule_list, fltr_node) {
struct virtchnl_fdir_fltr_conf *existing_conf;
enum ice_fltr_ptype flow_type_a, flow_type_b;
- struct ice_fdir_fltr *a, *b;
+ struct ice_ntuple_fltr *a, *b;
existing_conf = to_fltr_conf_from_desc(desc);
a = &existing_conf->input;
@@ -748,7 +748,7 @@ static int
ice_vc_fdir_config_input_set(struct ice_vf *vf, struct virtchnl_fdir_add *fltr,
struct virtchnl_fdir_fltr_conf *conf, int tun)
{
- struct ice_fdir_fltr *input = &conf->input;
+ struct ice_ntuple_fltr *input = &conf->input;
struct device *dev = ice_pf_to_dev(vf->pf);
struct ice_flow_seg_info *seg;
enum ice_fltr_ptype flow;
@@ -924,8 +924,8 @@ ice_vc_fdir_parse_pattern(struct ice_vf *vf, struct virtchnl_fdir_add *fltr,
struct virtchnl_proto_hdrs *proto = &fltr->rule_cfg.proto_hdrs;
enum virtchnl_proto_hdr_type l3 = VIRTCHNL_PROTO_HDR_NONE;
enum virtchnl_proto_hdr_type l4 = VIRTCHNL_PROTO_HDR_NONE;
+ struct ice_ntuple_fltr *input = &conf->input;
struct device *dev = ice_pf_to_dev(vf->pf);
- struct ice_fdir_fltr *input = &conf->input;
int i;
if (proto->count > VIRTCHNL_MAX_NUM_PROTO_HDRS) {
@@ -1150,8 +1150,8 @@ ice_vc_fdir_parse_action(struct ice_vf *vf, struct virtchnl_fdir_add *fltr,
struct virtchnl_fdir_fltr_conf *conf)
{
struct virtchnl_filter_action_set *as = &fltr->rule_cfg.action_set;
+ struct ice_ntuple_fltr *input = &conf->input;
struct device *dev = ice_pf_to_dev(vf->pf);
- struct ice_fdir_fltr *input = &conf->input;
u32 dest_num = 0;
u32 mark_num = 0;
int i;
@@ -1249,8 +1249,8 @@ static bool
ice_vc_fdir_comp_rules(struct virtchnl_fdir_fltr_conf *conf_a,
struct virtchnl_fdir_fltr_conf *conf_b)
{
- struct ice_fdir_fltr *a = &conf_a->input;
- struct ice_fdir_fltr *b = &conf_b->input;
+ struct ice_ntuple_fltr *a = &conf_a->input;
+ struct ice_ntuple_fltr *b = &conf_b->input;
if (conf_a->ttype != conf_b->ttype)
return false;
@@ -1288,7 +1288,7 @@ ice_vc_fdir_comp_rules(struct virtchnl_fdir_fltr_conf *conf_a,
static bool
ice_vc_fdir_is_dup_fltr(struct ice_vf *vf, struct virtchnl_fdir_fltr_conf *conf)
{
- struct ice_fdir_fltr *desc;
+ struct ice_ntuple_fltr *desc;
bool ret;
list_for_each_entry(desc, &vf->fdir.fdir_rule_list, fltr_node) {
@@ -1317,7 +1317,7 @@ static int
ice_vc_fdir_insert_entry(struct ice_vf *vf,
struct virtchnl_fdir_fltr_conf *conf, u32 *id)
{
- struct ice_fdir_fltr *input = &conf->input;
+ struct ice_ntuple_fltr *input = &conf->input;
int i;
/* alloc ID corresponding with conf */
@@ -1341,7 +1341,7 @@ static void
ice_vc_fdir_remove_entry(struct ice_vf *vf,
struct virtchnl_fdir_fltr_conf *conf, u32 id)
{
- struct ice_fdir_fltr *input = &conf->input;
+ struct ice_ntuple_fltr *input = &conf->input;
idr_remove(&vf->fdir.fdir_rule_idr, id);
list_del(&input->fltr_node);
@@ -1367,7 +1367,7 @@ ice_vc_fdir_lookup_entry(struct ice_vf *vf, u32 id)
static void ice_vc_fdir_flush_entry(struct ice_vf *vf)
{
struct virtchnl_fdir_fltr_conf *conf;
- struct ice_fdir_fltr *desc, *temp;
+ struct ice_ntuple_fltr *desc, *temp;
list_for_each_entry_safe(desc, temp,
&vf->fdir.fdir_rule_list, fltr_node) {
@@ -1390,7 +1390,7 @@ static int ice_vc_fdir_write_fltr(struct ice_vf *vf,
struct virtchnl_fdir_fltr_conf *conf,
bool add, bool is_tun)
{
- struct ice_fdir_fltr *input = &conf->input;
+ struct ice_ntuple_fltr *input = &conf->input;
struct ice_vsi *vsi, *ctrl_vsi;
struct ice_fltr_desc desc;
struct device *dev;
@@ -2315,7 +2315,7 @@ int ice_vc_del_fdir_fltr(struct ice_vf *vf, u8 *msg)
struct virtchnl_fdir_fltr_conf *conf;
struct ice_vf_fdir *fdir = &vf->fdir;
enum virtchnl_status_code v_ret;
- struct ice_fdir_fltr *input;
+ struct ice_ntuple_fltr *input;
enum ice_fltr_ptype flow;
struct device *dev;
struct ice_pf *pf;
--
2.49.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox