* [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 16:38 ` [External] : " ALOK TIWARI
` (2 more replies)
2025-10-13 15:27 ` [PATCH net-next v4 02/12] virtio: Add config_op for admin commands Daniel Jurgens
` (10 subsequent siblings)
11 siblings, 3 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
The cap ID list can be more than 64 bits. Remove the build assert. Also
remove caching of the supported caps, it wasn't used.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
---
v4: New patch for V4
---
drivers/virtio/virtio_pci_common.h | 1 -
drivers/virtio/virtio_pci_modern.c | 7 +------
2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h
index 8cd01de27baf..fc26e035e7a6 100644
--- a/drivers/virtio/virtio_pci_common.h
+++ b/drivers/virtio/virtio_pci_common.h
@@ -48,7 +48,6 @@ struct virtio_pci_admin_vq {
/* Protects virtqueue access. */
spinlock_t lock;
u64 supported_cmds;
- u64 supported_caps;
u8 max_dev_parts_objects;
struct ida dev_parts_ida;
/* Name of the admin queue: avq.$vq_index. */
diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
index dd0e65f71d41..810f9f636b5e 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -323,12 +323,7 @@ static void virtio_pci_admin_cmd_cap_init(struct virtio_device *virtio_dev)
if (ret)
goto end;
- /* Max number of caps fits into a single u64 */
- BUILD_BUG_ON(sizeof(data->supported_caps) > sizeof(u64));
-
- vp_dev->admin_vq.supported_caps = le64_to_cpu(data->supported_caps[0]);
-
- if (!(vp_dev->admin_vq.supported_caps & (1 << VIRTIO_DEV_PARTS_CAP)))
+ if (!(le64_to_cpu(data->support_caps[0]) & (1 << VIRTIO_DEV_PARTS_CAP)))
goto end;
virtio_pci_admin_cmd_dev_parts_objects_enable(virtio_dev);
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [External] : [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert
2025-10-13 15:27 ` [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert Daniel Jurgens
@ 2025-10-13 16:38 ` ALOK TIWARI
2025-10-13 17:57 ` Dan Jurgens
2025-10-14 5:28 ` kernel test robot
2025-10-14 7:04 ` kernel test robot
2 siblings, 1 reply; 19+ messages in thread
From: ALOK TIWARI @ 2025-10-13 16:38 UTC (permalink / raw)
To: Daniel Jurgens, netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet
On 10/13/2025 8:57 PM, Daniel Jurgens wrote:
> The cap ID list can be more than 64 bits. Remove the build assert. Also
> remove caching of the supported caps, it wasn't used.
>
> Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
> Reviewed-by: Parav Pandit <parav@nvidia.com>
>
> ---
> v4: New patch for V4
> ---
> drivers/virtio/virtio_pci_common.h | 1 -
> drivers/virtio/virtio_pci_modern.c | 7 +------
> 2 files changed, 1 insertion(+), 7 deletions(-)
>
> diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h
> index 8cd01de27baf..fc26e035e7a6 100644
> --- a/drivers/virtio/virtio_pci_common.h
> +++ b/drivers/virtio/virtio_pci_common.h
> @@ -48,7 +48,6 @@ struct virtio_pci_admin_vq {
> /* Protects virtqueue access. */
> spinlock_t lock;
> u64 supported_cmds;
> - u64 supported_caps;
> u8 max_dev_parts_objects;
> struct ida dev_parts_ida;
> /* Name of the admin queue: avq.$vq_index. */
> diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
> index dd0e65f71d41..810f9f636b5e 100644
> --- a/drivers/virtio/virtio_pci_modern.c
> +++ b/drivers/virtio/virtio_pci_modern.c
> @@ -323,12 +323,7 @@ static void virtio_pci_admin_cmd_cap_init(struct virtio_device *virtio_dev)
> if (ret)
> goto end;
>
> - /* Max number of caps fits into a single u64 */
> - BUILD_BUG_ON(sizeof(data->supported_caps) > sizeof(u64));
> -
> - vp_dev->admin_vq.supported_caps = le64_to_cpu(data->supported_caps[0]);
> -
> - if (!(vp_dev->admin_vq.supported_caps & (1 << VIRTIO_DEV_PARTS_CAP)))
> + if (!(le64_to_cpu(data->support_caps[0]) & (1 << VIRTIO_DEV_PARTS_CAP)))
typo support_caps -> supported_caps ?
> goto end;
>
> virtio_pci_admin_cmd_dev_parts_objects_enable(virtio_dev);
Thanks,
Alok
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [External] : [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert
2025-10-13 16:38 ` [External] : " ALOK TIWARI
@ 2025-10-13 17:57 ` Dan Jurgens
0 siblings, 0 replies; 19+ messages in thread
From: Dan Jurgens @ 2025-10-13 17:57 UTC (permalink / raw)
To: ALOK TIWARI, netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet
On 10/13/25 11:38 AM, ALOK TIWARI wrote:
>
>
> On 10/13/2025 8:57 PM, Daniel Jurgens wrote:
>> The cap ID list can be more than 64 bits. Remove the build assert. Also
>> remove caching of the supported caps, it wasn't used.
>> - if (!(vp_dev->admin_vq.supported_caps & (1 <<
>> VIRTIO_DEV_PARTS_CAP)))
>> + if (!(le64_to_cpu(data->support_caps[0]) & (1 <<
>> VIRTIO_DEV_PARTS_CAP)))
>
> typo support_caps -> supported_caps ?
>
Thanks Alok, I dropped a patch before submitting that fixed this
problem, not that it could have been left this way anyway.
I've added this, and the comments on 0004 for v5.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert
2025-10-13 15:27 ` [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert Daniel Jurgens
2025-10-13 16:38 ` [External] : " ALOK TIWARI
@ 2025-10-14 5:28 ` kernel test robot
2025-10-14 7:04 ` kernel test robot
2 siblings, 0 replies; 19+ messages in thread
From: kernel test robot @ 2025-10-14 5:28 UTC (permalink / raw)
To: Daniel Jurgens, netdev, mst, jasowang, alex.williamson, pabeni
Cc: oe-kbuild-all, virtualization, parav, shshitrit, yohadt, xuanzhuo,
eperezma, shameerali.kolothum.thodi, jgg, kevin.tian, kuba,
andrew+netdev, edumazet, Daniel Jurgens
Hi Daniel,
kernel test robot noticed the following build warnings:
[auto build test WARNING on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Daniel-Jurgens/virtio_pci-Remove-supported_cap-size-build-assert/20251014-004146
base: net-next/main
patch link: https://lore.kernel.org/r/20251013152742.619423-2-danielj%40nvidia.com
patch subject: [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert
config: x86_64-defconfig (https://download.01.org/0day-ci/archive/20251014/202510141339.ne1O8cPc-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251014/202510141339.ne1O8cPc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510141339.ne1O8cPc-lkp@intel.com/
All warnings (new ones prefixed by >>):
In file included from include/linux/byteorder/little_endian.h:5,
from arch/x86/include/uapi/asm/byteorder.h:5,
from include/asm-generic/qrwlock_types.h:6,
from arch/x86/include/asm/spinlock_types.h:7,
from include/linux/spinlock_types_raw.h:7,
from include/linux/ratelimit_types.h:7,
from include/linux/printk.h:9,
from include/asm-generic/bug.h:22,
from arch/x86/include/asm/bug.h:103,
from arch/x86/include/asm/alternative.h:9,
from arch/x86/include/asm/segment.h:6,
from arch/x86/include/asm/ptrace.h:5,
from arch/x86/include/asm/math_emu.h:5,
from arch/x86/include/asm/processor.h:13,
from include/linux/sched.h:13,
from include/linux/delay.h:13,
from drivers/virtio/virtio_pci_modern.c:17:
drivers/virtio/virtio_pci_modern.c: In function 'virtio_pci_admin_cmd_cap_init':
drivers/virtio/virtio_pci_modern.c:326:33: error: 'struct virtio_admin_cmd_query_cap_id_result' has no member named 'support_caps'; did you mean 'supported_caps'?
326 | if (!(le64_to_cpu(data->support_caps[0]) & (1 << VIRTIO_DEV_PARTS_CAP)))
| ^~~~~~~~~~~~
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition of macro '__le64_to_cpu'
33 | #define __le64_to_cpu(x) ((__force __u64)(__le64)(x))
| ^
drivers/virtio/virtio_pci_modern.c:326:15: note: in expansion of macro 'le64_to_cpu'
326 | if (!(le64_to_cpu(data->support_caps[0]) & (1 << VIRTIO_DEV_PARTS_CAP)))
| ^~~~~~~~~~~
>> drivers/virtio/virtio_pci_modern.c:307:35: warning: unused variable 'vp_dev' [-Wunused-variable]
307 | struct virtio_pci_device *vp_dev = to_vp_device(virtio_dev);
| ^~~~~~
vim +/vp_dev +307 drivers/virtio/virtio_pci_modern.c
bfcad518605d92 Yishai Hadas 2024-11-13 304
bfcad518605d92 Yishai Hadas 2024-11-13 305 static void virtio_pci_admin_cmd_cap_init(struct virtio_device *virtio_dev)
bfcad518605d92 Yishai Hadas 2024-11-13 306 {
bfcad518605d92 Yishai Hadas 2024-11-13 @307 struct virtio_pci_device *vp_dev = to_vp_device(virtio_dev);
bfcad518605d92 Yishai Hadas 2024-11-13 308 struct virtio_admin_cmd_query_cap_id_result *data;
bfcad518605d92 Yishai Hadas 2024-11-13 309 struct virtio_admin_cmd cmd = {};
bfcad518605d92 Yishai Hadas 2024-11-13 310 struct scatterlist result_sg;
bfcad518605d92 Yishai Hadas 2024-11-13 311 int ret;
bfcad518605d92 Yishai Hadas 2024-11-13 312
bfcad518605d92 Yishai Hadas 2024-11-13 313 data = kzalloc(sizeof(*data), GFP_KERNEL);
bfcad518605d92 Yishai Hadas 2024-11-13 314 if (!data)
bfcad518605d92 Yishai Hadas 2024-11-13 315 return;
bfcad518605d92 Yishai Hadas 2024-11-13 316
bfcad518605d92 Yishai Hadas 2024-11-13 317 sg_init_one(&result_sg, data, sizeof(*data));
bfcad518605d92 Yishai Hadas 2024-11-13 318 cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY);
16c22c56d42825 Daniel Jurgens 2025-03-04 319 cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SELF);
bfcad518605d92 Yishai Hadas 2024-11-13 320 cmd.result_sg = &result_sg;
bfcad518605d92 Yishai Hadas 2024-11-13 321
bfcad518605d92 Yishai Hadas 2024-11-13 322 ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
bfcad518605d92 Yishai Hadas 2024-11-13 323 if (ret)
bfcad518605d92 Yishai Hadas 2024-11-13 324 goto end;
bfcad518605d92 Yishai Hadas 2024-11-13 325
c1e3216169ec0d Daniel Jurgens 2025-10-13 @326 if (!(le64_to_cpu(data->support_caps[0]) & (1 << VIRTIO_DEV_PARTS_CAP)))
bfcad518605d92 Yishai Hadas 2024-11-13 327 goto end;
bfcad518605d92 Yishai Hadas 2024-11-13 328
bfcad518605d92 Yishai Hadas 2024-11-13 329 virtio_pci_admin_cmd_dev_parts_objects_enable(virtio_dev);
bfcad518605d92 Yishai Hadas 2024-11-13 330 end:
bfcad518605d92 Yishai Hadas 2024-11-13 331 kfree(data);
bfcad518605d92 Yishai Hadas 2024-11-13 332 }
bfcad518605d92 Yishai Hadas 2024-11-13 333
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert
2025-10-13 15:27 ` [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert Daniel Jurgens
2025-10-13 16:38 ` [External] : " ALOK TIWARI
2025-10-14 5:28 ` kernel test robot
@ 2025-10-14 7:04 ` kernel test robot
2 siblings, 0 replies; 19+ messages in thread
From: kernel test robot @ 2025-10-14 7:04 UTC (permalink / raw)
To: Daniel Jurgens, netdev, mst, jasowang, alex.williamson, pabeni
Cc: llvm, oe-kbuild-all, virtualization, parav, shshitrit, yohadt,
xuanzhuo, eperezma, shameerali.kolothum.thodi, jgg, kevin.tian,
kuba, andrew+netdev, edumazet, Daniel Jurgens
Hi Daniel,
kernel test robot noticed the following build errors:
[auto build test ERROR on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Daniel-Jurgens/virtio_pci-Remove-supported_cap-size-build-assert/20251014-004146
base: net-next/main
patch link: https://lore.kernel.org/r/20251013152742.619423-2-danielj%40nvidia.com
patch subject: [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert
config: s390-randconfig-001-20251014 (https://download.01.org/0day-ci/archive/20251014/202510141404.P5SMiruH-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 39f292ffa13d7ca0d1edff27ac8fd55024bb4d19)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251014/202510141404.P5SMiruH-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510141404.P5SMiruH-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/virtio/virtio_pci_modern.c:326:26: error: no member named 'support_caps' in 'struct virtio_admin_cmd_query_cap_id_result'
326 | if (!(le64_to_cpu(data->support_caps[0]) & (1 << VIRTIO_DEV_PARTS_CAP)))
| ~~~~ ^
include/linux/byteorder/generic.h:87:21: note: expanded from macro 'le64_to_cpu'
87 | #define le64_to_cpu __le64_to_cpu
| ^
1 error generated.
vim +326 drivers/virtio/virtio_pci_modern.c
304
305 static void virtio_pci_admin_cmd_cap_init(struct virtio_device *virtio_dev)
306 {
307 struct virtio_pci_device *vp_dev = to_vp_device(virtio_dev);
308 struct virtio_admin_cmd_query_cap_id_result *data;
309 struct virtio_admin_cmd cmd = {};
310 struct scatterlist result_sg;
311 int ret;
312
313 data = kzalloc(sizeof(*data), GFP_KERNEL);
314 if (!data)
315 return;
316
317 sg_init_one(&result_sg, data, sizeof(*data));
318 cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY);
319 cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SELF);
320 cmd.result_sg = &result_sg;
321
322 ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
323 if (ret)
324 goto end;
325
> 326 if (!(le64_to_cpu(data->support_caps[0]) & (1 << VIRTIO_DEV_PARTS_CAP)))
327 goto end;
328
329 virtio_pci_admin_cmd_dev_parts_objects_enable(virtio_dev);
330 end:
331 kfree(data);
332 }
333
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH net-next v4 02/12] virtio: Add config_op for admin commands
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 03/12] virtio: Expose generic device capability operations Daniel Jurgens
` (9 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
This will allow device drivers to issue administration commands.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
---
v4: New patch for v4
---
drivers/virtio/virtio_pci_modern.c | 2 ++
include/linux/virtio_config.h | 6 ++++++
2 files changed, 8 insertions(+)
diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
index 810f9f636b5e..6d557f9b0187 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -1237,6 +1237,7 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = {
.get_shm_region = vp_get_shm_region,
.disable_vq_and_reset = vp_modern_disable_vq_and_reset,
.enable_vq_after_reset = vp_modern_enable_vq_after_reset,
+ .admin_cmd_exec = vp_modern_admin_cmd_exec,
};
static const struct virtio_config_ops virtio_pci_config_ops = {
@@ -1257,6 +1258,7 @@ static const struct virtio_config_ops virtio_pci_config_ops = {
.get_shm_region = vp_get_shm_region,
.disable_vq_and_reset = vp_modern_disable_vq_and_reset,
.enable_vq_after_reset = vp_modern_enable_vq_after_reset,
+ .admin_cmd_exec = vp_modern_admin_cmd_exec,
};
/* the PCI probing function */
diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h
index 16001e9f9b39..19606609254e 100644
--- a/include/linux/virtio_config.h
+++ b/include/linux/virtio_config.h
@@ -108,6 +108,10 @@ struct virtqueue_info {
* Returns 0 on success or error status
* If disable_vq_and_reset is set, then enable_vq_after_reset must also be
* set.
+ * @admin_cmd_exec: Execute an admin VQ command.
+ * vdev: the virtio_device
+ * cmd: the command to execute
+ * Returns 0 on success or error status
*/
struct virtio_config_ops {
void (*get)(struct virtio_device *vdev, unsigned offset,
@@ -137,6 +141,8 @@ struct virtio_config_ops {
struct virtio_shm_region *region, u8 id);
int (*disable_vq_and_reset)(struct virtqueue *vq);
int (*enable_vq_after_reset)(struct virtqueue *vq);
+ int (*admin_cmd_exec)(struct virtio_device *vdev,
+ struct virtio_admin_cmd *cmd);
};
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH net-next v4 03/12] virtio: Expose generic device capability operations
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 01/12] virtio_pci: Remove supported_cap size build assert Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 02/12] virtio: Add config_op for admin commands Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 04/12] virtio: Expose object create and destroy API Daniel Jurgens
` (8 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
Currently querying and setting capabilities is restricted to a single
capability and contained within the virtio PCI driver. However, each
device type has generic and device specific capabilities, that may be
queried and set. In subsequent patches virtio_net will query and set
flow filter capabilities.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
---
v4: Moved this logic from virtio_pci_modern to new file
virtio_admin_commands.
---
drivers/virtio/Makefile | 2 +-
drivers/virtio/virtio_admin_commands.c | 90 ++++++++++++++++++++++++++
include/linux/virtio_admin.h | 80 +++++++++++++++++++++++
include/uapi/linux/virtio_pci.h | 7 +-
4 files changed, 176 insertions(+), 3 deletions(-)
create mode 100644 drivers/virtio/virtio_admin_commands.c
create mode 100644 include/linux/virtio_admin.h
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index eefcfe90d6b8..2b4a204dde33 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_VIRTIO) += virtio.o virtio_ring.o
+obj-$(CONFIG_VIRTIO) += virtio.o virtio_ring.o virtio_admin_commands.o
obj-$(CONFIG_VIRTIO_ANCHOR) += virtio_anchor.o
obj-$(CONFIG_VIRTIO_PCI_LIB) += virtio_pci_modern_dev.o
obj-$(CONFIG_VIRTIO_PCI_LIB_LEGACY) += virtio_pci_legacy_dev.o
diff --git a/drivers/virtio/virtio_admin_commands.c b/drivers/virtio/virtio_admin_commands.c
new file mode 100644
index 000000000000..94751d16b3c4
--- /dev/null
+++ b/drivers/virtio/virtio_admin_commands.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_admin.h>
+
+int virtio_admin_cap_id_list_query(struct virtio_device *vdev,
+ struct virtio_admin_cmd_query_cap_id_result *data)
+{
+ struct virtio_admin_cmd cmd = {};
+ struct scatterlist result_sg;
+
+ if (!vdev->config->admin_cmd_exec)
+ return -EOPNOTSUPP;
+
+ sg_init_one(&result_sg, data, sizeof(*data));
+ cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY);
+ cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SELF);
+ cmd.result_sg = &result_sg;
+
+ return vdev->config->admin_cmd_exec(vdev, &cmd);
+}
+EXPORT_SYMBOL_GPL(virtio_admin_cap_id_list_query);
+
+int virtio_admin_cap_get(struct virtio_device *vdev,
+ u16 id,
+ void *caps,
+ size_t cap_size)
+{
+ struct virtio_admin_cmd_cap_get_data *data;
+ struct virtio_admin_cmd cmd = {};
+ struct scatterlist result_sg;
+ struct scatterlist data_sg;
+ int err;
+
+ if (!vdev->config->admin_cmd_exec)
+ return -EOPNOTSUPP;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->id = cpu_to_le16(id);
+ sg_init_one(&data_sg, data, sizeof(*data));
+ sg_init_one(&result_sg, caps, cap_size);
+ cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEVICE_CAP_GET);
+ cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SELF);
+ cmd.data_sg = &data_sg;
+ cmd.result_sg = &result_sg;
+
+ err = vdev->config->admin_cmd_exec(vdev, &cmd);
+ kfree(data);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(virtio_admin_cap_get);
+
+int virtio_admin_cap_set(struct virtio_device *vdev,
+ u16 id,
+ const void *caps,
+ size_t cap_size)
+{
+ struct virtio_admin_cmd_cap_set_data *data;
+ struct virtio_admin_cmd cmd = {};
+ struct scatterlist data_sg;
+ size_t data_size;
+ int err;
+
+ if (!vdev->config->admin_cmd_exec)
+ return -EOPNOTSUPP;
+
+ data_size = sizeof(*data) + cap_size;
+ data = kzalloc(data_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->id = cpu_to_le16(id);
+ memcpy(data->cap_specific_data, caps, cap_size);
+ sg_init_one(&data_sg, data, data_size);
+ cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DRIVER_CAP_SET);
+ cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SELF);
+ cmd.data_sg = &data_sg;
+ cmd.result_sg = NULL;
+
+ err = vdev->config->admin_cmd_exec(vdev, &cmd);
+ kfree(data);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(virtio_admin_cap_set);
diff --git a/include/linux/virtio_admin.h b/include/linux/virtio_admin.h
new file mode 100644
index 000000000000..36df97b6487a
--- /dev/null
+++ b/include/linux/virtio_admin.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Header file for virtio admin operations
+ */
+#include <uapi/linux/virtio_pci.h>
+
+#ifndef _LINUX_VIRTIO_ADMIN_H
+#define _LINUX_VIRTIO_ADMIN_H
+
+struct virtio_device;
+
+/**
+ * VIRTIO_CAP_IN_LIST - Check if a capability is supported in the capability list
+ * @cap_list: Pointer to capability list structure containing supported_caps array
+ * @cap: Capability ID to check
+ *
+ * The cap_list contains a supported_caps array of little-endian 64-bit integers
+ * where each bit represents a capability. Bit 0 of the first element represents
+ * capability ID 0, bit 1 represents capability ID 1, and so on.
+ *
+ * Return: 1 if capability is supported, 0 otherwise
+ */
+#define VIRTIO_CAP_IN_LIST(cap_list, cap) \
+ (!!(1 & (le64_to_cpu(cap_list->supported_caps[cap / 64]) >> cap % 64)))
+
+/**
+ * virtio_admin_cap_id_list_query - Query the list of available capability IDs
+ * @vdev: The virtio device to query
+ * @data: Pointer to result structure (must be heap allocated)
+ *
+ * This function queries the virtio device for the list of available capability
+ * IDs that can be used with virtio_admin_cap_get() and virtio_admin_cap_set().
+ * The result is stored in the provided data structure.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if the device doesn't support admin
+ * operations or capability queries, or a negative error code on other failures.
+ */
+int virtio_admin_cap_id_list_query(struct virtio_device *vdev,
+ struct virtio_admin_cmd_query_cap_id_result *data);
+
+/**
+ * virtio_admin_cap_get - Get capability data for a specific capability ID
+ * @vdev: The virtio device
+ * @id: Capability ID to retrieve
+ * @caps: Pointer to capability data structure (must be heap allocated)
+ * @cap_size: Size of the capability data structure
+ *
+ * This function retrieves a specific capability from the virtio device.
+ * The capability data is stored in the provided buffer. The caller must
+ * ensure the buffer is large enough to hold the capability data.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if the device doesn't support admin
+ * operations or capability retrieval, or a negative error code on other failures.
+ */
+int virtio_admin_cap_get(struct virtio_device *vdev,
+ u16 id,
+ void *caps,
+ size_t cap_size);
+
+/**
+ * virtio_admin_cap_set - Set capability data for a specific capability ID
+ * @vdev: The virtio device
+ * @id: Capability ID to set
+ * @caps: Pointer to capability data structure (must be heap allocated)
+ * @cap_size: Size of the capability data structure
+ *
+ * This function sets a specific capability on the virtio device.
+ * The capability data is read from the provided buffer and applied
+ * to the device. The device may validate the capability data before
+ * applying it.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if the device doesn't support admin
+ * operations or capability setting, or a negative error code on other failures.
+ */
+int virtio_admin_cap_set(struct virtio_device *vdev,
+ u16 id,
+ const void *caps,
+ size_t cap_size);
+
+#endif /* _LINUX_VIRTIO_ADMIN_H */
diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h
index c691ac210ce2..0d5ca0cff629 100644
--- a/include/uapi/linux/virtio_pci.h
+++ b/include/uapi/linux/virtio_pci.h
@@ -315,15 +315,18 @@ struct virtio_admin_cmd_notify_info_result {
#define VIRTIO_DEV_PARTS_CAP 0x0000
+/* Update this value to largest implemented cap number. */
+#define VIRTIO_ADMIN_MAX_CAP 0x0fff
+
struct virtio_dev_parts_cap {
__u8 get_parts_resource_objects_limit;
__u8 set_parts_resource_objects_limit;
};
-#define MAX_CAP_ID __KERNEL_DIV_ROUND_UP(VIRTIO_DEV_PARTS_CAP + 1, 64)
+#define VIRTIO_ADMIN_CAP_ID_ARRAY_SIZE __KERNEL_DIV_ROUND_UP(VIRTIO_ADMIN_MAX_CAP, 64)
struct virtio_admin_cmd_query_cap_id_result {
- __le64 supported_caps[MAX_CAP_ID];
+ __le64 supported_caps[VIRTIO_ADMIN_CAP_ID_ARRAY_SIZE];
};
struct virtio_admin_cmd_cap_get_data {
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH net-next v4 04/12] virtio: Expose object create and destroy API
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
` (2 preceding siblings ...)
2025-10-13 15:27 ` [PATCH net-next v4 03/12] virtio: Expose generic device capability operations Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 16:25 ` [External] : " ALOK TIWARI
2025-10-13 15:27 ` [PATCH net-next v4 05/12] virtio_net: Query and set flow filter caps Daniel Jurgens
` (7 subsequent siblings)
11 siblings, 1 reply; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
Object create and destroy were implemented specifically for dev parts
device objects. Create general purpose APIs for use by upper layer
drivers.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
---
v4: Moved this logic from virtio_pci_modern to new file
virtio_admin_commands.
---
drivers/virtio/virtio_admin_commands.c | 76 ++++++++++++++++++++++++++
include/linux/virtio_admin.h | 40 ++++++++++++++
2 files changed, 116 insertions(+)
diff --git a/drivers/virtio/virtio_admin_commands.c b/drivers/virtio/virtio_admin_commands.c
index 94751d16b3c4..d3abb73354e8 100644
--- a/drivers/virtio/virtio_admin_commands.c
+++ b/drivers/virtio/virtio_admin_commands.c
@@ -88,3 +88,79 @@ int virtio_admin_cap_set(struct virtio_device *vdev,
return err;
}
EXPORT_SYMBOL_GPL(virtio_admin_cap_set);
+
+int virtio_admin_obj_create(struct virtio_device *vdev,
+ u16 obj_type,
+ u32 obj_id,
+ u16 group_type,
+ u64 group_member_id,
+ const void *obj_specific_data,
+ size_t obj_specific_data_size)
+{
+ size_t data_size = sizeof(struct virtio_admin_cmd_resource_obj_create_data);
+ struct virtio_admin_cmd_resource_obj_create_data *obj_create_data;
+ struct virtio_admin_cmd cmd = {};
+ struct scatterlist data_sg;
+ void *data;
+ int err;
+
+ if (!vdev->config->admin_cmd_exec)
+ return -EOPNOTSUPP;
+
+ data_size += (obj_specific_data_size);
+ data = kzalloc(data_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ obj_create_data = data;
+ obj_create_data->hdr.type = cpu_to_le16(obj_type);
+ obj_create_data->hdr.id = cpu_to_le32(obj_id);
+ memcpy(obj_create_data->resource_obj_specific_data, obj_specific_data,
+ obj_specific_data_size);
+ sg_init_one(&data_sg, data, data_size);
+
+ cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_CREATE);
+ cmd.group_type = cpu_to_le16(group_type);
+ cmd.group_member_id = cpu_to_le64(group_member_id);
+ cmd.data_sg = &data_sg;
+
+ err = vdev->config->admin_cmd_exec(vdev, &cmd);
+ kfree(data);
+
+ return err;
+
+}
+EXPORT_SYMBOL_GPL(virtio_admin_obj_create);
+
+int virtio_admin_obj_destroy(struct virtio_device *vdev,
+ u16 obj_type,
+ u32 obj_id,
+ u16 group_type,
+ u64 group_member_id)
+{
+ struct virtio_admin_cmd_resource_obj_cmd_hdr *data;
+ struct virtio_admin_cmd cmd = {};
+ struct scatterlist data_sg;
+ int err;
+
+ if (!vdev->config->admin_cmd_exec)
+ return -EOPNOTSUPP;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->type = cpu_to_le16(obj_type);
+ data->id = cpu_to_le32(obj_id);
+ sg_init_one(&data_sg, data, sizeof(*data));
+ cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_DESTROY);
+ cmd.group_type = cpu_to_le16(group_type);
+ cmd.group_member_id = cpu_to_le64(group_member_id);
+ cmd.data_sg = &data_sg;
+
+ err = vdev->config->admin_cmd_exec(vdev, &cmd);
+ kfree(data);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(virtio_admin_obj_destroy);
diff --git a/include/linux/virtio_admin.h b/include/linux/virtio_admin.h
index 36df97b6487a..99efe9e9dc17 100644
--- a/include/linux/virtio_admin.h
+++ b/include/linux/virtio_admin.h
@@ -77,4 +77,44 @@ int virtio_admin_cap_set(struct virtio_device *vdev,
const void *caps,
size_t cap_size);
+/**
+ * virtio_admin_obj_create - Create an object on a virtio device
+ * @vdev: the virtio device
+ * @obj_type: type of object to create
+ * @obj_id: ID for the new object
+ * @obj_specific_data: object-specific data for creation
+ * @obj_specific_data_size: size of the object-specific data in bytes
+ *
+ * Creates a new object on the virtio device with the specified type and ID.
+ * The object may require object-specific data for proper initialization.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if the device doesn't support admin
+ * operations or object creation, or a negative error code on other failures.
+ */
+int virtio_admin_obj_create(struct virtio_device *vdev,
+ u16 obj_type,
+ u32 obj_id,
+ u16 group_type,
+ u64 group_member_id,
+ const void *obj_specific_data,
+ size_t obj_specific_data_size);
+
+/**
+ * virtio_admin_obj_destroy - Destroy an object on a virtio device
+ * @vdev: the virtio device
+ * @obj_type: type of object to destroy
+ * @obj_id: ID of the object to destroy
+ *
+ * Destroys an existing object on the virtio device with the specified type
+ * and ID.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if the device doesn't support admin
+ * operations or object destruction, or a negative error code on other failures.
+ */
+int virtio_admin_obj_destroy(struct virtio_device *virtio_dev,
+ u16 obj_type,
+ u32 obj_id,
+ u16 group_type,
+ u64 group_member_id);
+
#endif /* _LINUX_VIRTIO_ADMIN_H */
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [External] : [PATCH net-next v4 04/12] virtio: Expose object create and destroy API
2025-10-13 15:27 ` [PATCH net-next v4 04/12] virtio: Expose object create and destroy API Daniel Jurgens
@ 2025-10-13 16:25 ` ALOK TIWARI
0 siblings, 0 replies; 19+ messages in thread
From: ALOK TIWARI @ 2025-10-13 16:25 UTC (permalink / raw)
To: Daniel Jurgens, netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet
On 10/13/2025 8:57 PM, Daniel Jurgens wrote:
> Object create and destroy were implemented specifically for dev parts
> device objects. Create general purpose APIs for use by upper layer
> drivers.
>
> Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
> Reviewed-by: Parav Pandit <parav@nvidia.com>
>
> ---
> v4: Moved this logic from virtio_pci_modern to new file
> virtio_admin_commands.
> ---
> drivers/virtio/virtio_admin_commands.c | 76 ++++++++++++++++++++++++++
> include/linux/virtio_admin.h | 40 ++++++++++++++
> 2 files changed, 116 insertions(+)
>
> diff --git a/drivers/virtio/virtio_admin_commands.c b/drivers/virtio/virtio_admin_commands.c
> index 94751d16b3c4..d3abb73354e8 100644
> --- a/drivers/virtio/virtio_admin_commands.c
> +++ b/drivers/virtio/virtio_admin_commands.c
> @@ -88,3 +88,79 @@ int virtio_admin_cap_set(struct virtio_device *vdev,
> return err;
> }
> EXPORT_SYMBOL_GPL(virtio_admin_cap_set);
> +
> +int virtio_admin_obj_create(struct virtio_device *vdev,
> + u16 obj_type,
> + u32 obj_id,
> + u16 group_type,
> + u64 group_member_id,
> + const void *obj_specific_data,
> + size_t obj_specific_data_size)
> +{
> + size_t data_size = sizeof(struct virtio_admin_cmd_resource_obj_create_data);
> + struct virtio_admin_cmd_resource_obj_create_data *obj_create_data;
> + struct virtio_admin_cmd cmd = {};
> + struct scatterlist data_sg;
> + void *data;
> + int err;
> +
> + if (!vdev->config->admin_cmd_exec)
> + return -EOPNOTSUPP;
> +
> + data_size += (obj_specific_data_size);
are Parentheses needed here ?
> + data = kzalloc(data_size, GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + obj_create_data = data;
> + obj_create_data->hdr.type = cpu_to_le16(obj_type);
> + obj_create_data->hdr.id = cpu_to_le32(obj_id);
> + memcpy(obj_create_data->resource_obj_specific_data, obj_specific_data,
> + obj_specific_data_size);
> + sg_init_one(&data_sg, data, data_size);
> +
> + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_CREATE);
> + cmd.group_type = cpu_to_le16(group_type);
> + cmd.group_member_id = cpu_to_le64(group_member_id);
> + cmd.data_sg = &data_sg;
> +
> + err = vdev->config->admin_cmd_exec(vdev, &cmd);
> + kfree(data);
> +
> + return err;
> +
remove this extra line
> +}
> +EXPORT_SYMBOL_GPL(virtio_admin_obj_create);
> +
> +int virtio_admin_obj_destroy(struct virtio_device *vdev,
> + u16 obj_type,
> + u32 obj_id,
> + u16 group_type,
> + u64 group_member_id)
> +{
> + struct virtio_admin_cmd_resource_obj_cmd_hdr *data;
> + struct virtio_admin_cmd cmd = {};
> + struct scatterlist data_sg;
> + int err;
> +
> + if (!vdev->config->admin_cmd_exec)
> + return -EOPNOTSUPP;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->type = cpu_to_le16(obj_type);
> + data->id = cpu_to_le32(obj_id);
> + sg_init_one(&data_sg, data, sizeof(*data));
> + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_DESTROY);
> + cmd.group_type = cpu_to_le16(group_type);
> + cmd.group_member_id = cpu_to_le64(group_member_id);
> + cmd.data_sg = &data_sg;
> +
> + err = vdev->config->admin_cmd_exec(vdev, &cmd);
> + kfree(data);
> +
> + return err;
> +}
> +EXPORT_SYMBOL_GPL(virtio_admin_obj_destroy);
> diff --git a/include/linux/virtio_admin.h b/include/linux/virtio_admin.h
> index 36df97b6487a..99efe9e9dc17 100644
> --- a/include/linux/virtio_admin.h
> +++ b/include/linux/virtio_admin.h
> @@ -77,4 +77,44 @@ int virtio_admin_cap_set(struct virtio_device *vdev,
> const void *caps,
> size_t cap_size);
>
> +/**
> + * virtio_admin_obj_create - Create an object on a virtio device
> + * @vdev: the virtio device
> + * @obj_type: type of object to create
> + * @obj_id: ID for the new object
> + * @obj_specific_data: object-specific data for creation
> + * @obj_specific_data_size: size of the object-specific data in bytes
> + *
> + * Creates a new object on the virtio device with the specified type and ID.
> + * The object may require object-specific data for proper initialization.
> + *
> + * Return: 0 on success, -EOPNOTSUPP if the device doesn't support admin
> + * operations or object creation, or a negative error code on other failures.
> + */
> +int virtio_admin_obj_create(struct virtio_device *vdev,
> + u16 obj_type,
> + u32 obj_id,
> + u16 group_type,
> + u64 group_member_id,
> + const void *obj_specific_data,
> + size_t obj_specific_data_size);
> +
> +/**
> + * virtio_admin_obj_destroy - Destroy an object on a virtio device
> + * @vdev: the virtio device
> + * @obj_type: type of object to destroy
> + * @obj_id: ID of the object to destroy
group_type, group_member_id missing
> + *
> + * Destroys an existing object on the virtio device with the specified type
> + * and ID.
> + *
> + * Return: 0 on success, -EOPNOTSUPP if the device doesn't support admin
> + * operations or object destruction, or a negative error code on other failures.
> + */
> +int virtio_admin_obj_destroy(struct virtio_device *virtio_dev,
Function parameters name mismatch in header
virtio_dev and vdev , better to Use the same parameter name.
> + u16 obj_type,
> + u32 obj_id,
> + u16 group_type,
> + u64 group_member_id);
> +
> #endif /* _LINUX_VIRTIO_ADMIN_H */
Thanks,
Alok
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH net-next v4 05/12] virtio_net: Query and set flow filter caps
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
` (3 preceding siblings ...)
2025-10-13 15:27 ` [PATCH net-next v4 04/12] virtio: Expose object create and destroy API Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-14 6:32 ` kernel test robot
2025-10-13 15:27 ` [PATCH net-next v4 06/12] virtio_net: Create a FF group for ethtool steering Daniel Jurgens
` (6 subsequent siblings)
11 siblings, 1 reply; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
When probing a virtnet device, attempt to read the flow filter
capabilities. In order to use the feature the caps must also
be set. For now setting what was read is sufficient.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
---
v4:
- Validate the length in the selector caps
- Removed __free usage.
- Removed for(int.
---
drivers/net/virtio_net.c | 165 +++++++++++++++++++++++++++++
include/linux/virtio_admin.h | 1 +
include/uapi/linux/virtio_net_ff.h | 91 ++++++++++++++++
3 files changed, 257 insertions(+)
create mode 100644 include/uapi/linux/virtio_net_ff.h
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index a757cbcab87f..520162985ad7 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -26,6 +26,9 @@
#include <net/netdev_rx_queue.h>
#include <net/netdev_queues.h>
#include <net/xdp_sock_drv.h>
+#include <linux/virtio_admin.h>
+#include <net/ipv6.h>
+#include <net/ip.h>
static int napi_weight = NAPI_POLL_WEIGHT;
module_param(napi_weight, int, 0444);
@@ -281,6 +284,14 @@ static const struct virtnet_stat_desc virtnet_stats_tx_speed_desc_qstat[] = {
VIRTNET_STATS_DESC_TX_QSTAT(speed, ratelimit_packets, hw_drop_ratelimits),
};
+struct virtnet_ff {
+ struct virtio_device *vdev;
+ bool ff_supported;
+ struct virtio_net_ff_cap_data *ff_caps;
+ struct virtio_net_ff_cap_mask_data *ff_mask;
+ struct virtio_net_ff_actions *ff_actions;
+};
+
#define VIRTNET_Q_TYPE_RX 0
#define VIRTNET_Q_TYPE_TX 1
#define VIRTNET_Q_TYPE_CQ 2
@@ -493,6 +504,8 @@ struct virtnet_info {
struct failover *failover;
u64 device_stats_cap;
+
+ struct virtnet_ff ff;
};
struct padded_vnet_hdr {
@@ -6753,6 +6766,154 @@ static const struct xdp_metadata_ops virtnet_xdp_metadata_ops = {
.xmo_rx_hash = virtnet_xdp_rx_hash,
};
+static size_t get_mask_size(u16 type)
+{
+ switch (type) {
+ case VIRTIO_NET_FF_MASK_TYPE_ETH:
+ return sizeof(struct ethhdr);
+ case VIRTIO_NET_FF_MASK_TYPE_IPV4:
+ return sizeof(struct iphdr);
+ case VIRTIO_NET_FF_MASK_TYPE_IPV6:
+ return sizeof(struct ipv6hdr);
+ case VIRTIO_NET_FF_MASK_TYPE_TCP:
+ return sizeof(struct tcphdr);
+ case VIRTIO_NET_FF_MASK_TYPE_UDP:
+ return sizeof(struct udphdr);
+ }
+
+ return 0;
+}
+
+#define MAX_SEL_LEN (sizeof(struct ipv6hdr))
+
+static void virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
+{
+ size_t ff_mask_size = sizeof(struct virtio_net_ff_cap_mask_data) +
+ sizeof(struct virtio_net_ff_selector) *
+ VIRTIO_NET_FF_MASK_TYPE_MAX;
+ struct virtio_admin_cmd_query_cap_id_result *cap_id_list;
+ struct virtio_net_ff_selector *sel;
+ int err;
+ int i;
+
+ cap_id_list = kzalloc(sizeof(*cap_id_list), GFP_KERNEL);
+ if (!cap_id_list)
+ return;
+
+ err = virtio_admin_cap_id_list_query(vdev, cap_id_list);
+ if (err)
+ goto err_cap_list;
+
+ if (!(VIRTIO_CAP_IN_LIST(cap_id_list,
+ VIRTIO_NET_FF_RESOURCE_CAP) &&
+ VIRTIO_CAP_IN_LIST(cap_id_list,
+ VIRTIO_NET_FF_SELECTOR_CAP) &&
+ VIRTIO_CAP_IN_LIST(cap_id_list,
+ VIRTIO_NET_FF_ACTION_CAP)))
+ goto err_cap_list;
+
+ ff->ff_caps = kzalloc(sizeof(*ff->ff_caps), GFP_KERNEL);
+ if (!ff->ff_caps)
+ goto err_cap_list;
+
+ err = virtio_admin_cap_get(vdev,
+ VIRTIO_NET_FF_RESOURCE_CAP,
+ ff->ff_caps,
+ sizeof(*ff->ff_caps));
+
+ if (err)
+ goto err_ff;
+
+ /* VIRTIO_NET_FF_MASK_TYPE start at 1 */
+ for (i = 1; i <= VIRTIO_NET_FF_MASK_TYPE_MAX; i++)
+ ff_mask_size += get_mask_size(i);
+
+ ff->ff_mask = kzalloc(ff_mask_size, GFP_KERNEL);
+ if (!ff->ff_mask)
+ goto err_ff;
+
+ err = virtio_admin_cap_get(vdev,
+ VIRTIO_NET_FF_SELECTOR_CAP,
+ ff->ff_mask,
+ ff_mask_size);
+
+ if (err)
+ goto err_ff_mask;
+
+ ff->ff_actions = kzalloc(sizeof(*ff->ff_actions) +
+ VIRTIO_NET_FF_ACTION_MAX,
+ GFP_KERNEL);
+ if (!ff->ff_actions)
+ goto err_ff_mask;
+
+ err = virtio_admin_cap_get(vdev,
+ VIRTIO_NET_FF_ACTION_CAP,
+ ff->ff_actions,
+ sizeof(*ff->ff_actions) + VIRTIO_NET_FF_ACTION_MAX);
+
+ if (err)
+ goto err_ff_action;
+
+ err = virtio_admin_cap_set(vdev,
+ VIRTIO_NET_FF_RESOURCE_CAP,
+ ff->ff_caps,
+ sizeof(*ff->ff_caps));
+ if (err)
+ goto err_ff_action;
+
+ ff_mask_size = sizeof(struct virtio_net_ff_cap_mask_data);
+ sel = &ff->ff_mask->selectors[0];
+
+ for (i = 0; i < ff->ff_mask->count; i++) {
+ if (sel->length > MAX_SEL_LEN()) {
+ err = -EINVAL;
+ goto err_ff_action;
+ }
+ ff_mask_size += sizeof(struct virtio_net_ff_selector) + sel->length;
+ sel = (void *)sel + sizeof(*sel) + sel->length;
+ }
+
+ err = virtio_admin_cap_set(vdev,
+ VIRTIO_NET_FF_SELECTOR_CAP,
+ ff->ff_mask,
+ ff_mask_size);
+ if (err)
+ goto err_ff_action;
+
+ err = virtio_admin_cap_set(vdev,
+ VIRTIO_NET_FF_ACTION_CAP,
+ ff->ff_actions,
+ sizeof(*ff->ff_actions) + VIRTIO_NET_FF_ACTION_MAX);
+ if (err)
+ goto err_ff_action;
+
+ ff->vdev = vdev;
+ ff->ff_supported = true;
+
+ kfree(cap_id_list);
+
+ return;
+
+err_ff_action:
+ kfree(ff->ff_actions);
+err_ff_mask:
+ kfree(ff->ff_mask);
+err_ff:
+ kfree(ff->ff_caps);
+err_cap_list:
+ kfree(cap_id_list);
+}
+
+static void virtnet_ff_cleanup(struct virtnet_ff *ff)
+{
+ if (!ff->ff_supported)
+ return;
+
+ kfree(ff->ff_actions);
+ kfree(ff->ff_mask);
+ kfree(ff->ff_caps);
+}
+
static int virtnet_probe(struct virtio_device *vdev)
{
int i, err = -ENOMEM;
@@ -7116,6 +7277,8 @@ static int virtnet_probe(struct virtio_device *vdev)
}
vi->guest_offloads_capable = vi->guest_offloads;
+ virtnet_ff_init(&vi->ff, vi->vdev);
+
rtnl_unlock();
err = virtnet_cpu_notif_add(vi);
@@ -7131,6 +7294,7 @@ static int virtnet_probe(struct virtio_device *vdev)
free_unregister_netdev:
unregister_netdev(dev);
+ virtnet_ff_cleanup(&vi->ff);
free_failover:
net_failover_destroy(vi->failover);
free_vqs:
@@ -7180,6 +7344,7 @@ static void virtnet_remove(struct virtio_device *vdev)
virtnet_free_irq_moder(vi);
unregister_netdev(vi->dev);
+ virtnet_ff_cleanup(&vi->ff);
net_failover_destroy(vi->failover);
diff --git a/include/linux/virtio_admin.h b/include/linux/virtio_admin.h
index 99efe9e9dc17..7a452c6e006b 100644
--- a/include/linux/virtio_admin.h
+++ b/include/linux/virtio_admin.h
@@ -3,6 +3,7 @@
* Header file for virtio admin operations
*/
#include <uapi/linux/virtio_pci.h>
+#include <uapi/linux/virtio_net_ff.h>
#ifndef _LINUX_VIRTIO_ADMIN_H
#define _LINUX_VIRTIO_ADMIN_H
diff --git a/include/uapi/linux/virtio_net_ff.h b/include/uapi/linux/virtio_net_ff.h
new file mode 100644
index 000000000000..1a4738889403
--- /dev/null
+++ b/include/uapi/linux/virtio_net_ff.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ *
+ * Header file for virtio_net flow filters
+ */
+#ifndef _LINUX_VIRTIO_NET_FF_H
+#define _LINUX_VIRTIO_NET_FF_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#define VIRTIO_NET_FF_RESOURCE_CAP 0x800
+#define VIRTIO_NET_FF_SELECTOR_CAP 0x801
+#define VIRTIO_NET_FF_ACTION_CAP 0x802
+
+/**
+ * struct virtio_net_ff_cap_data - Flow filter resource capability limits
+ * @groups_limit: maximum number of flow filter groups supported by the device
+ * @classifiers_limit: maximum number of classifiers supported by the device
+ * @rules_limit: maximum number of rules supported device-wide across all groups
+ * @rules_per_group_limit: maximum number of rules allowed in a single group
+ * @last_rule_priority: priority value associated with the lowest-priority rule
+ * @selectors_per_classifier_limit: maximum selectors allowed in one classifier
+ *
+ * The limits are reported by the device and describe resource capacities for
+ * flow filters. Multi-byte fields are little-endian.
+ */
+struct virtio_net_ff_cap_data {
+ __le32 groups_limit;
+ __le32 classifiers_limit;
+ __le32 rules_limit;
+ __le32 rules_per_group_limit;
+ __u8 last_rule_priority;
+ __u8 selectors_per_classifier_limit;
+};
+
+/**
+ * struct virtio_net_ff_selector - Selector mask descriptor
+ * @type: selector type, one of VIRTIO_NET_FF_MASK_TYPE_* constants
+ * @flags: selector flags, see VIRTIO_NET_FF_MASK_F_* constants
+ * @reserved: must be set to 0 by the driver and ignored by the device
+ * @length: size in bytes of @mask
+ * @reserved1: must be set to 0 by the driver and ignored by the device
+ * @mask: variable-length mask payload for @type, length given by @length
+ *
+ * A selector describes a header mask that a classifier can apply. The format
+ * of @mask depends on @type.
+ */
+struct virtio_net_ff_selector {
+ __u8 type;
+ __u8 flags;
+ __u8 reserved[2];
+ __u8 length;
+ __u8 reserved1[3];
+ __u8 mask[];
+};
+
+#define VIRTIO_NET_FF_MASK_TYPE_ETH 1
+#define VIRTIO_NET_FF_MASK_TYPE_IPV4 2
+#define VIRTIO_NET_FF_MASK_TYPE_IPV6 3
+#define VIRTIO_NET_FF_MASK_TYPE_TCP 4
+#define VIRTIO_NET_FF_MASK_TYPE_UDP 5
+#define VIRTIO_NET_FF_MASK_TYPE_MAX VIRTIO_NET_FF_MASK_TYPE_UDP
+
+/**
+ * struct virtio_net_ff_cap_mask_data - Supported selector mask formats
+ * @count: number of entries in @selectors
+ * @reserved: must be set to 0 by the driver and ignored by the device
+ * @selectors: array of supported selector descriptors
+ */
+struct virtio_net_ff_cap_mask_data {
+ __u8 count;
+ __u8 reserved[7];
+ struct virtio_net_ff_selector selectors[];
+};
+#define VIRTIO_NET_FF_MASK_F_PARTIAL_MASK (1 << 0)
+
+#define VIRTIO_NET_FF_ACTION_DROP 1
+#define VIRTIO_NET_FF_ACTION_RX_VQ 2
+#define VIRTIO_NET_FF_ACTION_MAX VIRTIO_NET_FF_ACTION_RX_VQ
+/**
+ * struct virtio_net_ff_actions - Supported flow actions
+ * @count: number of supported actions in @actions
+ * @reserved: must be set to 0 by the driver and ignored by the device
+ * @actions: array of action identifiers (VIRTIO_NET_FF_ACTION_*)
+ */
+struct virtio_net_ff_actions {
+ __u8 count;
+ __u8 reserved[7];
+ __u8 actions[];
+};
+#endif
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH net-next v4 05/12] virtio_net: Query and set flow filter caps
2025-10-13 15:27 ` [PATCH net-next v4 05/12] virtio_net: Query and set flow filter caps Daniel Jurgens
@ 2025-10-14 6:32 ` kernel test robot
0 siblings, 0 replies; 19+ messages in thread
From: kernel test robot @ 2025-10-14 6:32 UTC (permalink / raw)
To: Daniel Jurgens, netdev, mst, jasowang, alex.williamson, pabeni
Cc: llvm, oe-kbuild-all, virtualization, parav, shshitrit, yohadt,
xuanzhuo, eperezma, shameerali.kolothum.thodi, jgg, kevin.tian,
kuba, andrew+netdev, edumazet, Daniel Jurgens
Hi Daniel,
kernel test robot noticed the following build errors:
[auto build test ERROR on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Daniel-Jurgens/virtio_pci-Remove-supported_cap-size-build-assert/20251014-004146
base: net-next/main
patch link: https://lore.kernel.org/r/20251013152742.619423-6-danielj%40nvidia.com
patch subject: [PATCH net-next v4 05/12] virtio_net: Query and set flow filter caps
config: i386-buildonly-randconfig-003-20251014 (https://download.01.org/0day-ci/archive/20251014/202510141423.IeIhJgQG-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251014/202510141423.IeIhJgQG-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510141423.IeIhJgQG-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/net/virtio_net.c:6868:32: error: called object type 'unsigned int' is not a function or function pointer
6868 | if (sel->length > MAX_SEL_LEN()) {
| ~~~~~~~~~~~^
1 error generated.
vim +6868 drivers/net/virtio_net.c
6788
6789 static void virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
6790 {
6791 size_t ff_mask_size = sizeof(struct virtio_net_ff_cap_mask_data) +
6792 sizeof(struct virtio_net_ff_selector) *
6793 VIRTIO_NET_FF_MASK_TYPE_MAX;
6794 struct virtio_admin_cmd_query_cap_id_result *cap_id_list;
6795 struct virtio_net_ff_selector *sel;
6796 int err;
6797 int i;
6798
6799 cap_id_list = kzalloc(sizeof(*cap_id_list), GFP_KERNEL);
6800 if (!cap_id_list)
6801 return;
6802
6803 err = virtio_admin_cap_id_list_query(vdev, cap_id_list);
6804 if (err)
6805 goto err_cap_list;
6806
6807 if (!(VIRTIO_CAP_IN_LIST(cap_id_list,
6808 VIRTIO_NET_FF_RESOURCE_CAP) &&
6809 VIRTIO_CAP_IN_LIST(cap_id_list,
6810 VIRTIO_NET_FF_SELECTOR_CAP) &&
6811 VIRTIO_CAP_IN_LIST(cap_id_list,
6812 VIRTIO_NET_FF_ACTION_CAP)))
6813 goto err_cap_list;
6814
6815 ff->ff_caps = kzalloc(sizeof(*ff->ff_caps), GFP_KERNEL);
6816 if (!ff->ff_caps)
6817 goto err_cap_list;
6818
6819 err = virtio_admin_cap_get(vdev,
6820 VIRTIO_NET_FF_RESOURCE_CAP,
6821 ff->ff_caps,
6822 sizeof(*ff->ff_caps));
6823
6824 if (err)
6825 goto err_ff;
6826
6827 /* VIRTIO_NET_FF_MASK_TYPE start at 1 */
6828 for (i = 1; i <= VIRTIO_NET_FF_MASK_TYPE_MAX; i++)
6829 ff_mask_size += get_mask_size(i);
6830
6831 ff->ff_mask = kzalloc(ff_mask_size, GFP_KERNEL);
6832 if (!ff->ff_mask)
6833 goto err_ff;
6834
6835 err = virtio_admin_cap_get(vdev,
6836 VIRTIO_NET_FF_SELECTOR_CAP,
6837 ff->ff_mask,
6838 ff_mask_size);
6839
6840 if (err)
6841 goto err_ff_mask;
6842
6843 ff->ff_actions = kzalloc(sizeof(*ff->ff_actions) +
6844 VIRTIO_NET_FF_ACTION_MAX,
6845 GFP_KERNEL);
6846 if (!ff->ff_actions)
6847 goto err_ff_mask;
6848
6849 err = virtio_admin_cap_get(vdev,
6850 VIRTIO_NET_FF_ACTION_CAP,
6851 ff->ff_actions,
6852 sizeof(*ff->ff_actions) + VIRTIO_NET_FF_ACTION_MAX);
6853
6854 if (err)
6855 goto err_ff_action;
6856
6857 err = virtio_admin_cap_set(vdev,
6858 VIRTIO_NET_FF_RESOURCE_CAP,
6859 ff->ff_caps,
6860 sizeof(*ff->ff_caps));
6861 if (err)
6862 goto err_ff_action;
6863
6864 ff_mask_size = sizeof(struct virtio_net_ff_cap_mask_data);
6865 sel = &ff->ff_mask->selectors[0];
6866
6867 for (i = 0; i < ff->ff_mask->count; i++) {
> 6868 if (sel->length > MAX_SEL_LEN()) {
6869 err = -EINVAL;
6870 goto err_ff_action;
6871 }
6872 ff_mask_size += sizeof(struct virtio_net_ff_selector) + sel->length;
6873 sel = (void *)sel + sizeof(*sel) + sel->length;
6874 }
6875
6876 err = virtio_admin_cap_set(vdev,
6877 VIRTIO_NET_FF_SELECTOR_CAP,
6878 ff->ff_mask,
6879 ff_mask_size);
6880 if (err)
6881 goto err_ff_action;
6882
6883 err = virtio_admin_cap_set(vdev,
6884 VIRTIO_NET_FF_ACTION_CAP,
6885 ff->ff_actions,
6886 sizeof(*ff->ff_actions) + VIRTIO_NET_FF_ACTION_MAX);
6887 if (err)
6888 goto err_ff_action;
6889
6890 ff->vdev = vdev;
6891 ff->ff_supported = true;
6892
6893 kfree(cap_id_list);
6894
6895 return;
6896
6897 err_ff_action:
6898 kfree(ff->ff_actions);
6899 err_ff_mask:
6900 kfree(ff->ff_mask);
6901 err_ff:
6902 kfree(ff->ff_caps);
6903 err_cap_list:
6904 kfree(cap_id_list);
6905 }
6906
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH net-next v4 06/12] virtio_net: Create a FF group for ethtool steering
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
` (4 preceding siblings ...)
2025-10-13 15:27 ` [PATCH net-next v4 05/12] virtio_net: Query and set flow filter caps Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 07/12] virtio_net: Implement layer 2 ethtool flow rules Daniel Jurgens
` (5 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
All ethtool steering rules will go in one group, create it during
initialization.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
---
v4: Documented UAPI
---
drivers/net/virtio_net.c | 29 +++++++++++++++++++++++++++++
include/uapi/linux/virtio_net_ff.h | 15 +++++++++++++++
2 files changed, 44 insertions(+)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 520162985ad7..b38fad407ee6 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -284,6 +284,9 @@ static const struct virtnet_stat_desc virtnet_stats_tx_speed_desc_qstat[] = {
VIRTNET_STATS_DESC_TX_QSTAT(speed, ratelimit_packets, hw_drop_ratelimits),
};
+#define VIRTNET_FF_ETHTOOL_GROUP_PRIORITY 1
+#define VIRTNET_FF_MAX_GROUPS 1
+
struct virtnet_ff {
struct virtio_device *vdev;
bool ff_supported;
@@ -6791,6 +6794,7 @@ static void virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
size_t ff_mask_size = sizeof(struct virtio_net_ff_cap_mask_data) +
sizeof(struct virtio_net_ff_selector) *
VIRTIO_NET_FF_MASK_TYPE_MAX;
+ struct virtio_net_resource_obj_ff_group ethtool_group = {};
struct virtio_admin_cmd_query_cap_id_result *cap_id_list;
struct virtio_net_ff_selector *sel;
int err;
@@ -6854,6 +6858,12 @@ static void virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
if (err)
goto err_ff_action;
+ if (le32_to_cpu(ff->ff_caps->groups_limit) < VIRTNET_FF_MAX_GROUPS) {
+ err = -ENOSPC;
+ goto err_ff_action;
+ }
+ ff->ff_caps->groups_limit = cpu_to_le32(VIRTNET_FF_MAX_GROUPS);
+
err = virtio_admin_cap_set(vdev,
VIRTIO_NET_FF_RESOURCE_CAP,
ff->ff_caps,
@@ -6887,6 +6897,19 @@ static void virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
if (err)
goto err_ff_action;
+ ethtool_group.group_priority = cpu_to_le16(VIRTNET_FF_ETHTOOL_GROUP_PRIORITY);
+
+ /* Use priority for the object ID. */
+ err = virtio_admin_obj_create(vdev,
+ VIRTIO_NET_RESOURCE_OBJ_FF_GROUP,
+ VIRTNET_FF_ETHTOOL_GROUP_PRIORITY,
+ VIRTIO_ADMIN_GROUP_TYPE_SELF,
+ 0,
+ ðtool_group,
+ sizeof(ethtool_group));
+ if (err)
+ goto err_ff_action;
+
ff->vdev = vdev;
ff->ff_supported = true;
@@ -6909,6 +6932,12 @@ static void virtnet_ff_cleanup(struct virtnet_ff *ff)
if (!ff->ff_supported)
return;
+ virtio_admin_obj_destroy(ff->vdev,
+ VIRTIO_NET_RESOURCE_OBJ_FF_GROUP,
+ VIRTNET_FF_ETHTOOL_GROUP_PRIORITY,
+ VIRTIO_ADMIN_GROUP_TYPE_SELF,
+ 0);
+
kfree(ff->ff_actions);
kfree(ff->ff_mask);
kfree(ff->ff_caps);
diff --git a/include/uapi/linux/virtio_net_ff.h b/include/uapi/linux/virtio_net_ff.h
index 1a4738889403..eebaaf8f9468 100644
--- a/include/uapi/linux/virtio_net_ff.h
+++ b/include/uapi/linux/virtio_net_ff.h
@@ -12,6 +12,8 @@
#define VIRTIO_NET_FF_SELECTOR_CAP 0x801
#define VIRTIO_NET_FF_ACTION_CAP 0x802
+#define VIRTIO_NET_RESOURCE_OBJ_FF_GROUP 0x0200
+
/**
* struct virtio_net_ff_cap_data - Flow filter resource capability limits
* @groups_limit: maximum number of flow filter groups supported by the device
@@ -88,4 +90,17 @@ struct virtio_net_ff_actions {
__u8 reserved[7];
__u8 actions[];
};
+
+/**
+ * struct virtio_net_resource_obj_ff_group - Flow filter group object
+ * @group_priority: priority of the group used to order evaluation
+ *
+ * This structure is the payload for the VIRTIO_NET_RESOURCE_OBJ_FF_GROUP
+ * administrative object. Devices use @group_priority to order flow filter
+ * groups. Multi-byte fields are little-endian.
+ */
+struct virtio_net_resource_obj_ff_group {
+ __le16 group_priority;
+};
+
#endif
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH net-next v4 07/12] virtio_net: Implement layer 2 ethtool flow rules
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
` (5 preceding siblings ...)
2025-10-13 15:27 ` [PATCH net-next v4 06/12] virtio_net: Create a FF group for ethtool steering Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 08/12] virtio_net: Use existing classifier if possible Daniel Jurgens
` (4 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
Filtering a flow requires a classifier to match the packets, and a rule
to filter on the matches.
A classifier consists of one or more selectors. There is one selector
per header type. A selector must only use fields set in the selector
capability. If partial matching is supported, the classifier mask for a
particular field can be a subset of the mask for that field in the
capability.
The rule consists of a priority, an action and a key. The key is a byte
array containing headers corresponding to the selectors in the
classifier.
This patch implements ethtool rules for ethernet headers.
Example:
$ ethtool -U ens9 flow-type ether dst 08:11:22:33:44:54 action 30
Added rule with ID 1
The rule in the example directs received packets with the specified
destination MAC address to rq 30.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
---
v4:
- Fixed double free bug in error flows
- Build bug on for classifier struct ordering.
- (u8 *) to (void *) casting.
- Documentation in UAPI
- Answered questions about overflow with no changes.
---
drivers/net/virtio_net.c | 461 +++++++++++++++++++++++++++++
include/uapi/linux/virtio_net_ff.h | 50 ++++
2 files changed, 511 insertions(+)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index b38fad407ee6..4185b3dfb61b 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -284,6 +284,11 @@ static const struct virtnet_stat_desc virtnet_stats_tx_speed_desc_qstat[] = {
VIRTNET_STATS_DESC_TX_QSTAT(speed, ratelimit_packets, hw_drop_ratelimits),
};
+struct virtnet_ethtool_ff {
+ struct xarray rules;
+ int num_rules;
+};
+
#define VIRTNET_FF_ETHTOOL_GROUP_PRIORITY 1
#define VIRTNET_FF_MAX_GROUPS 1
@@ -293,8 +298,16 @@ struct virtnet_ff {
struct virtio_net_ff_cap_data *ff_caps;
struct virtio_net_ff_cap_mask_data *ff_mask;
struct virtio_net_ff_actions *ff_actions;
+ struct xarray classifiers;
+ int num_classifiers;
+ struct virtnet_ethtool_ff ethtool;
};
+static int virtnet_ethtool_flow_insert(struct virtnet_ff *ff,
+ struct ethtool_rx_flow_spec *fs,
+ u16 curr_queue_pairs);
+static int virtnet_ethtool_flow_remove(struct virtnet_ff *ff, int location);
+
#define VIRTNET_Q_TYPE_RX 0
#define VIRTNET_Q_TYPE_TX 1
#define VIRTNET_Q_TYPE_CQ 2
@@ -5632,6 +5645,21 @@ static u32 virtnet_get_rx_ring_count(struct net_device *dev)
return vi->curr_queue_pairs;
}
+static int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info)
+{
+ struct virtnet_info *vi = netdev_priv(dev);
+
+ switch (info->cmd) {
+ case ETHTOOL_SRXCLSRLINS:
+ return virtnet_ethtool_flow_insert(&vi->ff, &info->fs,
+ vi->curr_queue_pairs);
+ case ETHTOOL_SRXCLSRLDEL:
+ return virtnet_ethtool_flow_remove(&vi->ff, info->fs.location);
+ }
+
+ return -EOPNOTSUPP;
+}
+
static const struct ethtool_ops virtnet_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES |
ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
@@ -5658,6 +5686,7 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
.get_rxfh_fields = virtnet_get_hashflow,
.set_rxfh_fields = virtnet_set_hashflow,
.get_rx_ring_count = virtnet_get_rx_ring_count,
+ .set_rxnfc = virtnet_set_rxnfc,
};
static void virtnet_get_queue_stats_rx(struct net_device *dev, int i,
@@ -6769,6 +6798,427 @@ static const struct xdp_metadata_ops virtnet_xdp_metadata_ops = {
.xmo_rx_hash = virtnet_xdp_rx_hash,
};
+struct virtnet_ethtool_rule {
+ struct ethtool_rx_flow_spec flow_spec;
+ u32 classifier_id;
+};
+
+/* The classifier struct must be the last field in this struct */
+struct virtnet_classifier {
+ size_t size;
+ u32 id;
+ struct virtio_net_resource_obj_ff_classifier classifier;
+};
+
+static_assert(sizeof(struct virtnet_classifier) ==
+ ALIGN(offsetofend(struct virtnet_classifier, classifier),
+ __alignof__(struct virtnet_classifier)),
+ "virtnet_classifier: classifier must be the last member");
+
+static bool check_mask_vs_cap(const void *m, const void *c,
+ u16 len, bool partial)
+{
+ const u8 *mask = m;
+ const u8 *cap = c;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (partial && ((mask[i] & cap[i]) != mask[i]))
+ return false;
+ if (!partial && mask[i] != cap[i])
+ return false;
+ }
+
+ return true;
+}
+
+static
+struct virtio_net_ff_selector *get_selector_cap(const struct virtnet_ff *ff,
+ u8 selector_type)
+{
+ struct virtio_net_ff_selector *sel;
+ void *buf;
+ int i;
+
+ buf = &ff->ff_mask->selectors;
+ sel = buf;
+
+ for (i = 0; i < ff->ff_mask->count; i++) {
+ if (sel->type == selector_type)
+ return sel;
+
+ buf += sizeof(struct virtio_net_ff_selector) + sel->length;
+ sel = buf;
+ }
+
+ return NULL;
+}
+
+static bool validate_eth_mask(const struct virtnet_ff *ff,
+ const struct virtio_net_ff_selector *sel,
+ const struct virtio_net_ff_selector *sel_cap)
+{
+ bool partial_mask = !!(sel_cap->flags & VIRTIO_NET_FF_MASK_F_PARTIAL_MASK);
+ struct ethhdr *cap, *mask;
+ struct ethhdr zeros = {};
+
+ cap = (struct ethhdr *)&sel_cap->mask;
+ mask = (struct ethhdr *)&sel->mask;
+
+ if (memcmp(&zeros.h_dest, mask->h_dest, sizeof(zeros.h_dest)) &&
+ !check_mask_vs_cap(mask->h_dest, cap->h_dest,
+ sizeof(mask->h_dest), partial_mask))
+ return false;
+
+ if (memcmp(&zeros.h_source, mask->h_source, sizeof(zeros.h_source)) &&
+ !check_mask_vs_cap(mask->h_source, cap->h_source,
+ sizeof(mask->h_source), partial_mask))
+ return false;
+
+ if (mask->h_proto &&
+ !check_mask_vs_cap(&mask->h_proto, &cap->h_proto,
+ sizeof(__be16), partial_mask))
+ return false;
+
+ return true;
+}
+
+static bool validate_mask(const struct virtnet_ff *ff,
+ const struct virtio_net_ff_selector *sel)
+{
+ struct virtio_net_ff_selector *sel_cap = get_selector_cap(ff, sel->type);
+
+ if (!sel_cap)
+ return false;
+
+ switch (sel->type) {
+ case VIRTIO_NET_FF_MASK_TYPE_ETH:
+ return validate_eth_mask(ff, sel, sel_cap);
+ }
+
+ return false;
+}
+
+static int setup_classifier(struct virtnet_ff *ff, struct virtnet_classifier *c)
+{
+ int err;
+
+ err = xa_alloc(&ff->classifiers, &c->id, c,
+ XA_LIMIT(0, le32_to_cpu(ff->ff_caps->classifiers_limit) - 1),
+ GFP_KERNEL);
+ if (err)
+ return err;
+
+ err = virtio_admin_obj_create(ff->vdev,
+ VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER,
+ c->id,
+ VIRTIO_ADMIN_GROUP_TYPE_SELF,
+ 0,
+ &c->classifier,
+ c->size);
+ if (err)
+ goto err_xarray;
+
+ return 0;
+
+err_xarray:
+ xa_erase(&ff->classifiers, c->id);
+
+ return err;
+}
+
+static void destroy_classifier(struct virtnet_ff *ff,
+ u32 classifier_id)
+{
+ struct virtnet_classifier *c;
+
+ c = xa_load(&ff->classifiers, classifier_id);
+ if (c) {
+ virtio_admin_obj_destroy(ff->vdev,
+ VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER,
+ c->id,
+ VIRTIO_ADMIN_GROUP_TYPE_SELF,
+ 0);
+
+ xa_erase(&ff->classifiers, c->id);
+ kfree(c);
+ }
+}
+
+static void destroy_ethtool_rule(struct virtnet_ff *ff,
+ struct virtnet_ethtool_rule *eth_rule)
+{
+ ff->ethtool.num_rules--;
+
+ virtio_admin_obj_destroy(ff->vdev,
+ VIRTIO_NET_RESOURCE_OBJ_FF_RULE,
+ eth_rule->flow_spec.location,
+ VIRTIO_ADMIN_GROUP_TYPE_SELF,
+ 0);
+
+ xa_erase(&ff->ethtool.rules, eth_rule->flow_spec.location);
+ destroy_classifier(ff, eth_rule->classifier_id);
+ kfree(eth_rule);
+}
+
+static int insert_rule(struct virtnet_ff *ff,
+ struct virtnet_ethtool_rule *eth_rule,
+ u32 classifier_id,
+ const u8 *key,
+ size_t key_size)
+{
+ struct ethtool_rx_flow_spec *fs = ð_rule->flow_spec;
+ struct virtio_net_resource_obj_ff_rule *ff_rule;
+ int err;
+
+ ff_rule = kzalloc(sizeof(*ff_rule) + key_size, GFP_KERNEL);
+ if (!ff_rule)
+ return -ENOMEM;
+
+ /* Intentionally leave the priority as 0. All rules have the same
+ * priority.
+ */
+ ff_rule->group_id = cpu_to_le32(VIRTNET_FF_ETHTOOL_GROUP_PRIORITY);
+ ff_rule->classifier_id = cpu_to_le32(classifier_id);
+ ff_rule->key_length = (u8)key_size;
+ ff_rule->action = fs->ring_cookie == RX_CLS_FLOW_DISC ?
+ VIRTIO_NET_FF_ACTION_DROP :
+ VIRTIO_NET_FF_ACTION_RX_VQ;
+ ff_rule->vq_index = fs->ring_cookie != RX_CLS_FLOW_DISC ?
+ cpu_to_le16(fs->ring_cookie) : 0;
+ memcpy(&ff_rule->keys, key, key_size);
+
+ err = virtio_admin_obj_create(ff->vdev,
+ VIRTIO_NET_RESOURCE_OBJ_FF_RULE,
+ fs->location,
+ VIRTIO_ADMIN_GROUP_TYPE_SELF,
+ 0,
+ ff_rule,
+ sizeof(*ff_rule) + key_size);
+ if (err)
+ goto err_ff_rule;
+
+ eth_rule->classifier_id = classifier_id;
+ ff->ethtool.num_rules++;
+ kfree(ff_rule);
+
+ return 0;
+
+err_ff_rule:
+ kfree(ff_rule);
+
+ return err;
+}
+
+static u32 flow_type_mask(u32 flow_type)
+{
+ return flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
+}
+
+static bool supported_flow_type(const struct ethtool_rx_flow_spec *fs)
+{
+ switch (fs->flow_type) {
+ case ETHER_FLOW:
+ return true;
+ }
+
+ return false;
+}
+
+static int validate_flow_input(struct virtnet_ff *ff,
+ const struct ethtool_rx_flow_spec *fs,
+ u16 curr_queue_pairs)
+{
+ /* Force users to use RX_CLS_LOC_ANY - don't allow specific locations */
+ if (fs->location != RX_CLS_LOC_ANY)
+ return -EOPNOTSUPP;
+
+ if (fs->ring_cookie != RX_CLS_FLOW_DISC &&
+ fs->ring_cookie >= curr_queue_pairs)
+ return -EINVAL;
+
+ if (fs->flow_type != flow_type_mask(fs->flow_type))
+ return -EOPNOTSUPP;
+
+ if (!supported_flow_type(fs))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static void calculate_flow_sizes(struct ethtool_rx_flow_spec *fs,
+ size_t *key_size, size_t *classifier_size,
+ int *num_hdrs)
+{
+ *num_hdrs = 1;
+ *key_size = sizeof(struct ethhdr);
+ /*
+ * The classifier size is the size of the classifier header, a selector
+ * header for each type of header in the match criteria, and each header
+ * providing the mask for matching against.
+ */
+ *classifier_size = *key_size +
+ sizeof(struct virtio_net_resource_obj_ff_classifier) +
+ sizeof(struct virtio_net_ff_selector) * (*num_hdrs);
+}
+
+static void setup_eth_hdr_key_mask(struct virtio_net_ff_selector *selector,
+ u8 *key,
+ const struct ethtool_rx_flow_spec *fs)
+{
+ struct ethhdr *eth_m = (struct ethhdr *)&selector->mask;
+ struct ethhdr *eth_k = (struct ethhdr *)key;
+
+ selector->type = VIRTIO_NET_FF_MASK_TYPE_ETH;
+ selector->length = sizeof(struct ethhdr);
+
+ memcpy(eth_m, &fs->m_u.ether_spec, sizeof(*eth_m));
+ memcpy(eth_k, &fs->h_u.ether_spec, sizeof(*eth_k));
+}
+
+static int
+validate_classifier_selectors(struct virtnet_ff *ff,
+ struct virtio_net_resource_obj_ff_classifier *classifier,
+ int num_hdrs)
+{
+ struct virtio_net_ff_selector *selector = classifier->selectors;
+
+ for (int i = 0; i < num_hdrs; i++) {
+ if (!validate_mask(ff, selector))
+ return -EINVAL;
+
+ selector = (((void *)selector) + sizeof(*selector) +
+ selector->length);
+ }
+
+ return 0;
+}
+
+static int build_and_insert(struct virtnet_ff *ff,
+ struct virtnet_ethtool_rule *eth_rule)
+{
+ struct virtio_net_resource_obj_ff_classifier *classifier;
+ struct ethtool_rx_flow_spec *fs = ð_rule->flow_spec;
+ struct virtio_net_ff_selector *selector;
+ struct virtnet_classifier *c;
+ size_t classifier_size;
+ size_t key_size;
+ int num_hdrs;
+ u8 *key;
+ int err;
+
+ calculate_flow_sizes(fs, &key_size, &classifier_size, &num_hdrs);
+
+ key = kzalloc(key_size, GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+
+ /*
+ * virtio_net_ff_obj_ff_classifier is already included in the
+ * classifier_size.
+ */
+ c = kzalloc(classifier_size +
+ sizeof(struct virtnet_classifier) -
+ sizeof(struct virtio_net_resource_obj_ff_classifier),
+ GFP_KERNEL);
+ if (!c) {
+ kfree(key);
+ return -ENOMEM;
+ }
+
+ c->size = classifier_size;
+ classifier = &c->classifier;
+ classifier->count = num_hdrs;
+ selector = &classifier->selectors[0];
+
+ setup_eth_hdr_key_mask(selector, key, fs);
+
+ err = validate_classifier_selectors(ff, classifier, num_hdrs);
+ if (err)
+ goto err_key;
+
+ err = setup_classifier(ff, c);
+ if (err)
+ goto err_classifier;
+
+ err = insert_rule(ff, eth_rule, c->id, key, key_size);
+ if (err) {
+ /* destroy_classifier will free the classifier */
+ destroy_classifier(ff, c->id);
+ goto err_key;
+ }
+
+ return 0;
+
+err_classifier:
+ kfree(c);
+err_key:
+ kfree(key);
+
+ return err;
+}
+
+static int virtnet_ethtool_flow_insert(struct virtnet_ff *ff,
+ struct ethtool_rx_flow_spec *fs,
+ u16 curr_queue_pairs)
+{
+ struct virtnet_ethtool_rule *eth_rule;
+ int err;
+
+ if (!ff->ff_supported)
+ return -EOPNOTSUPP;
+
+ err = validate_flow_input(ff, fs, curr_queue_pairs);
+ if (err)
+ return err;
+
+ eth_rule = kzalloc(sizeof(*eth_rule), GFP_KERNEL);
+ if (!eth_rule)
+ return -ENOMEM;
+
+ err = xa_alloc(&ff->ethtool.rules, &fs->location, eth_rule,
+ XA_LIMIT(0, le32_to_cpu(ff->ff_caps->rules_limit) - 1),
+ GFP_KERNEL);
+ if (err)
+ goto err_rule;
+
+ eth_rule->flow_spec = *fs;
+
+ err = build_and_insert(ff, eth_rule);
+ if (err)
+ goto err_xa;
+
+ return err;
+
+err_xa:
+ xa_erase(&ff->ethtool.rules, eth_rule->flow_spec.location);
+
+err_rule:
+ fs->location = RX_CLS_LOC_ANY;
+ kfree(eth_rule);
+
+ return err;
+}
+
+static int virtnet_ethtool_flow_remove(struct virtnet_ff *ff, int location)
+{
+ struct virtnet_ethtool_rule *eth_rule;
+ int err = 0;
+
+ if (!ff->ff_supported)
+ return -EOPNOTSUPP;
+
+ eth_rule = xa_load(&ff->ethtool.rules, location);
+ if (!eth_rule) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ destroy_ethtool_rule(ff, eth_rule);
+out:
+ return err;
+}
+
static size_t get_mask_size(u16 type)
{
switch (type) {
@@ -6910,6 +7360,8 @@ static void virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
if (err)
goto err_ff_action;
+ xa_init_flags(&ff->classifiers, XA_FLAGS_ALLOC);
+ xa_init_flags(&ff->ethtool.rules, XA_FLAGS_ALLOC);
ff->vdev = vdev;
ff->ff_supported = true;
@@ -6929,9 +7381,18 @@ static void virtnet_ff_init(struct virtnet_ff *ff, struct virtio_device *vdev)
static void virtnet_ff_cleanup(struct virtnet_ff *ff)
{
+ struct virtnet_ethtool_rule *eth_rule;
+ unsigned long i;
+
if (!ff->ff_supported)
return;
+ xa_for_each(&ff->ethtool.rules, i, eth_rule)
+ destroy_ethtool_rule(ff, eth_rule);
+
+ xa_destroy(&ff->ethtool.rules);
+ xa_destroy(&ff->classifiers);
+
virtio_admin_obj_destroy(ff->vdev,
VIRTIO_NET_RESOURCE_OBJ_FF_GROUP,
VIRTNET_FF_ETHTOOL_GROUP_PRIORITY,
diff --git a/include/uapi/linux/virtio_net_ff.h b/include/uapi/linux/virtio_net_ff.h
index eebaaf8f9468..eb17aa90d24c 100644
--- a/include/uapi/linux/virtio_net_ff.h
+++ b/include/uapi/linux/virtio_net_ff.h
@@ -13,6 +13,8 @@
#define VIRTIO_NET_FF_ACTION_CAP 0x802
#define VIRTIO_NET_RESOURCE_OBJ_FF_GROUP 0x0200
+#define VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER 0x0201
+#define VIRTIO_NET_RESOURCE_OBJ_FF_RULE 0x0202
/**
* struct virtio_net_ff_cap_data - Flow filter resource capability limits
@@ -103,4 +105,52 @@ struct virtio_net_resource_obj_ff_group {
__le16 group_priority;
};
+/**
+ * struct virtio_net_resource_obj_ff_classifier - Flow filter classifier object
+ * @count: number of selector entries in @selectors
+ * @reserved: must be set to 0 by the driver and ignored by the device
+ * @selectors: array of selector descriptors that define match masks
+ *
+ * Payload for the VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER administrative object.
+ * Each selector describes a header mask used to match packets
+ * (see struct virtio_net_ff_selector). Selectors appear in the order they are
+ * to be applied.
+ */
+struct virtio_net_resource_obj_ff_classifier {
+ __u8 count;
+ __u8 reserved[7];
+ struct virtio_net_ff_selector selectors[];
+};
+
+/**
+ * struct virtio_net_resource_obj_ff_rule - Flow filter rule object
+ * @group_id: identifier of the target flow filter group
+ * @classifier_id: identifier of the classifier referenced by this rule
+ * @rule_priority: relative priority of this rule within the group
+ * @key_length: number of bytes in @keys
+ * @action: action to perform, one of VIRTIO_NET_FF_ACTION_*
+ * @reserved: must be set to 0 by the driver and ignored by the device
+ * @vq_index: RX virtqueue index for VIRTIO_NET_FF_ACTION_RX_VQ, 0 otherwise
+ * @reserved1: must be set to 0 by the driver and ignored by the device
+ * @keys: concatenated key bytes matching the classifier's selectors order
+ *
+ * Payload for the VIRTIO_NET_RESOURCE_OBJ_FF_RULE administrative object.
+ * @group_id and @classifier_id refer to previously created objects of types
+ * VIRTIO_NET_RESOURCE_OBJ_FF_GROUP and VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER
+ * respectively. The key bytes are compared against packet headers using the
+ * masks provided by the classifier's selectors. Multi-byte fields are
+ * little-endian.
+ */
+struct virtio_net_resource_obj_ff_rule {
+ __le32 group_id;
+ __le32 classifier_id;
+ __u8 rule_priority;
+ __u8 key_length; /* length of key in bytes */
+ __u8 action;
+ __u8 reserved;
+ __le16 vq_index;
+ __u8 reserved1[2];
+ __u8 keys[];
+};
+
#endif
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH net-next v4 08/12] virtio_net: Use existing classifier if possible
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
` (6 preceding siblings ...)
2025-10-13 15:27 ` [PATCH net-next v4 07/12] virtio_net: Implement layer 2 ethtool flow rules Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 09/12] virtio_net: Implement IPv4 ethtool flow rules Daniel Jurgens
` (3 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
Classifiers can be used by more than one rule. If there is an existing
classifier, use it instead of creating a new one.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
---
v4:
- Fixed typo in commit message
- for (int -> for (
---
drivers/net/virtio_net.c | 42 +++++++++++++++++++++++++++-------------
1 file changed, 29 insertions(+), 13 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 4185b3dfb61b..e0f0be40c238 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -6806,6 +6806,7 @@ struct virtnet_ethtool_rule {
/* The classifier struct must be the last field in this struct */
struct virtnet_classifier {
size_t size;
+ refcount_t refcount;
u32 id;
struct virtio_net_resource_obj_ff_classifier classifier;
};
@@ -6899,11 +6900,24 @@ static bool validate_mask(const struct virtnet_ff *ff,
return false;
}
-static int setup_classifier(struct virtnet_ff *ff, struct virtnet_classifier *c)
+static int setup_classifier(struct virtnet_ff *ff,
+ struct virtnet_classifier **c)
{
+ struct virtnet_classifier *tmp;
+ unsigned long i;
int err;
- err = xa_alloc(&ff->classifiers, &c->id, c,
+ xa_for_each(&ff->classifiers, i, tmp) {
+ if ((*c)->size == tmp->size &&
+ !memcmp(&tmp->classifier, &(*c)->classifier, tmp->size)) {
+ refcount_inc(&tmp->refcount);
+ kfree(*c);
+ *c = tmp;
+ goto out;
+ }
+ }
+
+ err = xa_alloc(&ff->classifiers, &(*c)->id, *c,
XA_LIMIT(0, le32_to_cpu(ff->ff_caps->classifiers_limit) - 1),
GFP_KERNEL);
if (err)
@@ -6911,29 +6925,30 @@ static int setup_classifier(struct virtnet_ff *ff, struct virtnet_classifier *c)
err = virtio_admin_obj_create(ff->vdev,
VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER,
- c->id,
+ (*c)->id,
VIRTIO_ADMIN_GROUP_TYPE_SELF,
0,
- &c->classifier,
- c->size);
+ &(*c)->classifier,
+ (*c)->size);
if (err)
goto err_xarray;
+ refcount_set(&(*c)->refcount, 1);
+out:
return 0;
err_xarray:
- xa_erase(&ff->classifiers, c->id);
+ xa_erase(&ff->classifiers, (*c)->id);
return err;
}
-static void destroy_classifier(struct virtnet_ff *ff,
- u32 classifier_id)
+static void try_destroy_classifier(struct virtnet_ff *ff, u32 classifier_id)
{
struct virtnet_classifier *c;
c = xa_load(&ff->classifiers, classifier_id);
- if (c) {
+ if (c && refcount_dec_and_test(&c->refcount)) {
virtio_admin_obj_destroy(ff->vdev,
VIRTIO_NET_RESOURCE_OBJ_FF_CLASSIFIER,
c->id,
@@ -6957,7 +6972,7 @@ static void destroy_ethtool_rule(struct virtnet_ff *ff,
0);
xa_erase(&ff->ethtool.rules, eth_rule->flow_spec.location);
- destroy_classifier(ff, eth_rule->classifier_id);
+ try_destroy_classifier(ff, eth_rule->classifier_id);
kfree(eth_rule);
}
@@ -7082,8 +7097,9 @@ validate_classifier_selectors(struct virtnet_ff *ff,
int num_hdrs)
{
struct virtio_net_ff_selector *selector = classifier->selectors;
+ int i;
- for (int i = 0; i < num_hdrs; i++) {
+ for (i = 0; i < num_hdrs; i++) {
if (!validate_mask(ff, selector))
return -EINVAL;
@@ -7137,14 +7153,14 @@ static int build_and_insert(struct virtnet_ff *ff,
if (err)
goto err_key;
- err = setup_classifier(ff, c);
+ err = setup_classifier(ff, &c);
if (err)
goto err_classifier;
err = insert_rule(ff, eth_rule, c->id, key, key_size);
if (err) {
/* destroy_classifier will free the classifier */
- destroy_classifier(ff, c->id);
+ try_destroy_classifier(ff, c->id);
goto err_key;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH net-next v4 09/12] virtio_net: Implement IPv4 ethtool flow rules
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
` (7 preceding siblings ...)
2025-10-13 15:27 ` [PATCH net-next v4 08/12] virtio_net: Use existing classifier if possible Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 10/12] virtio_net: Add support for IPv6 ethtool steering Daniel Jurgens
` (2 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
Add support for IP_USER type rules from ethtool.
Example:
$ ethtool -U ens9 flow-type ip4 src-ip 192.168.51.101 action -1
Added rule with ID 1
The example rule will drop packets with the source IP specified.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
---
v4:
- Fixed bug in protocol check of parse_ip4
- (u8 *) to (void *) casting.
- Alignment issues.
---
drivers/net/virtio_net.c | 122 ++++++++++++++++++++++++++++++++++++---
1 file changed, 115 insertions(+), 7 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index e0f0be40c238..5f6e8f0a0121 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -6884,6 +6884,34 @@ static bool validate_eth_mask(const struct virtnet_ff *ff,
return true;
}
+static bool validate_ip4_mask(const struct virtnet_ff *ff,
+ const struct virtio_net_ff_selector *sel,
+ const struct virtio_net_ff_selector *sel_cap)
+{
+ bool partial_mask = !!(sel_cap->flags & VIRTIO_NET_FF_MASK_F_PARTIAL_MASK);
+ struct iphdr *cap, *mask;
+
+ cap = (struct iphdr *)&sel_cap->mask;
+ mask = (struct iphdr *)&sel->mask;
+
+ if (mask->saddr &&
+ !check_mask_vs_cap(&mask->saddr, &cap->saddr,
+ sizeof(__be32), partial_mask))
+ return false;
+
+ if (mask->daddr &&
+ !check_mask_vs_cap(&mask->daddr, &cap->daddr,
+ sizeof(__be32), partial_mask))
+ return false;
+
+ if (mask->protocol &&
+ !check_mask_vs_cap(&mask->protocol, &cap->protocol,
+ sizeof(u8), partial_mask))
+ return false;
+
+ return true;
+}
+
static bool validate_mask(const struct virtnet_ff *ff,
const struct virtio_net_ff_selector *sel)
{
@@ -6895,11 +6923,36 @@ static bool validate_mask(const struct virtnet_ff *ff,
switch (sel->type) {
case VIRTIO_NET_FF_MASK_TYPE_ETH:
return validate_eth_mask(ff, sel, sel_cap);
+
+ case VIRTIO_NET_FF_MASK_TYPE_IPV4:
+ return validate_ip4_mask(ff, sel, sel_cap);
}
return false;
}
+static void parse_ip4(struct iphdr *mask, struct iphdr *key,
+ const struct ethtool_rx_flow_spec *fs)
+{
+ const struct ethtool_usrip4_spec *l3_mask = &fs->m_u.usr_ip4_spec;
+ const struct ethtool_usrip4_spec *l3_val = &fs->h_u.usr_ip4_spec;
+
+ mask->saddr = l3_mask->ip4src;
+ mask->daddr = l3_mask->ip4dst;
+ key->saddr = l3_val->ip4src;
+ key->daddr = l3_val->ip4dst;
+
+ if (l3_mask->proto) {
+ mask->protocol = l3_mask->proto;
+ key->protocol = l3_val->proto;
+ }
+}
+
+static bool has_ipv4(u32 flow_type)
+{
+ return flow_type == IP_USER_FLOW;
+}
+
static int setup_classifier(struct virtnet_ff *ff,
struct virtnet_classifier **c)
{
@@ -7034,6 +7087,7 @@ static bool supported_flow_type(const struct ethtool_rx_flow_spec *fs)
{
switch (fs->flow_type) {
case ETHER_FLOW:
+ case IP_USER_FLOW:
return true;
}
@@ -7062,11 +7116,23 @@ static int validate_flow_input(struct virtnet_ff *ff,
}
static void calculate_flow_sizes(struct ethtool_rx_flow_spec *fs,
- size_t *key_size, size_t *classifier_size,
- int *num_hdrs)
+ size_t *key_size, size_t *classifier_size,
+ int *num_hdrs)
{
+ size_t size = sizeof(struct ethhdr);
+
*num_hdrs = 1;
*key_size = sizeof(struct ethhdr);
+
+ if (fs->flow_type == ETHER_FLOW)
+ goto done;
+
+ ++(*num_hdrs);
+ if (has_ipv4(fs->flow_type))
+ size += sizeof(struct iphdr);
+
+done:
+ *key_size = size;
/*
* The classifier size is the size of the classifier header, a selector
* header for each type of header in the match criteria, and each header
@@ -7078,8 +7144,9 @@ static void calculate_flow_sizes(struct ethtool_rx_flow_spec *fs,
}
static void setup_eth_hdr_key_mask(struct virtio_net_ff_selector *selector,
- u8 *key,
- const struct ethtool_rx_flow_spec *fs)
+ u8 *key,
+ const struct ethtool_rx_flow_spec *fs,
+ int num_hdrs)
{
struct ethhdr *eth_m = (struct ethhdr *)&selector->mask;
struct ethhdr *eth_k = (struct ethhdr *)key;
@@ -7087,8 +7154,33 @@ static void setup_eth_hdr_key_mask(struct virtio_net_ff_selector *selector,
selector->type = VIRTIO_NET_FF_MASK_TYPE_ETH;
selector->length = sizeof(struct ethhdr);
- memcpy(eth_m, &fs->m_u.ether_spec, sizeof(*eth_m));
- memcpy(eth_k, &fs->h_u.ether_spec, sizeof(*eth_k));
+ if (num_hdrs > 1) {
+ eth_m->h_proto = cpu_to_be16(0xffff);
+ eth_k->h_proto = cpu_to_be16(ETH_P_IP);
+ } else {
+ memcpy(eth_m, &fs->m_u.ether_spec, sizeof(*eth_m));
+ memcpy(eth_k, &fs->h_u.ether_spec, sizeof(*eth_k));
+ }
+}
+
+static int setup_ip_key_mask(struct virtio_net_ff_selector *selector,
+ u8 *key,
+ const struct ethtool_rx_flow_spec *fs)
+{
+ struct iphdr *v4_m = (struct iphdr *)&selector->mask;
+ struct iphdr *v4_k = (struct iphdr *)key;
+
+ selector->type = VIRTIO_NET_FF_MASK_TYPE_IPV4;
+ selector->length = sizeof(struct iphdr);
+
+ if (fs->h_u.usr_ip4_spec.l4_4_bytes ||
+ fs->h_u.usr_ip4_spec.tos ||
+ fs->h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4)
+ return -EOPNOTSUPP;
+
+ parse_ip4(v4_m, v4_k, fs);
+
+ return 0;
}
static int
@@ -7110,6 +7202,13 @@ validate_classifier_selectors(struct virtnet_ff *ff,
return 0;
}
+static
+struct virtio_net_ff_selector *next_selector(struct virtio_net_ff_selector *sel)
+{
+ return (void *)sel + sizeof(struct virtio_net_ff_selector) +
+ sel->length;
+}
+
static int build_and_insert(struct virtnet_ff *ff,
struct virtnet_ethtool_rule *eth_rule)
{
@@ -7147,8 +7246,17 @@ static int build_and_insert(struct virtnet_ff *ff,
classifier->count = num_hdrs;
selector = &classifier->selectors[0];
- setup_eth_hdr_key_mask(selector, key, fs);
+ setup_eth_hdr_key_mask(selector, key, fs, num_hdrs);
+ if (num_hdrs == 1)
+ goto validate;
+
+ selector = next_selector(selector);
+
+ err = setup_ip_key_mask(selector, key + sizeof(struct ethhdr), fs);
+ if (err)
+ goto err_classifier;
+validate:
err = validate_classifier_selectors(ff, classifier, num_hdrs);
if (err)
goto err_key;
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH net-next v4 10/12] virtio_net: Add support for IPv6 ethtool steering
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
` (8 preceding siblings ...)
2025-10-13 15:27 ` [PATCH net-next v4 09/12] virtio_net: Implement IPv4 ethtool flow rules Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 11/12] virtio_net: Add support for TCP and UDP ethtool rules Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 12/12] virtio_net: Add get ethtool flow rules ops Daniel Jurgens
11 siblings, 0 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
Implement support for IPV6_USER_FLOW type rules.
Example:
$ ethtool -U ens9 flow-type ip6 src-ip fe80::2 dst-ip fe80::4 action 3
Added rule with ID 0
The example rule will forward packets with the specified source and
destination IP addresses to RX ring 3.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
---
v4: commit message typo
---
drivers/net/virtio_net.c | 89 ++++++++++++++++++++++++++++++++++++----
1 file changed, 81 insertions(+), 8 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 5f6e8f0a0121..591bdbe77b99 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -6912,6 +6912,34 @@ static bool validate_ip4_mask(const struct virtnet_ff *ff,
return true;
}
+static bool validate_ip6_mask(const struct virtnet_ff *ff,
+ const struct virtio_net_ff_selector *sel,
+ const struct virtio_net_ff_selector *sel_cap)
+{
+ bool partial_mask = !!(sel_cap->flags & VIRTIO_NET_FF_MASK_F_PARTIAL_MASK);
+ struct ipv6hdr *cap, *mask;
+
+ cap = (struct ipv6hdr *)&sel_cap->mask;
+ mask = (struct ipv6hdr *)&sel->mask;
+
+ if (!ipv6_addr_any(&mask->saddr) &&
+ !check_mask_vs_cap(&mask->saddr, &cap->saddr,
+ sizeof(cap->saddr), partial_mask))
+ return false;
+
+ if (!ipv6_addr_any(&mask->daddr) &&
+ !check_mask_vs_cap(&mask->daddr, &cap->daddr,
+ sizeof(cap->daddr), partial_mask))
+ return false;
+
+ if (mask->nexthdr &&
+ !check_mask_vs_cap(&mask->nexthdr, &cap->nexthdr,
+ sizeof(cap->nexthdr), partial_mask))
+ return false;
+
+ return true;
+}
+
static bool validate_mask(const struct virtnet_ff *ff,
const struct virtio_net_ff_selector *sel)
{
@@ -6926,6 +6954,9 @@ static bool validate_mask(const struct virtnet_ff *ff,
case VIRTIO_NET_FF_MASK_TYPE_IPV4:
return validate_ip4_mask(ff, sel, sel_cap);
+
+ case VIRTIO_NET_FF_MASK_TYPE_IPV6:
+ return validate_ip6_mask(ff, sel, sel_cap);
}
return false;
@@ -6948,11 +6979,38 @@ static void parse_ip4(struct iphdr *mask, struct iphdr *key,
}
}
+static void parse_ip6(struct ipv6hdr *mask, struct ipv6hdr *key,
+ const struct ethtool_rx_flow_spec *fs)
+{
+ const struct ethtool_usrip6_spec *l3_mask = &fs->m_u.usr_ip6_spec;
+ const struct ethtool_usrip6_spec *l3_val = &fs->h_u.usr_ip6_spec;
+
+ if (!ipv6_addr_any((struct in6_addr *)l3_mask->ip6src)) {
+ memcpy(&mask->saddr, l3_mask->ip6src, sizeof(mask->saddr));
+ memcpy(&key->saddr, l3_val->ip6src, sizeof(key->saddr));
+ }
+
+ if (!ipv6_addr_any((struct in6_addr *)l3_mask->ip6dst)) {
+ memcpy(&mask->daddr, l3_mask->ip6dst, sizeof(mask->daddr));
+ memcpy(&key->daddr, l3_val->ip6dst, sizeof(key->daddr));
+ }
+
+ if (l3_mask->l4_proto) {
+ mask->nexthdr = l3_mask->l4_proto;
+ key->nexthdr = l3_val->l4_proto;
+ }
+}
+
static bool has_ipv4(u32 flow_type)
{
return flow_type == IP_USER_FLOW;
}
+static bool has_ipv6(u32 flow_type)
+{
+ return flow_type == IPV6_USER_FLOW;
+}
+
static int setup_classifier(struct virtnet_ff *ff,
struct virtnet_classifier **c)
{
@@ -7088,6 +7146,7 @@ static bool supported_flow_type(const struct ethtool_rx_flow_spec *fs)
switch (fs->flow_type) {
case ETHER_FLOW:
case IP_USER_FLOW:
+ case IPV6_USER_FLOW:
return true;
}
@@ -7130,7 +7189,8 @@ static void calculate_flow_sizes(struct ethtool_rx_flow_spec *fs,
++(*num_hdrs);
if (has_ipv4(fs->flow_type))
size += sizeof(struct iphdr);
-
+ else if (has_ipv6(fs->flow_type))
+ size += sizeof(struct ipv6hdr);
done:
*key_size = size;
/*
@@ -7167,18 +7227,31 @@ static int setup_ip_key_mask(struct virtio_net_ff_selector *selector,
u8 *key,
const struct ethtool_rx_flow_spec *fs)
{
+ struct ipv6hdr *v6_m = (struct ipv6hdr *)&selector->mask;
struct iphdr *v4_m = (struct iphdr *)&selector->mask;
+ struct ipv6hdr *v6_k = (struct ipv6hdr *)key;
struct iphdr *v4_k = (struct iphdr *)key;
- selector->type = VIRTIO_NET_FF_MASK_TYPE_IPV4;
- selector->length = sizeof(struct iphdr);
+ if (has_ipv6(fs->flow_type)) {
+ selector->type = VIRTIO_NET_FF_MASK_TYPE_IPV6;
+ selector->length = sizeof(struct ipv6hdr);
- if (fs->h_u.usr_ip4_spec.l4_4_bytes ||
- fs->h_u.usr_ip4_spec.tos ||
- fs->h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4)
- return -EOPNOTSUPP;
+ if (fs->h_u.usr_ip6_spec.l4_4_bytes ||
+ fs->h_u.usr_ip6_spec.tclass)
+ return -EOPNOTSUPP;
- parse_ip4(v4_m, v4_k, fs);
+ parse_ip6(v6_m, v6_k, fs);
+ } else {
+ selector->type = VIRTIO_NET_FF_MASK_TYPE_IPV4;
+ selector->length = sizeof(struct iphdr);
+
+ if (fs->h_u.usr_ip4_spec.l4_4_bytes ||
+ fs->h_u.usr_ip4_spec.tos ||
+ fs->h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4)
+ return -EOPNOTSUPP;
+
+ parse_ip4(v4_m, v4_k, fs);
+ }
return 0;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH net-next v4 11/12] virtio_net: Add support for TCP and UDP ethtool rules
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
` (9 preceding siblings ...)
2025-10-13 15:27 ` [PATCH net-next v4 10/12] virtio_net: Add support for IPv6 ethtool steering Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
2025-10-13 15:27 ` [PATCH net-next v4 12/12] virtio_net: Add get ethtool flow rules ops Daniel Jurgens
11 siblings, 0 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
Implement TCP and UDP V4/V6 ethtool flow types.
Examples:
$ ethtool -U ens9 flow-type udp4 dst-ip 192.168.5.2 dst-port\
4321 action 20
Added rule with ID 4
This example directs IPv4 UDP traffic with the specified address and
port to queue 20.
$ ethtool -U ens9 flow-type tcp6 src-ip 2001:db8::1 src-port 1234 dst-ip\
2001:db8::2 dst-port 4321 action 12
Added rule with ID 5
This example directs IPv6 TCP traffic with the specified address and
port to queue 12.
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
---
v4: (*num_hdrs)++ to ++(*num_hdrs)
---
drivers/net/virtio_net.c | 207 +++++++++++++++++++++++++++++++++++++--
1 file changed, 198 insertions(+), 9 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 591bdbe77b99..2878a1b9bc62 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -6940,6 +6940,52 @@ static bool validate_ip6_mask(const struct virtnet_ff *ff,
return true;
}
+static bool validate_tcp_mask(const struct virtnet_ff *ff,
+ const struct virtio_net_ff_selector *sel,
+ const struct virtio_net_ff_selector *sel_cap)
+{
+ bool partial_mask = !!(sel_cap->flags & VIRTIO_NET_FF_MASK_F_PARTIAL_MASK);
+ struct tcphdr *cap, *mask;
+
+ cap = (struct tcphdr *)&sel_cap->mask;
+ mask = (struct tcphdr *)&sel->mask;
+
+ if (mask->source &&
+ !check_mask_vs_cap(&mask->source, &cap->source,
+ sizeof(cap->source), partial_mask))
+ return false;
+
+ if (mask->dest &&
+ !check_mask_vs_cap(&mask->dest, &cap->dest,
+ sizeof(cap->dest), partial_mask))
+ return false;
+
+ return true;
+}
+
+static bool validate_udp_mask(const struct virtnet_ff *ff,
+ const struct virtio_net_ff_selector *sel,
+ const struct virtio_net_ff_selector *sel_cap)
+{
+ bool partial_mask = !!(sel_cap->flags & VIRTIO_NET_FF_MASK_F_PARTIAL_MASK);
+ struct udphdr *cap, *mask;
+
+ cap = (struct udphdr *)&sel_cap->mask;
+ mask = (struct udphdr *)&sel->mask;
+
+ if (mask->source &&
+ !check_mask_vs_cap(&mask->source, &cap->source,
+ sizeof(cap->source), partial_mask))
+ return false;
+
+ if (mask->dest &&
+ !check_mask_vs_cap(&mask->dest, &cap->dest,
+ sizeof(cap->dest), partial_mask))
+ return false;
+
+ return true;
+}
+
static bool validate_mask(const struct virtnet_ff *ff,
const struct virtio_net_ff_selector *sel)
{
@@ -6957,11 +7003,45 @@ static bool validate_mask(const struct virtnet_ff *ff,
case VIRTIO_NET_FF_MASK_TYPE_IPV6:
return validate_ip6_mask(ff, sel, sel_cap);
+
+ case VIRTIO_NET_FF_MASK_TYPE_TCP:
+ return validate_tcp_mask(ff, sel, sel_cap);
+
+ case VIRTIO_NET_FF_MASK_TYPE_UDP:
+ return validate_udp_mask(ff, sel, sel_cap);
}
return false;
}
+static void set_tcp(struct tcphdr *mask, struct tcphdr *key,
+ __be16 psrc_m, __be16 psrc_k,
+ __be16 pdst_m, __be16 pdst_k)
+{
+ if (psrc_m) {
+ mask->source = psrc_m;
+ key->source = psrc_k;
+ }
+ if (pdst_m) {
+ mask->dest = pdst_m;
+ key->dest = pdst_k;
+ }
+}
+
+static void set_udp(struct udphdr *mask, struct udphdr *key,
+ __be16 psrc_m, __be16 psrc_k,
+ __be16 pdst_m, __be16 pdst_k)
+{
+ if (psrc_m) {
+ mask->source = psrc_m;
+ key->source = psrc_k;
+ }
+ if (pdst_m) {
+ mask->dest = pdst_m;
+ key->dest = pdst_k;
+ }
+}
+
static void parse_ip4(struct iphdr *mask, struct iphdr *key,
const struct ethtool_rx_flow_spec *fs)
{
@@ -7003,12 +7083,26 @@ static void parse_ip6(struct ipv6hdr *mask, struct ipv6hdr *key,
static bool has_ipv4(u32 flow_type)
{
- return flow_type == IP_USER_FLOW;
+ return flow_type == TCP_V4_FLOW ||
+ flow_type == UDP_V4_FLOW ||
+ flow_type == IP_USER_FLOW;
}
static bool has_ipv6(u32 flow_type)
{
- return flow_type == IPV6_USER_FLOW;
+ return flow_type == TCP_V6_FLOW ||
+ flow_type == UDP_V6_FLOW ||
+ flow_type == IPV6_USER_FLOW;
+}
+
+static bool has_tcp(u32 flow_type)
+{
+ return flow_type == TCP_V4_FLOW || flow_type == TCP_V6_FLOW;
+}
+
+static bool has_udp(u32 flow_type)
+{
+ return flow_type == UDP_V4_FLOW || flow_type == UDP_V6_FLOW;
}
static int setup_classifier(struct virtnet_ff *ff,
@@ -7147,6 +7241,10 @@ static bool supported_flow_type(const struct ethtool_rx_flow_spec *fs)
case ETHER_FLOW:
case IP_USER_FLOW:
case IPV6_USER_FLOW:
+ case TCP_V4_FLOW:
+ case TCP_V6_FLOW:
+ case UDP_V4_FLOW:
+ case UDP_V6_FLOW:
return true;
}
@@ -7191,6 +7289,12 @@ static void calculate_flow_sizes(struct ethtool_rx_flow_spec *fs,
size += sizeof(struct iphdr);
else if (has_ipv6(fs->flow_type))
size += sizeof(struct ipv6hdr);
+
+ if (has_tcp(fs->flow_type) || has_udp(fs->flow_type)) {
+ ++(*num_hdrs);
+ size += has_tcp(fs->flow_type) ? sizeof(struct tcphdr) :
+ sizeof(struct udphdr);
+ }
done:
*key_size = size;
/*
@@ -7225,7 +7329,8 @@ static void setup_eth_hdr_key_mask(struct virtio_net_ff_selector *selector,
static int setup_ip_key_mask(struct virtio_net_ff_selector *selector,
u8 *key,
- const struct ethtool_rx_flow_spec *fs)
+ const struct ethtool_rx_flow_spec *fs,
+ int num_hdrs)
{
struct ipv6hdr *v6_m = (struct ipv6hdr *)&selector->mask;
struct iphdr *v4_m = (struct iphdr *)&selector->mask;
@@ -7236,21 +7341,93 @@ static int setup_ip_key_mask(struct virtio_net_ff_selector *selector,
selector->type = VIRTIO_NET_FF_MASK_TYPE_IPV6;
selector->length = sizeof(struct ipv6hdr);
- if (fs->h_u.usr_ip6_spec.l4_4_bytes ||
- fs->h_u.usr_ip6_spec.tclass)
+ if (num_hdrs == 2 && (fs->h_u.usr_ip6_spec.l4_4_bytes ||
+ fs->h_u.usr_ip6_spec.tclass))
return -EOPNOTSUPP;
parse_ip6(v6_m, v6_k, fs);
+
+ if (num_hdrs > 2) {
+ v6_m->nexthdr = 0xff;
+ if (has_tcp(fs->flow_type))
+ v6_k->nexthdr = IPPROTO_TCP;
+ else
+ v6_k->nexthdr = IPPROTO_UDP;
+ }
} else {
selector->type = VIRTIO_NET_FF_MASK_TYPE_IPV4;
selector->length = sizeof(struct iphdr);
- if (fs->h_u.usr_ip4_spec.l4_4_bytes ||
- fs->h_u.usr_ip4_spec.tos ||
- fs->h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4)
+ if (num_hdrs == 2 &&
+ (fs->h_u.usr_ip4_spec.l4_4_bytes ||
+ fs->h_u.usr_ip4_spec.tos ||
+ fs->h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4))
return -EOPNOTSUPP;
parse_ip4(v4_m, v4_k, fs);
+
+ if (num_hdrs > 2) {
+ v4_m->protocol = 0xff;
+ if (has_tcp(fs->flow_type))
+ v4_k->protocol = IPPROTO_TCP;
+ else
+ v4_k->protocol = IPPROTO_UDP;
+ }
+ }
+
+ return 0;
+}
+
+static int setup_transport_key_mask(struct virtio_net_ff_selector *selector,
+ u8 *key,
+ struct ethtool_rx_flow_spec *fs)
+{
+ struct tcphdr *tcp_m = (struct tcphdr *)&selector->mask;
+ struct udphdr *udp_m = (struct udphdr *)&selector->mask;
+ const struct ethtool_tcpip6_spec *v6_l4_mask;
+ const struct ethtool_tcpip4_spec *v4_l4_mask;
+ const struct ethtool_tcpip6_spec *v6_l4_key;
+ const struct ethtool_tcpip4_spec *v4_l4_key;
+ struct tcphdr *tcp_k = (struct tcphdr *)key;
+ struct udphdr *udp_k = (struct udphdr *)key;
+
+ if (has_tcp(fs->flow_type)) {
+ selector->type = VIRTIO_NET_FF_MASK_TYPE_TCP;
+ selector->length = sizeof(struct tcphdr);
+
+ if (has_ipv6(fs->flow_type)) {
+ v6_l4_mask = &fs->m_u.tcp_ip6_spec;
+ v6_l4_key = &fs->h_u.tcp_ip6_spec;
+
+ set_tcp(tcp_m, tcp_k, v6_l4_mask->psrc, v6_l4_key->psrc,
+ v6_l4_mask->pdst, v6_l4_key->pdst);
+ } else {
+ v4_l4_mask = &fs->m_u.tcp_ip4_spec;
+ v4_l4_key = &fs->h_u.tcp_ip4_spec;
+
+ set_tcp(tcp_m, tcp_k, v4_l4_mask->psrc, v4_l4_key->psrc,
+ v4_l4_mask->pdst, v4_l4_key->pdst);
+ }
+
+ } else if (has_udp(fs->flow_type)) {
+ selector->type = VIRTIO_NET_FF_MASK_TYPE_UDP;
+ selector->length = sizeof(struct udphdr);
+
+ if (has_ipv6(fs->flow_type)) {
+ v6_l4_mask = &fs->m_u.udp_ip6_spec;
+ v6_l4_key = &fs->h_u.udp_ip6_spec;
+
+ set_udp(udp_m, udp_k, v6_l4_mask->psrc, v6_l4_key->psrc,
+ v6_l4_mask->pdst, v6_l4_key->pdst);
+ } else {
+ v4_l4_mask = &fs->m_u.udp_ip4_spec;
+ v4_l4_key = &fs->h_u.udp_ip4_spec;
+
+ set_udp(udp_m, udp_k, v4_l4_mask->psrc, v4_l4_key->psrc,
+ v4_l4_mask->pdst, v4_l4_key->pdst);
+ }
+ } else {
+ return -EOPNOTSUPP;
}
return 0;
@@ -7290,6 +7467,7 @@ static int build_and_insert(struct virtnet_ff *ff,
struct virtio_net_ff_selector *selector;
struct virtnet_classifier *c;
size_t classifier_size;
+ size_t key_offset;
size_t key_size;
int num_hdrs;
u8 *key;
@@ -7323,9 +7501,20 @@ static int build_and_insert(struct virtnet_ff *ff,
if (num_hdrs == 1)
goto validate;
+ key_offset = selector->length;
+ selector = next_selector(selector);
+
+ err = setup_ip_key_mask(selector, key + key_offset, fs, num_hdrs);
+ if (err)
+ goto err_classifier;
+
+ if (num_hdrs == 2)
+ goto validate;
+
+ key_offset += selector->length;
selector = next_selector(selector);
- err = setup_ip_key_mask(selector, key + sizeof(struct ethhdr), fs);
+ err = setup_transport_key_mask(selector, key + key_offset, fs);
if (err)
goto err_classifier;
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH net-next v4 12/12] virtio_net: Add get ethtool flow rules ops
2025-10-13 15:27 [PATCH net-next v4 00/12] virtio_net: Add ethtool flow rules support Daniel Jurgens
` (10 preceding siblings ...)
2025-10-13 15:27 ` [PATCH net-next v4 11/12] virtio_net: Add support for TCP and UDP ethtool rules Daniel Jurgens
@ 2025-10-13 15:27 ` Daniel Jurgens
11 siblings, 0 replies; 19+ messages in thread
From: Daniel Jurgens @ 2025-10-13 15:27 UTC (permalink / raw)
To: netdev, mst, jasowang, alex.williamson, pabeni
Cc: virtualization, parav, shshitrit, yohadt, xuanzhuo, eperezma,
shameerali.kolothum.thodi, jgg, kevin.tian, kuba, andrew+netdev,
edumazet, Daniel Jurgens
- Get total number of rules. There's no user interface for this. It is
used to allocate an appropriately sized buffer for getting all the
rules.
- Get specific rule
$ ethtool -u ens9 rule 0
Filter: 0
Rule Type: UDP over IPv4
Src IP addr: 0.0.0.0 mask: 255.255.255.255
Dest IP addr: 192.168.5.2 mask: 0.0.0.0
TOS: 0x0 mask: 0xff
Src port: 0 mask: 0xffff
Dest port: 4321 mask: 0x0
Action: Direct to queue 16
- Get all rules:
$ ethtool -u ens9
31 RX rings available
Total 2 rules
Filter: 0
Rule Type: UDP over IPv4
Src IP addr: 0.0.0.0 mask: 255.255.255.255
Dest IP addr: 192.168.5.2 mask: 0.0.0.0
...
Filter: 1
Flow Type: Raw Ethernet
Src MAC addr: 00:00:00:00:00:00 mask: FF:FF:FF:FF:FF:FF
Dest MAC addr: 08:11:22:33:44:54 mask: 00:00:00:00:00:00
Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Shahar Shitrit <shshitrit@nvidia.com>
---
v4: Answered questions about rules_limit overflow with no changes.
---
drivers/net/virtio_net.c | 78 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 2878a1b9bc62..9dfa534d2130 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -307,6 +307,13 @@ static int virtnet_ethtool_flow_insert(struct virtnet_ff *ff,
struct ethtool_rx_flow_spec *fs,
u16 curr_queue_pairs);
static int virtnet_ethtool_flow_remove(struct virtnet_ff *ff, int location);
+static int virtnet_ethtool_get_flow_count(struct virtnet_ff *ff,
+ struct ethtool_rxnfc *info);
+static int virtnet_ethtool_get_flow(struct virtnet_ff *ff,
+ struct ethtool_rxnfc *info);
+static int
+virtnet_ethtool_get_all_flows(struct virtnet_ff *ff,
+ struct ethtool_rxnfc *info, u32 *rule_locs);
#define VIRTNET_Q_TYPE_RX 0
#define VIRTNET_Q_TYPE_TX 1
@@ -5645,6 +5652,28 @@ static u32 virtnet_get_rx_ring_count(struct net_device *dev)
return vi->curr_queue_pairs;
}
+static int virtnet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rule_locs)
+{
+ struct virtnet_info *vi = netdev_priv(dev);
+ int rc = 0;
+
+ switch (info->cmd) {
+ case ETHTOOL_GRXCLSRLCNT:
+ rc = virtnet_ethtool_get_flow_count(&vi->ff, info);
+ break;
+ case ETHTOOL_GRXCLSRULE:
+ rc = virtnet_ethtool_get_flow(&vi->ff, info);
+ break;
+ case ETHTOOL_GRXCLSRLALL:
+ rc = virtnet_ethtool_get_all_flows(&vi->ff, info, rule_locs);
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+
+ return rc;
+}
+
static int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info)
{
struct virtnet_info *vi = netdev_priv(dev);
@@ -5686,6 +5715,7 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
.get_rxfh_fields = virtnet_get_hashflow,
.set_rxfh_fields = virtnet_set_hashflow,
.get_rx_ring_count = virtnet_get_rx_ring_count,
+ .get_rxnfc = virtnet_get_rxnfc,
.set_rxnfc = virtnet_set_rxnfc,
};
@@ -7605,6 +7635,54 @@ static int virtnet_ethtool_flow_remove(struct virtnet_ff *ff, int location)
return err;
}
+static int virtnet_ethtool_get_flow_count(struct virtnet_ff *ff,
+ struct ethtool_rxnfc *info)
+{
+ if (!ff->ff_supported)
+ return -EOPNOTSUPP;
+
+ info->rule_cnt = ff->ethtool.num_rules;
+ info->data = le32_to_cpu(ff->ff_caps->rules_limit) | RX_CLS_LOC_SPECIAL;
+
+ return 0;
+}
+
+static int virtnet_ethtool_get_flow(struct virtnet_ff *ff,
+ struct ethtool_rxnfc *info)
+{
+ struct virtnet_ethtool_rule *eth_rule;
+
+ if (!ff->ff_supported)
+ return -EOPNOTSUPP;
+
+ eth_rule = xa_load(&ff->ethtool.rules, info->fs.location);
+ if (!eth_rule)
+ return -ENOENT;
+
+ info->fs = eth_rule->flow_spec;
+
+ return 0;
+}
+
+static int
+virtnet_ethtool_get_all_flows(struct virtnet_ff *ff,
+ struct ethtool_rxnfc *info, u32 *rule_locs)
+{
+ struct virtnet_ethtool_rule *eth_rule;
+ unsigned long i = 0;
+ int idx = 0;
+
+ if (!ff->ff_supported)
+ return -EOPNOTSUPP;
+
+ xa_for_each(&ff->ethtool.rules, i, eth_rule)
+ rule_locs[idx++] = i;
+
+ info->data = le32_to_cpu(ff->ff_caps->rules_limit);
+
+ return 0;
+}
+
static size_t get_mask_size(u16 type)
{
switch (type) {
--
2.50.1
^ permalink raw reply related [flat|nested] 19+ messages in thread