This patch introduces uAPI headers import into the DPDK
repository. This import is possible thanks to Linux Kernel
licence exception for syscalls:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/LICENSES/exceptions/Linux-syscall-note

Header files have to be explicitly imported.

Guidelines are provided in the documentation, and helper
script is also provided to ensure proper import of the
headers (unmodified content from a released Kernel
version).

Signed-off-by: Maxime Coquelin <maxime.coque...@redhat.com>
---
 devtools/linux-uapi.sh                 | 177 +++++++++++++++++++++++++
 doc/guides/contributing/index.rst      |   1 +
 doc/guides/contributing/linux_uapi.rst |  70 ++++++++++
 kernel/linux/uapi/.gitignore           |   4 +
 kernel/linux/uapi/version              |   1 +
 meson.build                            |   8 +-
 6 files changed, 259 insertions(+), 2 deletions(-)
 create mode 100755 devtools/linux-uapi.sh
 create mode 100644 doc/guides/contributing/linux_uapi.rst
 create mode 100644 kernel/linux/uapi/.gitignore
 create mode 100644 kernel/linux/uapi/version

diff --git a/devtools/linux-uapi.sh b/devtools/linux-uapi.sh
new file mode 100755
index 0000000000..175d6a345d
--- /dev/null
+++ b/devtools/linux-uapi.sh
@@ -0,0 +1,177 @@
+#!/bin/sh -e
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2024 Red Hat, Inc.
+
+#
+# Import and check Linux Kernel uAPI headers
+#
+
+base_url="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/";
+base_path="kernel/linux/uapi/"
+version=""
+file=""
+check_headers=0
+errors=0
+
+print_usage()
+{
+       echo "Usage: $(basename $0) [-h] [-i FILE] [-u VERSION] [-c]"
+       echo "-i FILE      import Linux header file. E.g. linux/vfio.h"
+       echo "-u VERSION   update imported list of Linux headers to a given 
version. E.g. v6.10"
+       echo "-c           check headers are valid"
+}
+
+version_older_than() {
+    printf '%s\n%s' "$1" "$2" | sort -C -V
+}
+
+download_header()
+{
+       local header=$1
+       local path=$2
+
+       local url="${base_url}${header}?h=${version}"
+
+       if ! curl -s -f --create-dirs -o $path $url; then
+               echo "Failed to download $url"
+               return 1
+       fi
+
+       return 0
+}
+
+update_headers()
+{
+       local header
+       local url
+       local path
+
+       echo "Updating to $version"
+       for filename in $(find $base_path -name "*.h" -type f); do
+               header=${filename#$base_path}
+               download_header $header $filename || return 1
+       done
+
+       return 0
+}
+
+import_header()
+{
+       local include
+       local import
+       local header=$1
+
+       local path="${base_path}${header}"
+
+       download_header $header $path || return 1
+
+       for include in $(sed -ne 's/^#include <\(.*\)>$/\1/p' $path); do
+               if [ ! -f "${base_path}${include}" ]; then
+                       read -p "Import $include (y/n): " import && [ "$import" 
= 'y' ] || continue
+                       echo "Importing $include for $path"
+                       import_header "$include" || return 1
+               fi
+       done
+
+       return 0
+}
+
+fixup_includes()
+{
+       local path=$1
+
+       sed -i -E -e '
+       s/([[:space:](])(__user|__force|__iomem)[[:space:]]/\1/g
+       s/__attribute_const__([[:space:]]|$)/\1/g
+       s@^#include <linux/compiler(|_types).h>@@
+       
s/(^|[^a-zA-Z0-9])__packed([^a-zA-Z0-9_]|$)/\1__attribute__((packed))\2/g
+       s/(^|[[:space:](])(inline|asm|volatile)([[:space:](]|$)/\1__\2__\3/g
+       s@#(ifndef|define|endif[[:space:]]*/[*])[[:space:]]*_UAPI@#\1 @
+       ' $path
+
+       # Prepend include path with "uapi/" if the header is imported
+       for include in $(sed -ne 's/^#include <\(.*\)>$/\1/p' $path); do
+               if [ -f "${base_path}${include}" ]; then
+                       sed -i "s|${include}|uapi/${include}|g" $path
+               fi
+       done
+}
+
+check_header() {
+       echo -n "Checking $1... "
+
+       if ! diff -q $1 $2 >/dev/null; then
+               echo "KO"
+               diff -u $1 $2
+               return 1
+       else
+               echo "OK"
+       fi
+
+       return 0
+}
+
+while getopts i:u:ch opt ; do
+       case ${opt} in
+               i ) file=$OPTARG ;;
+               u ) version=$OPTARG ;;
+               c ) check_headers=1 ;;
+               h ) print_usage ; exit 0 ;;
+               ? ) print_usage ; exit 1 ;;
+       esac
+done
+
+shift $(($OPTIND - 1))
+if [ $# -ne 0 ]; then
+       print_usage
+       exit 1
+fi
+
+cd $(dirname $0)/..
+
+current_version=$(< ${base_path}/version)
+
+if [ -n "${version}" ]; then
+       if version_older_than "$version" "$current_version"; then
+               echo "Headers already up to date ($current_version >= $version)"
+               version=$current_version
+       else
+               update_headers || exit 1
+       fi
+else
+       echo "Version not specified, using current version ($current_version)"
+       version=$current_version
+fi
+
+if [ -n "${file}" ]; then
+       import_header $file || exit 1
+fi
+
+for filename in $(find $base_path -name "*.h" -type f); do
+       fixup_includes $filename || exit 1
+done
+
+echo $version > ${base_path}/version
+
+if [ $check_headers -eq 0 ]; then
+       exit 0
+fi
+
+tmpheader="$(mktemp -t dpdk.checkuapi.XXXXXX)"
+trap "rm -f '$tmpheader" INT
+
+echo "Checking imported headers for version ${version}"
+
+for filename in $(find $base_path -name "*.h" -type f); do
+       header=${filename#$base_path}
+       download_header $header $tmpheader || exit 1
+       fixup_includes $tmpheader || exit 1
+       check_header $filename $tmpheader || errors=$((errors+1))
+done
+
+echo "$errors error(s) found"
+
+rm -f $tmpheader
+trap - INT
+
+exit $errors
diff --git a/doc/guides/contributing/index.rst 
b/doc/guides/contributing/index.rst
index dcb9b1fbf0..603dc72654 100644
--- a/doc/guides/contributing/index.rst
+++ b/doc/guides/contributing/index.rst
@@ -19,3 +19,4 @@ Contributor's Guidelines
     vulnerability
     stable
     cheatsheet
+    linux_uapi
diff --git a/doc/guides/contributing/linux_uapi.rst 
b/doc/guides/contributing/linux_uapi.rst
new file mode 100644
index 0000000000..00bc092311
--- /dev/null
+++ b/doc/guides/contributing/linux_uapi.rst
@@ -0,0 +1,70 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+   Copyright(c) 2024 Red Hat, Inc.
+
+Linux uAPI header files
+=======================
+
+Rationale
+---------
+
+The system a DPDK library or driver is built on is not necessarily running the
+same Kernel version than the system that will run it.
+Importing Linux Kernel uAPI headers enable to build features that are not
+supported yet by the build system.
+
+For example, the build system runs upstream Kernel v5.19 and we would like to
+build a VDUSE application that will use VDUSE_IOTLB_GET_INFO ioctl() introduced
+in Linux Kernel v6.0.
+
+`Linux Kernel licence exception regarding syscalls
+<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/LICENSES/exceptions/Linux-syscall-note>`_
+enable importing unmodified Linux Kernel uAPI header files.
+
+Importing or updating an uAPI header file
+-----------------------------------------
+
+In order to ensure the imported uAPI headers are both unmodified and from a
+released version of the linux Kernel, a helper script is made available and
+MUST be used.
+Below is an example to import ``linux/vduse.h`` file from Linux ``v6.10``:
+
+.. code-block:: console
+
+   ./devtools/linux-uapi.sh -i linux/vduse.h -u v6.10
+
+Once imported, the header files should be committed without any other change.
+Note that all the imported headers will be updated to the requested version.
+
+Updating imported headers to a newer released version should only be done on
+a need basis, it can be achieved using the same script:
+
+.. code-block:: console
+
+   ./devtools/linux-uapi.sh -u v6.10
+
+The commit message should reflect why updating the headers is necessary.
+
+Once committed, user can check headers are valid by using the same Linux
+uAPI script using the check option:
+
+.. code-block:: console
+
+   ./devtools/linux-uapi.sh -c
+
+Note that all the linux-uapi.sh script options can be combined. For example:
+
+.. code-block:: console
+
+   ./devtools/linux-uapi.sh -i linux/virtio_net.h -u v6.10 -c
+
+Header inclusion into library or driver
+---------------------------------------
+
+The library or driver willing to make use of imported uAPI headers needs to
+explicitly include the header file with ``uapi/`` prefix in C files.
+For example to include VDUSE uAPI:
+
+.. code-block:: c
+
+   #include <uapi/linux/vduse.h>
+
diff --git a/kernel/linux/uapi/.gitignore b/kernel/linux/uapi/.gitignore
new file mode 100644
index 0000000000..558ba597d6
--- /dev/null
+++ b/kernel/linux/uapi/.gitignore
@@ -0,0 +1,4 @@
+**
+!**/
+!**/*.h
+!version
diff --git a/kernel/linux/uapi/version b/kernel/linux/uapi/version
new file mode 100644
index 0000000000..2b256fd48f
--- /dev/null
+++ b/kernel/linux/uapi/version
@@ -0,0 +1 @@
+v0.0
diff --git a/meson.build b/meson.build
index 8b248d4505..85b53cafbb 100644
--- a/meson.build
+++ b/meson.build
@@ -67,16 +67,20 @@ endif
 # configure the build, and make sure configs here and in config folder are
 # able to be included in any file. We also store a global array of include dirs
 # for passing to pmdinfogen scripts
-global_inc = include_directories('.', 'config',
+global_inc = [include_directories('.', 'config',
     'lib/eal/include',
     'lib/eal/@0@/include'.format(host_machine.system()),
     'lib/eal/@0@/include'.format(arch_subdir),
-)
+)]
 
 # do configuration and get tool paths
 subdir('buildtools')
 subdir('config')
 
+if is_linux
+    global_inc += include_directories('kernel/linux')
+endif
+
 # build libs and drivers
 subdir('lib')
 subdir('drivers')
-- 
2.46.2

Reply via email to