diff --git a/README.md b/README.md index b27373b..46b3834 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,14 @@ Tested worker node upgrade chain: --- +## CMM + +Based on vendor's ASK + +See: + +- [Running cmm](docs/cmm.md) + ## Current status This project is usable for experimenting with a single control-plane device image, but it is still a development project. diff --git a/alpine/install-packages.sh b/alpine/install-packages.sh index d5679c3..d924634 100755 --- a/alpine/install-packages.sh +++ b/alpine/install-packages.sh @@ -9,9 +9,9 @@ apk add alpine-base \ # For diagnotics apk add \ - iproute2 iproute2-ss curl bind-tools procps strace tcpdump lsof jq binutils \ + iproute2 iproute2-ss curl bind-tools procps strace tcpdump lsof jq gdb binutils \ openssl conntrack-tools ethtool findmnt kmod coreutils util-linux zstd libcap-utils \ - iotop sysstat + iotop sysstat dtc echo '[ -x /bin/bash ] && exec /bin/bash -l' >> "/root/.profile" # Compat layer for kubelet for now. Will look into building it myself later. If needed diff --git a/alpine/preload-k8s-images.sh b/alpine/preload-k8s-images.sh index d963384..b449780 100755 --- a/alpine/preload-k8s-images.sh +++ b/alpine/preload-k8s-images.sh @@ -27,6 +27,7 @@ FUSE_OVERLAYFS="${FUSE_OVERLAYFS:-/usr/bin/fuse-overlayfs}" EXTRA_IMAGES=( "${EXTRA_IMAGES[@]:-}" "docker-daemon:localhost/monok8s/node-control:$TAG" + "docker-daemon:localhost/monok8s/cmm:$TAG" ) # Keep archive cache version/arch scoped so downloads do not get mixed. diff --git a/alpine/rootfs-extra/etc/fancontrol b/alpine/rootfs-extra/etc/fancontrol index 143d6eb..ddb9db2 100644 --- a/alpine/rootfs-extra/etc/fancontrol +++ b/alpine/rootfs-extra/etc/fancontrol @@ -1,9 +1,10 @@ INTERVAL=10 -DEVPATH=hwmon0=devices/platform/soc/2180000.i2c/i2c-0/i2c-7/7-002e hwmon1=devices/virtual/thermal/thermal_zone0 -DEVNAME=hwmon0=emc2305 hwmon1=ddr_thermal -FCTEMPS=hwmon0/pwm2=hwmon1/temp1_input -FCFANS= hwmon0/pwm2=hwmon0/fan1_input -MINTEMP=hwmon0/pwm2=35 -MAXTEMP=hwmon0/pwm2=60 -MINSTART=hwmon0/pwm2=60 -MINSTOP=hwmon0/pwm2=45 +DEVPATH=hwmon0=devices/platform/soc/1a00000.fman/1afd000.mdio/mdio_bus/0x0000000001afd000/0x0000000001afd000:00 hwmon3=devices/platform/soc/2180000.i2c/i2c-0/i2c-7/7-002e +DEVNAME=hwmon0=0x0000000001afd000:00 hwmon3=emc2305 +FCTEMPS=hwmon3/pwm1=hwmon0/temp1_input +FCFANS= hwmon3/pwm1=hwmon3/fan1_input +MINTEMP=hwmon3/pwm1=35 +MAXTEMP=hwmon3/pwm1=60 +MINSTART=hwmon3/pwm1=60 +MINSTOP=hwmon3/pwm1=45 +MINPWM=hwmon3/pwm1=0 diff --git a/alpine/rootfs-extra/etc/local.d/monok8s.start b/alpine/rootfs-extra/etc/local.d/monok8s.start index 30428c9..8d350fe 100755 --- a/alpine/rootfs-extra/etc/local.d/monok8s.start +++ b/alpine/rootfs-extra/etc/local.d/monok8s.start @@ -11,6 +11,19 @@ BOOT_STATE=/run/monok8s/boot-state.env BOOTPART_FILE="$CONFIG_DIR/.bootpart" MIGRATION_STATE_DIR="$CONFIG_DIR/migration-state" +load_module_optional() { + module="$1" + + if ! modprobe "$module"; then + echo "WARNING: failed to load optional module: $module" + fi +} + +echo "Loading optional hardware offload modules..." +load_module_optional cdx +load_module_optional fci +load_module_optional auto_bridge + mkdir -p /dev/hugepages mountpoint -q /dev/hugepages || mount -t hugetlbfs none /dev/hugepages echo 256 > /proc/sys/vm/nr_hugepages diff --git a/ask/cmm/cmm.conf b/ask/cmm/cmm.conf new file mode 100644 index 0000000..0e2977a --- /dev/null +++ b/ask/cmm/cmm.conf @@ -0,0 +1,27 @@ +# CMM Fast Forward configuration +# This file specifies traffic that should NOT be offloaded to the fast path + +# Don't Fast Forward FTP traffic (needs ALG) +config fastforward ftp + option proto tcp + option port 21 + +# Don't Fast Forward SIP (needs ALG) +config fastforward sip + option proto udp + option port 5060 + +# Don't Fast Forward PPTP control +config fastforward pptp + option proto tcp + option port 1723 + +# Optional logging +# NOTE: "stdout" only supported in our own patched version. +# Keep info disabled by default; CMM receives the whole conntrack stream. +config logging + option file stdout + option command 0 + option error 1 + option warning 1 + option info 0 diff --git a/ask/cmm/entrypoint.sh b/ask/cmm/entrypoint.sh new file mode 100644 index 0000000..a9dac4e --- /dev/null +++ b/ask/cmm/entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -eu + +CMM_CONFIG="${CMM_CONFIG:-/etc/cmm/cmm.conf}" + +# Vendor default from cmm.service: +# 131072 = 128 * 1024 max active conntrack/offload entries. +CMM_MAX_CONNECTIONS="${CMM_MAX_CONNECTIONS:-131072}" + +echo "[ask] starting cmm" +exec /bin/cmm -D -f "$CMM_CONFIG" -n "$CMM_MAX_CONNECTIONS" diff --git a/ask/cmm/init_dpa.sh b/ask/cmm/init_dpa.sh new file mode 100755 index 0000000..e11b624 --- /dev/null +++ b/ask/cmm/init_dpa.sh @@ -0,0 +1,37 @@ +#!/bin/sh +set -eu + +STATE_DIR="${CMM_STATE_DIR:-/host/run/monok8s/cmm}" +DONE_FILE="$STATE_DIR/dpa_app.loaded" +LOCK_DIR="$STATE_DIR/dpa_app.lock" + +mkdir -p "$STATE_DIR" + +if [ -e "$DONE_FILE" ]; then + echo "[ask] dpa_app already loaded for this boot; skipping" + exit 0 +fi + +if ! mkdir "$LOCK_DIR" 2>/dev/null; then + echo "[ask] another dpa_app initialization is running; waiting" + + while [ ! -e "$DONE_FILE" ]; do + sleep 1 + done + + echo "[ask] dpa_app was loaded by another process; skipping" + exit 0 +fi + +trap 'rmdir "$LOCK_DIR" 2>/dev/null || true' EXIT + +if [ -e "$DONE_FILE" ]; then + echo "[ask] dpa_app already loaded for this boot; skipping" + exit 0 +fi + +echo "[ask] running dpa_app" +/bin/dpa_app + +date -u +"%Y-%m-%dT%H:%M:%SZ" > "$DONE_FILE" +echo "[ask] dpa_app loaded" diff --git a/build.env b/build.env index 970850f..a7446f7 100644 --- a/build.env +++ b/build.env @@ -11,7 +11,17 @@ FMC_VERSION=lf-$(LINUX_FACTORY) DPDK_VERSION=lf-$(LINUX_FACTORY) VPP_VERSION=lf-$(LINUX_FACTORY) VPP_UPSTREAM_VERSION=23.10 -MONO_ASK_VERSION=mt-$(LINUX_FACTORY) + +# ASK's deps +MONO_ASK_VERSION=mt-6.12.49-2.2.0 +LIBNFNETLINK_VERSION=1.0.2 +LIBMNL_VERSION=1.0.5 +LIBNFCT_VERSION=1.1.0 +LIBCLI_VERSION=1.10.7 +# Check the package version for Debian trixies (what ASK uses) +LIBXML2_VERSION=2.11.7 +TCLAP_VERSION=1.2.5 +LIBPCAP_VERSION=1.10.4 CRIO_VERSION=cri-o.arm64.v1.35.2 KUBE_VERSION=v1.35.3 @@ -47,3 +57,10 @@ APT_PROXY= # remote image repository prefix to push to # e.g. ghcr.io/monok8s IMAGE_REPOSITORY= + +# Mirror +# You can host your local mirror by running (but you'll need to download them first) +# kubectl apply -f devtools/dep-pkg-mirror.yaml +# devtools/push-dep-pkg-mirror.sh +# e.g. http://dep-pkg-mirror.default.svc.cluster.local/monok8s +DEP_PKG_MIRROR= diff --git a/clitools/pkg/cmd/create/create.go b/clitools/pkg/cmd/create/create.go index e3b5fd8..9ec1a23 100644 --- a/clitools/pkg/cmd/create/create.go +++ b/clitools/pkg/cmd/create/create.go @@ -165,6 +165,47 @@ func NewCmdCreate(flags *genericclioptions.ConfigFlags) *cobra.Command { cmd.AddCommand(&agentcmd) + cmmconf := render.CMMConf{} + cmmcmd := cobra.Command{ + Use: "cmm", + Short: "Print CMM daemonsets template", + RunE: func(cmd *cobra.Command, _ []string) error { + if len(cmmconf.ImagePullSecrets) > 0 && strings.TrimSpace(cmmconf.Image) == "" { + return fmt.Errorf("--image-pull-secret requires --image") + } + + ns, _, err := flags.ToRawKubeConfigLoader().Namespace() + if err != nil { + return err + } + + cmmconf.Namespace = ns + + out, err := render.RenderCMMDaemonSets(cmmconf) + if err != nil { + return err + } + + _, err = fmt.Fprint(cmd.OutOrStdout(), out) + return err + }, + } + + cmmcmd.Flags().StringVar( + &cmmconf.Image, + "image", + "", + "CMM image, including optional registry and tag", + ) + cmmcmd.Flags().StringSliceVar( + &cmmconf.ImagePullSecrets, + "image-pull-secret", + nil, + "Image pull secret name for the CMM image; may be specified multiple times or as a comma-separated list", + ) + + cmd.AddCommand(&cmmcmd) + return cmd } diff --git a/clitools/pkg/render/cmm.go b/clitools/pkg/render/cmm.go new file mode 100644 index 0000000..4b85275 --- /dev/null +++ b/clitools/pkg/render/cmm.go @@ -0,0 +1,219 @@ +package render + +import ( + "fmt" + "strings" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + + buildinfo "example.com/monok8s/pkg/buildinfo" +) + +const cmmName = "cmm" + +type CMMConf struct { + Namespace string + Image string + ImagePullSecrets []string + Labels map[string]string +} + +func RenderCMMDaemonSets(conf CMMConf) (string, error) { + objs, err := buildCMMDaemonSetObjects(conf) + if err != nil { + return "", err + } + + return renderObjects(objs) +} + +func buildCMMDaemonSetObjects(conf CMMConf) ([]runtime.Object, error) { + if strings.TrimSpace(conf.Namespace) == "" { + return nil, fmt.Errorf("namespace is required") + } + + conf.Labels = map[string]string{ + "app.kubernetes.io/name": cmmName, + "app.kubernetes.io/component": "hardware-offload", + "app.kubernetes.io/part-of": "monok8s", + "app.kubernetes.io/managed-by": "monok8s", + } + + return []runtime.Object{ + buildCMMServiceAccount(conf), + buildCMMDaemonSet(conf), + }, nil +} + +func buildCMMServiceAccount(conf CMMConf) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: cmmName, + Namespace: conf.Namespace, + Labels: copyStringMap(conf.Labels), + }, + } +} + +func buildCMMDaemonSet(conf CMMConf) *appsv1.DaemonSet { + privileged := true + dsLabels := map[string]string{ + "app.kubernetes.io/name": cmmName, + "app.kubernetes.io/component": "hardware-offload", + "app.kubernetes.io/part-of": "monok8s", + "app.kubernetes.io/managed-by": "monok8s", + } + + image, pullPolicy := cmmImage(conf) + + return &appsv1.DaemonSet{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "DaemonSet", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: cmmName, + Namespace: conf.Namespace, + Labels: copyStringMap(conf.Labels), + }, + Spec: appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": cmmName, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: dsLabels, + }, + Spec: corev1.PodSpec{ + ServiceAccountName: cmmName, + HostNetwork: true, + DNSPolicy: corev1.DNSClusterFirstWithHostNet, + ImagePullSecrets: imagePullSecrets(conf.ImagePullSecrets), + Tolerations: []corev1.Toleration{ + {Operator: corev1.TolerationOpExists}, + }, + InitContainers: []corev1.Container{ + { + Name: "dpa-app", + Image: image, + ImagePullPolicy: pullPolicy, + Command: []string{"/init_dpa.sh"}, + Env: cdxEnv(), + SecurityContext: &corev1.SecurityContext{ + Privileged: &privileged, + }, + VolumeMounts: append( + []corev1.VolumeMount{ + { + Name: "host-run-cmm", + MountPath: "/host/run/monok8s/cmm", + }, + }, + cdxVolumeMounts()..., + ), + }, + }, + Containers: []corev1.Container{ + { + Name: cmmName, + Image: image, + ImagePullPolicy: pullPolicy, + Env: cmmEnv(), + SecurityContext: &corev1.SecurityContext{ + Privileged: &privileged, + }, + VolumeMounts: cdxVolumeMounts(), + }, + }, + Volumes: cmmVolumes(), + NodeSelector: map[string]string{ + "node.kubernetes.io/instance-type": "mono-gateway", + }, + }, + }, + }, + } +} + +func cdxEnv() []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: "CDX_CFG_FILE", + Value: "/etc/dpa/cdx_cfg.xml", + }, + { + Name: "CDX_PCD_FILE", + Value: "/etc/dpa/cdx_pcd.xml", + }, + { + Name: "CDX_PDL_FILE", + Value: "/etc/fmc/config/hxs_pdl_v3.xml", + }, + { + Name: "CDX_SP_FILE", + Value: "/etc/dpa/cdx_sp.xml", + }, + } +} + +func cmmEnv() []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: "CMM_CONFIG", + Value: "/etc/cmm/cmm.conf", + }, + { + Name: "CMM_MAX_CONNECTIONS", + Value: "131072", + }, + } +} + +func cdxVolumeMounts() []corev1.VolumeMount { + return []corev1.VolumeMount{ + { + Name: "cdx-ctrl", + MountPath: "/dev/cdx_ctrl", + }, + } +} + +func cmmVolumes() []corev1.Volume { + return []corev1.Volume{ + { + Name: "cdx-ctrl", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/dev/cdx_ctrl", + Type: hostPathType(corev1.HostPathCharDev), + }, + }, + }, + { + Name: "host-run-cmm", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/run/monok8s/cmm", + Type: hostPathType(corev1.HostPathDirectoryOrCreate), + }, + }, + }, + } +} + +func cmmImage(conf CMMConf) (string, corev1.PullPolicy) { + if conf.Image != "" { + return conf.Image, corev1.PullIfNotPresent + } + + return fmt.Sprintf("localhost/monok8s/cmm:%s", buildinfo.Version), corev1.PullNever +} diff --git a/devtools/create-join-token.sh b/devtools/create-join-token.sh index 977435d..bf3ea93 100755 --- a/devtools/create-join-token.sh +++ b/devtools/create-join-token.sh @@ -188,7 +188,6 @@ make cluster-config \\ MKS_CNI_PLUGIN=none EOF - if [ "$signed" != "true" ]; then echo >&2 echo "warning: cluster-info was not signed within ${WAIT_SECONDS}s." >&2 diff --git a/devtools/dep-pkg-mirror.yaml b/devtools/dep-pkg-mirror.yaml new file mode 100644 index 0000000..9181ae5 --- /dev/null +++ b/devtools/dep-pkg-mirror.yaml @@ -0,0 +1,101 @@ +# Hosts a mirror for dep pkg +# kubectl apply -f dep-pkg-mirror.yaml -n [namespace] +# kubectl -n [namespace] cp ./packages \ deploy/monok8s-mirror:/usr/share/nginx/html/monok8s/ +# Fetch helper contract: +# DEP_PKG_MIRROR=https://mirror.example.com/monok8s +# mirror URL = ${DEP_PKG_MIRROR}/packages/${mirror_path} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: dep-pkg-mirror-data +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dep-pkg-mirror-nginx +data: + default.conf: | + server { + listen 8080; + server_name _; + + root /usr/share/nginx/html; + + autoindex on; + autoindex_exact_size off; + autoindex_localtime on; + + location /monok8s/packages/ { + try_files $uri =404; + } + + location = /healthz { + access_log off; + return 200 "ok\n"; + } + } +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep-pkg-mirror +spec: + replicas: 1 + selector: + matchLabels: + app: dep-pkg-mirror + template: + metadata: + labels: + app: dep-pkg-mirror + spec: + containers: + - name: nginx + image: nginx:1.27-alpine + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8080 + readinessProbe: + httpGet: + path: /healthz + port: http + livenessProbe: + httpGet: + path: /healthz + port: http + volumeMounts: + - name: data + mountPath: /usr/share/nginx/html/monok8s/packages + subPath: packages + readOnly: false + - name: nginx-conf + mountPath: /etc/nginx/conf.d/default.conf + subPath: default.conf + readOnly: true + volumes: + - name: data + persistentVolumeClaim: + claimName: dep-pkg-mirror-data + - name: nginx-conf + configMap: + name: dep-pkg-mirror-nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: dep-pkg-mirror +spec: + type: ClusterIP + selector: + app: dep-pkg-mirror + ports: + - name: http + port: 80 + targetPort: http diff --git a/devtools/push-dep-pkg-mirror.sh b/devtools/push-dep-pkg-mirror.sh new file mode 100755 index 0000000..bbe6302 --- /dev/null +++ b/devtools/push-dep-pkg-mirror.sh @@ -0,0 +1,80 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +NAMESPACE="${NAMESPACE:-default}" +PACKAGES_DIR="$(realpath "$SCRIPT_DIR/../packages/")" +APP_LABEL="${APP_LABEL:-app=dep-pkg-mirror}" +CONTAINER="${CONTAINER:-nginx}" +REMOTE_DIR="${REMOTE_DIR:-/usr/share/nginx/html/monok8s/packages}" + +if [ ! -d "$PACKAGES_DIR" ]; then + echo "error: package dir not found: $PACKAGES_DIR" >&2 + exit 1 +fi + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || { + echo "error: missing command: $1" >&2 + exit 1 + } +} + +need_cmd kubectl +need_cmd mktemp +need_cmd tar +need_cmd cp +need_cmd find + +pod="$( + kubectl -n "$NAMESPACE" get pod \ + -l "$APP_LABEL" \ + --field-selector=status.phase=Running \ + -o jsonpath='{.items[0].metadata.name}' +)" + +if [ -z "$pod" ]; then + echo "error: no running pod found with label: $APP_LABEL in namespace: $NAMESPACE" >&2 + exit 1 +fi + +echo "using pod: $pod" +echo "remote dir: $REMOTE_DIR" + +echo "checking remote dir is writable" +kubectl -n "$NAMESPACE" exec "$pod" -c "$CONTAINER" -- sh -c " + mkdir -p '$REMOTE_DIR' && + touch '$REMOTE_DIR/.write-test' && + rm -f '$REMOTE_DIR/.write-test' +" + +stage="$(mktemp -d)" +cleanup() { + rm -rf "$stage" +} +trap cleanup EXIT INT TERM + +mkdir -p "$stage/packages" + +echo "staging $PACKAGES_DIR/ as packages/" +tar \ + --exclude='.DS_Store' \ + --exclude='.stamp-*' \ + -C "$PACKAGES_DIR" \ + -cf - . | tar -C "$stage/packages" -xf - + +echo "copying staged packages into mirror pod" +kubectl -n "$NAMESPACE" cp \ + "$stage/packages/." \ + "$pod:$REMOTE_DIR" \ + -c "$CONTAINER" + +echo "done" +echo +echo "Mirror base URL:" +echo " DEP_PKG_MIRROR=http://dep-pkg-mirror.${NAMESPACE}.svc.cluster.local/monok8s" +echo +echo "Example:" +echo " packages/kubernetes/kubelet-v1.35.3" +echo " -> http://dep-pkg-mirror.${NAMESPACE}.svc.cluster.local/monok8s/packages/kubernetes/kubelet-v1.35.3" diff --git a/docker/ask.Dockerfile b/docker/ask.Dockerfile new file mode 100644 index 0000000..896620a --- /dev/null +++ b/docker/ask.Dockerfile @@ -0,0 +1,310 @@ +ARG BUILD_BASE_TAG=dev +ARG DOCKER_IMAGE_ROOT=monok8s + +FROM --platform=$BUILDPLATFORM ${DOCKER_IMAGE_ROOT}/build-base:${BUILD_BASE_TAG} AS build + +# Install glibc cross-compiler for kernel and standard build dependencies +RUN apt-get update && apt-get install -y pkg-config + +RUN git config --global user.email "monok8s@localhost" && \ + git config --global user.name "monok8s authors" && \ + git config --global --add safe.directory '*' + +WORKDIR /src + +ARG AARCH64_MUSL_CC_TAR +ARG NXP_TAR +ARG MONO_ASK_TAR +ARG LIBNFNETLINK_TAR +ARG LIBMNL_TAR +ARG LIBNFCT_TAR +ARG FMLIB_TAR +ARG FMC_TAR +ARG LIBXML2_TAR +ARG LIBPCAP_TAR +ARG LIBCLI_TAR +ARG TCLAP_TAR + +# ASK's version pins (hardcoded wget) +ARG LIBNFNETLINK_VERSION +ARG LIBNFCT_VERSION + +# MUSL Cross Compiler +COPY "${AARCH64_MUSL_CC_TAR}" ./aarch64_musl_cc.tar.gz + +# Linux kernel +COPY "${NXP_TAR}" ./kernel.tar.gz + +# Copy the ASK deps +COPY "${MONO_ASK_TAR}" ./mono-ask.tar.gz +COPY "${FMC_TAR}" ./fmc.tar.gz +COPY "${FMLIB_TAR}" ./fmlib.tar.gz +COPY "${LIBXML2_TAR}" ./libxml2.tar.xz +COPY "${LIBPCAP_TAR}" ./libpcap.tar.xz +COPY "${TCLAP_TAR}" ./tclap.tar.gz +COPY "${LIBMNL_TAR}" ./libmnl.tar.bz2 +COPY "${LIBCLI_TAR}" ./libcli.tar.gz +# Pinned version should keep version names +COPY "${LIBNFNETLINK_TAR}" ./libnfnetlink-${LIBNFNETLINK_VERSION}.tar.bz2 +COPY "${LIBNFCT_TAR}" ./libnetfilter_conntrack-${LIBNFCT_VERSION}.tar.xz + +# Provision the musl cross-compiler from musl.cc +RUN tar zxf "aarch64_musl_cc.tar.gz" -C /opt + +# Common paths / flags for the remaining ASK build. +# Keep userspace fully static against the musl sysroot. +ENV PATH="/opt/aarch64-linux-musl-cross/bin:${PATH}" \ + ASK_DIR=/src/ASK \ + KERNEL_DIR=/src/linux \ + SYSROOT=/opt/aarch64-linux-musl-cross/aarch64-linux-musl \ + HOST=aarch64-linux-musl \ + CROSS_COMPILE=aarch64-linux-musl- \ + ARCH=arm64 \ + PLATFORM=LS1043A + +# Extract and build the dependency libraries +RUN mkdir -p ASK/sources/tarballs && \ + tar zxf "mono-ask.tar.gz" -C "ASK" --strip-components=1 && \ + mv libnfnetlink-${LIBNFNETLINK_VERSION}.tar.bz2 ASK/sources/tarballs/ && \ + mv libnetfilter_conntrack-${LIBNFCT_VERSION}.tar.xz ASK/sources/tarballs/ + +RUN mkdir linux && tar zxf "kernel.tar.gz" -C "linux" --strip-components=1 && \ + mkdir -p /src/ASK/patches/kernel/updated-patch + +# ASK's Kernel patches +COPY patches/ask/split-kernel-patch.sh /src/split-kernel-patch.sh +COPY patches/ask/upstream/kernel/*.patch /src/ASK/patches/kernel/updated-patch/ + +RUN chmod +x /src/split-kernel-patch.sh && \ + PATCH_FILE="/src/ASK/patches/kernel/002-mono-gateway-ask-kernel_linux_6_12.patch" \ + LINUX_DIR="/src/linux" \ + OUT_DIR="/src/ASK/patches/kernel/split-002" \ + /src/split-kernel-patch.sh split && \ + /src/split-kernel-patch.sh apply + +# Build the kernel first, without the custom DTS +COPY kernel-extra.config /src/kernel-extra.config +COPY kernel-build/ensure-kconfig.sh /src/ + +RUN cd /src/linux \ + && make ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" defconfig lsdk.config \ + && ./scripts/kconfig/merge_config.sh -m .config "${ASK_DIR}/config/kernel/defconfig" /src/kernel-extra.config \ + && make ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" olddefconfig \ + && /src/ensure-kconfig.sh .config /src/kernel-extra.config \ + && make ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" -j"$(nproc)" + +# tclap +RUN mkdir -p tclap && tar zxf "tclap.tar.gz" -C "tclap" --strip-components=1 && \ + cp -r tclap/include/tclap /opt/aarch64-linux-musl-cross/aarch64-linux-musl/include/ && \ + rm -rf tclap + +# libxml2 +RUN mkdir -p libxml2 && tar xf "libxml2.tar.xz" -C "libxml2" --strip-components=1 && \ + cd libxml2 && \ + CC=aarch64-linux-musl-gcc ./configure --host=aarch64-linux-musl \ + --prefix=/opt/aarch64-linux-musl-cross/aarch64-linux-musl \ + --enable-static --disable-shared --without-python --without-zlib --without-lzma && \ + make -j$(nproc) && make install && \ + cd .. && rm -rf libxml2 + +# libmnl +RUN mkdir -p libmnl && tar xjf "libmnl.tar.bz2" -C "libmnl" --strip-components=1 && \ + cd libmnl && \ + CC=aarch64-linux-musl-gcc ./configure --host=aarch64-linux-musl \ + --prefix=/opt/aarch64-linux-musl-cross/aarch64-linux-musl \ + --enable-static --disable-shared && \ + make -j$(nproc) && make install && \ + cd .. && rm -rf libmnl + +# libcli +RUN mkdir -p libcli && tar zxf "libcli.tar.gz" -C "libcli" --strip-components=1 && \ + cd libcli && \ + make CC=aarch64-linux-musl-gcc AR=aarch64-linux-musl-ar libcli.a && \ + cp libcli.h /opt/aarch64-linux-musl-cross/aarch64-linux-musl/include/ && \ + cp libcli.a /opt/aarch64-linux-musl-cross/aarch64-linux-musl/lib/ && \ + cd .. && rm -rf libcli + +# libpcap +RUN mkdir -p libpcap && tar xf "libpcap.tar.xz" -C "libpcap" --strip-components=1 && \ + cd libpcap && \ + CC=aarch64-linux-musl-gcc ./configure --host=aarch64-linux-musl \ + --prefix=/opt/aarch64-linux-musl-cross/aarch64-linux-musl \ + --with-pcap=linux --enable-static --disable-shared \ + --disable-usb --disable-netmap --disable-bluetooth --disable-dbus && \ + make -j$(nproc) && make install && \ + cd .. && rm -rf libpcap + +# fmlib, fmc +RUN mkdir fmlib && \ + tar zxf "fmlib.tar.gz" -C "fmlib" --strip-components=1 && \ + cd fmlib && git init -q && git add -A && git commit -q -m "base" && \ + git apply /src/ASK/patches/fmlib/01-mono-ask-extensions.patch && \ + make CROSS_COMPILE=aarch64-linux-musl- KERNEL_SRC=../linux libfm-arm.a && \ + ln -sf libfm-arm.a libfm.a && \ + cd ../ && \ + mkdir -p fmc && \ + tar zxf "fmc.tar.gz" -C "fmc" --strip-components=1 && \ + # Handle fmc: Initialize dummy repo, patch, and build \ + cd fmc && git init -q && git add -A && git commit -q -m "base" && \ + git apply /src/ASK/patches/fmc/01-mono-ask-extensions.patch && \ + make -C "source" CC="aarch64-linux-musl-gcc -static" CXX="aarch64-linux-musl-g++ -static" AR=aarch64-linux-musl-ar \ + MACHINE=ls1046 \ + FMD_USPACE_HEADER_PATH=../../fmlib/include/fmd \ + FMD_USPACE_LIB_PATH=../../fmlib \ + LIBXML2_HEADER_PATH=/opt/aarch64-linux-musl-cross/aarch64-linux-musl/include/libxml2 \ + TCLAP_HEADER_PATH=/opt/aarch64-linux-musl-cross/aarch64-linux-musl/include + +# Patch verdor's modules +RUN mkdir -p /src/patches/ask +COPY patches/ask/ /src/patches/ask/ + +RUN cd /src/ASK && \ + git init -q && git add -A && git commit -q -m "base" && \ + find /src/patches/ask \ + -path /src/patches/ask/upstream -prune -o \ + -name '*.patch' -print \ + | sort > /tmp/ask-module-patches.list && \ + test ! -s /tmp/ask-module-patches.list || xargs -a /tmp/ask-module-patches.list git apply --check && \ + test ! -s /tmp/ask-module-patches.list || xargs -a /tmp/ask-module-patches.list git apply + +# Build patched libnfnetlink + libnetfilter_conntrack into the musl sysroot. +# These are needed by cmm through pkg-config. +RUN mkdir -p "${ASK_DIR}/sources" && \ + mkdir -p libnfnetlink && \ + tar xjf "${ASK_DIR}/sources/tarballs/libnfnetlink-${LIBNFNETLINK_VERSION}.tar.bz2" \ + -C libnfnetlink --strip-components=1 && \ + cd libnfnetlink && \ + git init -q && git add -A && git commit -q -m "upstream" && \ + git apply "${ASK_DIR}/patches/libnfnetlink/01-nxp-ask-nonblocking-heap-buffer.patch" && \ + CC=aarch64-linux-musl-gcc AR=aarch64-linux-musl-ar RANLIB=aarch64-linux-musl-ranlib \ + ./configure --host="${HOST}" --prefix="${SYSROOT}" \ + --enable-static --disable-shared && \ + make -j$(nproc) && make install && \ + cd /src && rm -rf libnfnetlink && \ + mkdir -p libnetfilter_conntrack && \ + tar xf "${ASK_DIR}/sources/tarballs/libnetfilter_conntrack-${LIBNFCT_VERSION}.tar.xz" \ + -C libnetfilter_conntrack --strip-components=1 && \ + cd libnetfilter_conntrack && \ + cp /src/patches/ask/upstream/libnetfilter-conntrack/* "${ASK_DIR}/patches/libnetfilter-conntrack/" && \ + git init -q && git add -A && git commit -q -m "upstream" && \ + find "${ASK_DIR}/patches/libnetfilter-conntrack/" \ + -name '*.patch' -exec sha256sum {} \; && \ + find "${ASK_DIR}/patches/libnetfilter-conntrack/" \ + -name '*.patch' -print \ + | sort > /tmp/libnfct-patches.list && \ + test ! -s /tmp/libnfct-patches.list || xargs -a /tmp/libnfct-patches.list git apply --check && \ + test ! -s /tmp/libnfct-patches.list || xargs -a /tmp/libnfct-patches.list git apply && \ + PKG_CONFIG_PATH="${SYSROOT}/lib/pkgconfig" \ + CC=aarch64-linux-musl-gcc AR=aarch64-linux-musl-ar RANLIB=aarch64-linux-musl-ranlib \ + ./configure --host="${HOST}" --prefix="${SYSROOT}" \ + --enable-static --disable-shared \ + CFLAGS="-I${SYSROOT}/include" \ + LDFLAGS="-L${SYSROOT}/lib" && \ + make -j$(nproc) && make install && \ + cd /src && rm -rf libnetfilter_conntrack + +# Build libfci, used by cmm. +RUN make -C "${ASK_DIR}/fci/lib" \ + CC=aarch64-linux-musl-gcc \ + AR=aarch64-linux-musl-ar + +# DTS changes happen after the expensive kernel build layer +COPY kernel-build/dts/*.dts /src/linux/arch/arm64/boot/dts/freescale/ + +ARG DEVICE_TREE_TARGET + +RUN cd /src/linux \ + && grep -q "^dtb-\\\$(CONFIG_ARCH_LAYERSCAPE) += ${DEVICE_TREE_TARGET}.dtb$" \ + arch/arm64/boot/dts/freescale/Makefile \ + || echo "dtb-\$(CONFIG_ARCH_LAYERSCAPE) += ${DEVICE_TREE_TARGET}.dtb" \ + >> arch/arm64/boot/dts/freescale/Makefile \ + && make ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" \ + "freescale/${DEVICE_TREE_TARGET}.dtb" + +# Build out-of-tree ASK kernel modules: cdx -> fci, plus auto_bridge. +RUN make -C "${ASK_DIR}/cdx" \ + CROSS_COMPILE="${CROSS_COMPILE}" \ + ARCH="${ARCH}" \ + KERNELDIR="${KERNEL_DIR}" \ + PLATFORM="${PLATFORM}" \ + modules && \ + make -C "${ASK_DIR}/fci" \ + CROSS_COMPILE="${CROSS_COMPILE}" \ + ARCH="${ARCH}" \ + KERNEL_SOURCE="${KERNEL_DIR}" \ + BOARD_ARCH="${ARCH}" \ + KBUILD_EXTRA_SYMBOLS="${ASK_DIR}/cdx/Module.symvers" \ + modules && \ + make -C "${ASK_DIR}/auto_bridge" \ + CROSS_COMPILE="${CROSS_COMPILE}" \ + ARCH="${ARCH}" \ + KERNEL_SOURCE="${KERNEL_DIR}" \ + PLATFORM="${PLATFORM}" + +# Patch vendor's binaries +RUN sed -i '/^[[:space:]]*CFLAGS[[:space:]]*+=.*-Wall.*-Werror/a CFLAGS += -Wno-address-of-packed-member' "${ASK_DIR}/cmm/Makefile" + +# Build remaining userspace binaries: cmm and dpa_app. +RUN make -C "${ASK_DIR}/cmm" \ + CC="aarch64-linux-musl-gcc -static" \ + LIBFCI_DIR="${ASK_DIR}/fci/lib" \ + ABM_DIR="${ASK_DIR}/auto_bridge" \ + SYSROOT="${SYSROOT}" \ + PKG_CONFIG_PATH="${SYSROOT}/lib/pkgconfig" && \ + make -C "${ASK_DIR}/dpa_app" \ + CC="aarch64-linux-musl-gcc -static" \ + CFLAGS="-DDPAA_DEBUG_ENABLE -DLS1043 -DNCSW_LINUX -D__STDC_LIMIT_MACROS \ + -I/src/fmc/source \ + -I${ASK_DIR}/cdx \ + -I/src/fmlib/include/fmd \ + -I/src/fmlib/include/fmd/Peripherals \ + -I/src/fmlib/include/fmd/integrations" \ + LDFLAGS="-static -L/src/fmc/source -L/src/fmlib -L${SYSROOT}/lib \ + -lfmc -lfm -lcli -lxml2 -lstdc++ -lpthread -lm" + +# Stage outputs +RUN mkdir -p /out/ASK/dist && \ + cp "${ASK_DIR}/cdx/cdx.ko" /out/ASK/dist && \ + cp "${ASK_DIR}/fci/fci.ko" /out/ASK/dist && \ + cp "${ASK_DIR}/auto_bridge/auto_bridge.ko" /out/ASK/dist && \ + mkdir -p /out/ASK/bin && \ + cp /src/fmc/source/fmc /out/ASK/bin/ && \ + cp "${ASK_DIR}/cmm/src/cmm" /out/ASK/bin/ && \ + cp "${ASK_DIR}/dpa_app/dpa_app" /out/ASK/bin/ +# aarch64-linux-musl-strip /out/ASK/bin/fmc /out/ASK/bin/cmm /out/ASK/bin/dpa_app + +# in-tree Linux kernel modules +RUN mkdir -p /out/rootfs && \ + make -C /src/linux ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \ + modules_install INSTALL_MOD_PATH=/out/rootfs + +RUN KERNEL_VER=$(ls /out/rootfs/lib/modules/) && \ + mkdir -p /out/rootfs/lib/modules/$KERNEL_VER/extra && \ + mv /out/ASK/dist/*.ko /out/rootfs/lib/modules/$KERNEL_VER/extra/ && \ + depmod -b /out/rootfs $KERNEL_VER && \ + cd /out && tar zcf rootfs.tar.gz rootfs + +RUN mkdir -p /out/rootfs-cfg/etc/dpa && \ + cp -r "${ASK_DIR}/dpa_app/files/etc/"* /out/rootfs-cfg/etc/dpa/ && \ + cp "${ASK_DIR}/config/gateway-dk/cdx_cfg.xml" /out/rootfs-cfg/etc/dpa/ && \ + cp -r /src/fmc/etc/* /out/rootfs-cfg/etc && \ + mkdir -p /out/rootfs-cfg/etc/cmm && \ + cp "${ASK_DIR}/config/fastforward" /out/rootfs-cfg/etc/cmm/fastforward.vendor.orig + +FROM scratch AS export + +ARG DEVICE_TREE_TARGET + +# Export the newly staged in-tree modules +COPY --from=build /out/rootfs.tar.gz / +COPY --from=build \ + /src/linux/System.map \ + /src/linux/.config \ + /src/linux/arch/arm64/boot/Image.gz \ + /src/linux/arch/arm64/boot/dts/freescale/${DEVICE_TREE_TARGET}.dtb \ + /kernel/ + +COPY --from=build /out/ASK/bin/ /bin/ + +# Export the configs for Gateway Development Kit +COPY --from=build /out/rootfs-cfg/ /rootfs-cfg/ diff --git a/docker/cmm.Dockerfile b/docker/cmm.Dockerfile new file mode 100644 index 0000000..498c094 --- /dev/null +++ b/docker/cmm.Dockerfile @@ -0,0 +1,13 @@ +ARG ALPINE_SERIES=3.23 +FROM alpine:${ALPINE_SERIES} + +COPY ./out/ASK/rootfs-cfg/etc /etc +COPY ./out/ASK/bin/cmm /bin/cmm +COPY ./out/ASK/bin/dpa_app /bin/dpa_app +COPY ./ask/cmm/cmm.conf /etc/cmm/cmm.conf +COPY ./ask/cmm/entrypoint.sh /entrypoint.sh +COPY ./ask/cmm/init_dpa.sh /init_dpa.sh + +RUN chmod +x /bin/cmm /bin/dpa_app /init_dpa.sh /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/download-packages.Dockerfile b/docker/download-packages.Dockerfile index d423894..964e10b 100644 --- a/docker/download-packages.Dockerfile +++ b/docker/download-packages.Dockerfile @@ -1,13 +1,24 @@ +ARG DEP_PKG_MIRROR= +ARG DEP_PKG_OFFLINE=0 + FROM alpine:3.23.0 AS base +ARG DEP_PKG_MIRROR +ARG DEP_PKG_OFFLINE +ENV DEP_PKG_MIRROR="${DEP_PKG_MIRROR}" +ENV DEP_PKG_OFFLINE="${DEP_PKG_OFFLINE}" RUN apk add --no-cache curl ca-certificates +COPY scripts/fetch-artifact /usr/local/bin/fetch-artifact + # ---- kubelet ---- FROM base AS kubelet ARG KUBE_VERSION ARG ARCH WORKDIR /out/kubernetes -RUN curl -fL --retry 3 -o "kubelet-${KUBE_VERSION}" \ - "https://dl.k8s.io/${KUBE_VERSION}/bin/linux/${ARCH}/kubelet" && \ +RUN fetch-artifact \ + "packages/kubernetes/kubelet-${KUBE_VERSION}" \ + "kubelet-${KUBE_VERSION}" \ + "https://dl.k8s.io/${KUBE_VERSION}/bin/linux/${ARCH}/kubelet" && \ chmod +x "kubelet-${KUBE_VERSION}" # ---- kubeadm ---- @@ -15,8 +26,10 @@ FROM base AS kubeadm ARG KUBE_VERSION ARG ARCH WORKDIR /out/kubernetes -RUN curl -fL --retry 3 -o "kubeadm-${KUBE_VERSION}" \ - "https://dl.k8s.io/${KUBE_VERSION}/bin/linux/${ARCH}/kubeadm" && \ +RUN fetch-artifact \ + "packages/kubernetes/kubeadm-${KUBE_VERSION}" \ + "kubeadm-${KUBE_VERSION}" \ + "https://dl.k8s.io/${KUBE_VERSION}/bin/linux/${ARCH}/kubeadm" && \ chmod +x "kubeadm-${KUBE_VERSION}" # ---- kubectl ---- @@ -24,74 +37,190 @@ FROM base AS kubectl ARG KUBE_VERSION ARG ARCH WORKDIR /out/kubernetes -RUN curl -fL --retry 3 -o "kubectl-${KUBE_VERSION}" \ - "https://dl.k8s.io/${KUBE_VERSION}/bin/linux/${ARCH}/kubectl" && \ +RUN fetch-artifact \ + "packages/kubernetes/kubectl-${KUBE_VERSION}" \ + "kubectl-${KUBE_VERSION}" \ + "https://dl.k8s.io/${KUBE_VERSION}/bin/linux/${ARCH}/kubectl" && \ chmod +x "kubectl-${KUBE_VERSION}" # ---- busybox ---- FROM base AS busybox ARG BUSYBOX_VERSION +ARG BUSYBOX_TAR WORKDIR /out -RUN curl -fL --retry 3 -o "busybox-${BUSYBOX_VERSION}.tar.gz" \ - "https://github.com/mirror/busybox/archive/refs/tags/${BUSYBOX_VERSION}.tar.gz" +RUN fetch-artifact \ + "${BUSYBOX_TAR}" \ + "busybox-${BUSYBOX_VERSION}.tar.gz" \ + "https://github.com/mirror/busybox/archive/refs/tags/${BUSYBOX_VERSION}.tar.gz" # ---- e2fsprogs ---- FROM base AS e2fsprogs ARG E2FSPROGS_VERSION +ARG E2FSPROGS_TAR WORKDIR /out -RUN curl -fL --retry 3 -o "e2fsprogs-v${E2FSPROGS_VERSION}.tar.gz" \ - "https://github.com/tytso/e2fsprogs/archive/refs/tags/v${E2FSPROGS_VERSION}.tar.gz" +RUN fetch-artifact \ + "${E2FSPROGS_TAR}" \ + "e2fsprogs-v${E2FSPROGS_VERSION}.tar.gz" \ + "https://github.com/tytso/e2fsprogs/archive/refs/tags/v${E2FSPROGS_VERSION}.tar.gz" # ---- dpdk ---- FROM base AS dpdk ARG DPDK_VERSION +ARG DPDK_TAR WORKDIR /out/nxp/dpdk -RUN curl -fL --retry 3 -o "${DPDK_VERSION}.tar.gz" \ - "https://github.com/nxp-qoriq/dpdk/archive/refs/tags/${DPDK_VERSION}.tar.gz" +RUN fetch-artifact \ + "${DPDK_TAR}" \ + "${DPDK_VERSION}.tar.gz" \ + "https://github.com/nxp-qoriq/dpdk/archive/refs/tags/${DPDK_VERSION}.tar.gz" # ---- fmlib ---- FROM base AS fmlib ARG FMLIB_VERSION +ARG FMLIB_TAR WORKDIR /out/nxp/fmlib -RUN curl -fL --retry 3 -o "${FMLIB_VERSION}.tar.gz" \ - "https://github.com/nxp-qoriq/fmlib/archive/refs/tags/${FMLIB_VERSION}.tar.gz" +RUN fetch-artifact \ + "${FMLIB_TAR}" \ + "${FMLIB_VERSION}.tar.gz" \ + "https://github.com/nxp-qoriq/fmlib/archive/refs/tags/${FMLIB_VERSION}.tar.gz" # ---- fmc ---- FROM base AS fmc ARG FMC_VERSION +ARG FMC_TAR WORKDIR /out/nxp/fmc -RUN curl -fL --retry 3 -o "${FMC_VERSION}.tar.gz" \ - "https://github.com/nxp-qoriq/fmc/archive/refs/tags/${FMC_VERSION}.tar.gz" +RUN fetch-artifact \ + "${FMC_TAR}" \ + "${FMC_VERSION}.tar.gz" \ + "https://github.com/nxp-qoriq/fmc/archive/refs/tags/${FMC_VERSION}.tar.gz" # ---- vpp ---- FROM base AS vpp ARG VPP_VERSION +ARG VPP_TAR WORKDIR /out/nxp/vpp -RUN curl -fL --retry 3 -o "${VPP_VERSION}.tar.gz" \ - "https://github.com/nxp-qoriq/vpp/archive/refs/tags/${VPP_VERSION}.tar.gz" +RUN fetch-artifact \ + "${VPP_TAR}" \ + "${VPP_VERSION}.tar.gz" \ + "https://github.com/nxp-qoriq/vpp/archive/refs/tags/${VPP_VERSION}.tar.gz" + +# ---- MUSL CC ---- +FROM base AS aarch64_musl_cc +WORKDIR /out +RUN fetch-artifact \ + "packages/aarch64-linux-musl-cross.tgz" \ + "aarch64-linux-musl-cross.tgz" \ + "https://musl.cc/aarch64-linux-musl-cross.tgz" + +# ---- ASK ---- +FROM base AS mono_ask +ARG MONO_ASK_VERSION +ARG MONO_ASK_TAR +WORKDIR /out/ask +RUN fetch-artifact \ + "${MONO_ASK_TAR}" \ + "${MONO_ASK_VERSION}.tar.gz" \ + "https://github.com/we-are-mono/ASK/archive/refs/tags/${MONO_ASK_VERSION}.tar.gz" + +# ---- libnfnetlink ---- +FROM base AS libnfnetlink +ARG LIBNFNETLINK_VERSION +ARG LIBNFNETLINK_TAR +WORKDIR /out/ask/libnfnetlink +RUN fetch-artifact \ + "${LIBNFNETLINK_TAR}" \ + "${LIBNFNETLINK_VERSION}.tar.bz2" \ + "https://www.netfilter.org/projects/libnfnetlink/files/libnfnetlink-${LIBNFNETLINK_VERSION}.tar.bz2" + +# ---- libnfct ---- +FROM base AS libnfct +ARG LIBNFCT_VERSION +ARG LIBNFCT_TAR +WORKDIR /out/ask/libnfct +RUN fetch-artifact \ + "${LIBNFCT_TAR}" \ + "${LIBNFCT_VERSION}.tar.xz" \ + "https://www.netfilter.org/projects/libnetfilter_conntrack/files/libnetfilter_conntrack-${LIBNFCT_VERSION}.tar.xz" + +# ---- libmnl ---- +FROM base AS libmnl +ARG LIBMNL_VERSION +ARG LIBMNL_TAR +WORKDIR /out/ask/libmnl +RUN fetch-artifact \ + "${LIBMNL_TAR}" \ + "${LIBMNL_VERSION}.tar.bz2" \ + "https://www.netfilter.org/projects/libmnl/files/libmnl-${LIBMNL_VERSION}.tar.bz2" + +# ---- tclap ---- +FROM base AS tclap +ARG TCLAP_VERSION +ARG TCLAP_TAR +WORKDIR /out/ask/tclap +RUN fetch-artifact \ + "${TCLAP_TAR}" \ + "${TCLAP_VERSION}.tar.gz" \ + "https://sourceforge.net/projects/tclap/files/tclap-${TCLAP_VERSION}.tar.gz" + +# ---- libxml2 ---- +FROM base AS libxml2 +ARG LIBXML2_VERSION +ARG LIBXML2_TAR +WORKDIR /out/ask/libxml2 +RUN fetch-artifact \ + "${LIBXML2_TAR}" \ + "${LIBXML2_VERSION}.tar.xz" \ + "https://download.gnome.org/sources/libxml2/2.11/libxml2-${LIBXML2_VERSION}.tar.xz" + +# ---- libcli ---- +FROM base AS libcli +ARG LIBCLI_VERSION +ARG LIBCLI_TAR +WORKDIR /out/ask/libcli +RUN fetch-artifact \ + "${LIBCLI_TAR}" \ + "${LIBCLI_VERSION}.tar.gz" \ + "https://github.com/dparrish/libcli/archive/refs/tags/V${LIBCLI_VERSION}.tar.gz" + +# ---- libpcap ---- +FROM base AS libpcap +ARG LIBPCAP_VERSION +ARG LIBPCAP_TAR +WORKDIR /out/ask/libpcap +RUN fetch-artifact \ + "${LIBPCAP_TAR}" \ + "${LIBPCAP_VERSION}.tar.xz" \ + "https://www.tcpdump.org/release/libpcap-${LIBPCAP_VERSION}.tar.xz" # ---- alpine rootfs ---- FROM base AS alpine_rootfs ARG ALPINE_SERIES ARG ALPINE_ARCH -ARG ALPINE_VER +ARG ALPINE_TAR WORKDIR /out -RUN curl -fL --retry 3 -o "alpine-minirootfs-${ALPINE_VER}-${ALPINE_ARCH}.tar.gz" \ - "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_SERIES}/releases/${ALPINE_ARCH}/alpine-minirootfs-${ALPINE_VER}-${ALPINE_ARCH}.tar.gz" +RUN fetch-artifact \ + "${ALPINE_TAR}" \ + "alpine-minirootfs-${ALPINE_VER}-${ALPINE_ARCH}.tar.gz" \ + "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_SERIES}/releases/${ALPINE_ARCH}/alpine-minirootfs-${ALPINE_VER}-${ALPINE_ARCH}.tar.gz" # ---- nxp linux ---- FROM base AS nxp_linux ARG NXP_VERSION +ARG NXP_TAR WORKDIR /out/nxp/kernel -RUN curl -fL --retry 3 -o "${NXP_VERSION}.tar.gz" \ - "https://github.com/nxp-qoriq/linux/archive/refs/tags/${NXP_VERSION}.tar.gz" +RUN fetch-artifact \ + "${NXP_TAR}" \ + "${NXP_VERSION}.tar.gz" \ + "https://github.com/nxp-qoriq/linux/archive/refs/tags/${NXP_VERSION}.tar.gz" # ---- crio ---- FROM base AS crio ARG CRIO_VERSION +ARG CRIO_TAR WORKDIR /out -RUN curl -fL --retry 3 -o "${CRIO_VERSION}.tar.gz" \ - "https://storage.googleapis.com/cri-o/artifacts/${CRIO_VERSION}.tar.gz" +RUN fetch-artifact \ + "${CRIO_TAR}" \ + "${CRIO_VERSION}.tar.gz" \ + "https://storage.googleapis.com/cri-o/artifacts/${CRIO_VERSION}.tar.gz" # ---- final exported artifact set ---- FROM scratch @@ -101,9 +230,18 @@ COPY --from=kubectl /out/ / COPY --from=busybox /out/ / COPY --from=e2fsprogs /out/ / COPY --from=dpdk /out/ / +COPY --from=aarch64_musl_cc /out/ / +COPY --from=mono_ask /out/ / +COPY --from=vpp /out/ / COPY --from=fmlib /out/ / COPY --from=fmc /out/ / -COPY --from=vpp /out/ / +COPY --from=libnfnetlink /out/ / +COPY --from=libnfct /out/ / +COPY --from=libmnl /out/ / +COPY --from=libcli /out/ / +COPY --from=libpcap /out/ / +COPY --from=libxml2 /out/ / +COPY --from=tclap /out/ / COPY --from=alpine_rootfs /out/ / COPY --from=nxp_linux /out/ / COPY --from=crio /out/ / diff --git a/docs/cmm.md b/docs/cmm.md new file mode 100644 index 0000000..7596d49 --- /dev/null +++ b/docs/cmm.md @@ -0,0 +1,320 @@ +# CMM integration for monok8s + +This document describes how monok8s runs the vendor Connection Manager daemon (`cmm`) from [ASK](https://github.com/we-are-mono/ASK/) on Kubernetes nodes. + +`cmm` is part of the NXP/ASK hardware-offload stack. In the vendor layout it is normally started as a boot-time service, together with the `cdx` kernel module and `dpa_app`. monok8s intentionally does **not** follow that model. Kubernetes has priority: the node should boot, kubelet should come up, CNI should configure networking, and only then should the CMM stack start from a DaemonSet. + +## Startup model + +The intended startup order is: + +1. The node boots. +2. `kubelet` starts. +3. CNI is configured. +4. The `cmm` DaemonSet starts on the node. +5. The DaemonSet prepares the DPA/CDX runtime and starts `cmm` in the foreground. + +This is different from the vendor flow, where CMM-related components are treated as host services started early during boot. That flow is a poor fit for monok8s because CNI and Kubernetes-owned networking must win any ordering conflict. + +## Local changes from vendor ASK + +monok8s carries a small set of patches so the ASK CMM stack behaves correctly inside a Kubernetes pod. + +### `cmm` + +The `cmm` daemon is patched to: + +- run in the foreground, so it can be supervised directly by Kubernetes; +- log to stdout/stderr, so logs are visible through `kubectl logs`; +- avoid exiting when it sees CNI-managed conntrack entries it does not understand. + +The last item is important. On a Kubernetes node, conntrack is not exclusively owned by CMM. CNI, kubelet, host networking, and ordinary pods can all create conntrack entries. CMM must tolerate that environment. + +### `cdx` kernel module + +The `cdx` module is patched so loading the module does **not** automatically start `dpa_app`. + +In monok8s, module loading and DPA configuration are separate steps. This avoids doing device configuration too early, before Kubernetes networking is ready. + +### `dpa_app` + +`dpa_app` is patched so the XML config paths can be supplied through environment variables: + +- `CDX_CFG_FILE` +- `CDX_PCD_FILE` +- `CDX_PDL_FILE` +- `CDX_SP_FILE` + +This lets the DaemonSet select different XML files per node, board, or port layout without rebuilding the image. + +## Patch locations + +The relevant patch sets are under: + +```text +patches/ask/upstream/libnetfilter-conntrack +patches/ask/cmm +patches/ask/cdx +patches/ask/dpa +``` + +Other ASK patches in the tree are mostly kernel-porting work for the target NXP LSDK kernel, including the 6.18-based kernel used by monok8s. + +## Installation + +CMM is **not installed by default**. Install it explicitly after the node-control components are available. + +With `MKS_ENABLE_NODE_CONTROL` enabled, generate and apply the CMM manifests with: + +```sh +kubectl -n mono-system exec -it ds/node-agent -- ctl create cmm | kubectl apply -f - +``` + +This creates the CMM DaemonSet and the supporting objects required to run it on each matching node. + +Check that the pod is running: + +```sh +kubectl -n mono-system get pods -l app.kubernetes.io/name=cmm -owide +``` + +View logs with: + +```sh +kubectl -n mono-system logs ds/cmm -f +``` + +If the DaemonSet name or labels change, inspect the generated YAML from `ctl create cmm` and use the actual object names. + +## Accessing the CMM CLI + +The CMM CLI is exposed from the pod for debugging and manual inspection. The DaemonSet uses `hostNetwork: true`, but the safest access method is still `kubectl port-forward`; it avoids exposing the CLI beyond your local machine. + +First find a CMM pod: + +```sh +kubectl -n mono-system get pods -l app.kubernetes.io/name=cmm +``` + +Then forward a local port to the CMM CLI port inside the pod. Kubernetes port-forward syntax is `LOCAL_PORT:REMOTE_PORT`. + +For example, if CMM listens on port `12345` in the pod: + +```sh +kubectl -n mono-system port-forward pod/cmm-xxxxx 12345:2103 +``` + +In another terminal, connect to the local forwarded port: + +```sh +telnet 127.0.0.1 12345 +``` + +Use `telnet` for this CLI. Plain `ncat` can show leading garbage characters or behave badly with the login prompt because the CMM CLI behaves like a telnet-style interactive console rather than a clean raw TCP protocol. + +Default login, if unchanged by the generated config, is usually: + +```text +Username: admin +Password: admin +``` + +Do not expose this port through a Service or LoadBalancer unless you have added proper access control. Treat the CMM CLI as an operator/debug interface. + +## Configuration + +`ctl create cmm` emits a default configuration suitable for the expected monok8s hardware layout. You can override the generated YAML before applying it. + +The vendor's original `fastforward` config is preserved in the image as a reference file, but monok8s uses its own runtime config. Keep those roles separate: + +- vendor reference config: useful for comparison and debugging; +- monok8s runtime config: the config actually consumed by the DaemonSet. + +A clear filename for the preserved vendor file is: + +```text +fastforward.vendor.orig +``` + +That name is less project-specific than `fastforward.ask.orig` and makes the intent obvious: it is the original vendor-provided config, not the active config. + +## Multi-node configuration + +If all nodes have the same board and port layout, one shared CMM/DPA config is enough. + +If nodes have different port layouts, use node-specific XML config. The recommended pattern is: + +1. Mount all supported configs into the CMM pod. +2. Pass the Kubernetes node name into the pod. +3. Run a small wrapper script before `dpa_app`. +4. The wrapper selects the XML files for the current node and exports the corresponding `CDX_*` environment variables. +5. The wrapper then execs the normal DPA initialization script. + +Example DaemonSet fragment: + +```yaml +spec: + template: + spec: + initContainers: + - name: dpa-app + image: localhost/monok8s/cmm:dev + imagePullPolicy: Never + command: + - /node-config/select-dpa-config.sh + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + volumeMounts: + - name: node-config + mountPath: /node-config + readOnly: true + - name: dpa-configs + mountPath: /etc/monok8s/dpa-configs + readOnly: true + volumes: + - name: node-config + configMap: + name: cmm-node-config-wrapper + defaultMode: 0755 + - name: dpa-configs + configMap: + name: cmm-dpa-configs +``` + +Example wrapper: + +```sh +#!/bin/sh +set -eu + +CONFIG_DIR="/etc/monok8s/dpa-configs/${NODE_NAME}" + +if [ ! -d "${CONFIG_DIR}" ]; then + echo "missing DPA config directory for node ${NODE_NAME}: ${CONFIG_DIR}" >&2 + exit 1 +fi + +export CDX_CFG_FILE="${CONFIG_DIR}/cdx_cfg.xml" +export CDX_PCD_FILE="${CONFIG_DIR}/cdx_pcd.xml" +export CDX_PDL_FILE="${CONFIG_DIR}/hxs_pdl_v3.xml" +export CDX_SP_FILE="${CONFIG_DIR}/cdx_sp.xml" + +exec /bin/init_dpa.sh +``` + +The ConfigMap layout should then look like this conceptually: + +```text +/etc/monok8s/dpa-configs/ + node-a/ + cdx_cfg.xml + cdx_pcd.xml + hxs_pdl_v3.xml + cdx_sp.xml + node-b/ + cdx_cfg.xml + cdx_pcd.xml + hxs_pdl_v3.xml + cdx_sp.xml +``` + +For production, prefer a naming scheme based on stable node labels or hardware profiles rather than raw node names if multiple nodes share the same layout. Raw node names are fine for early bring-up, but they do not scale well. + +## Operational notes + +### CMM and Kubernetes conntrack + +Do not assume CMM owns the full conntrack table. Kubernetes nodes contain conntrack entries from: + +- CNI traffic; +- kubelet; +- host-network pods; +- service routing; +- node-local traffic; +- ordinary workloads. + +CMM must tolerate unknown entries. If it exits because it encountered a CNI or Kubernetes conntrack entry, that is a bug in the integration layer, not an operator error. + +### `hostNetwork: true` + +The CMM pod uses `hostNetwork: true` because it needs to interact with host networking and hardware-offload state. This also means any port bound by the pod may be bound in the host network namespace. + +For the CLI, prefer `kubectl port-forward` anyway. It gives you a controlled local tunnel and avoids accidentally publishing the CLI on the node network. + +### `CMM_MAX_CONNECTIONS` + +The default value: + +```sh +CMM_MAX_CONNECTIONS="${CMM_MAX_CONNECTIONS:-131072}" +``` + +uses `131072`, which is `128 * 1024`. It is a power-of-two-sized default commonly used for connection-table limits. Treat it as a capacity default, not a magic correctness value. + +Lower it if memory pressure is a concern. Raise it only if the hardware, memory budget, and expected traffic justify it. + +## Troubleshooting + +### The CMM pod is not running + +Check the DaemonSet and pod events: + +```sh +kubectl -n mono-system get ds cmm -oyaml +kubectl -n mono-system describe pod -l app.kubernetes.io/name=cmm +``` + +Then check logs: + +```sh +kubectl -n mono-system logs ds/cmm --all-containers=true --tail=200 +``` + +### The CLI shows strange characters with `ncat` + +Use `telnet` instead: + +```sh +telnet 127.0.0.1 2103 +``` + +The CMM CLI behaves like a telnet-style console. `ncat --telnet` may still not behave exactly like traditional telnet for this CLI. + +### Port-forward connects to the wrong port + +Remember the syntax: + +```text +LOCAL_PORT:REMOTE_PORT +``` + +So this command: + +```sh +kubectl -n mono-system port-forward pod/cmm-xxxxx 12345:2103 +``` + +means: + +```text +127.0.0.1:12345 on your workstation -> port 2103 inside the pod +``` + +Connect to `127.0.0.1:12345`, not `127.0.0.1:2103`. + +### `dpa_app` uses the wrong XML files + +Confirm the environment seen by the init container or wrapper: + +```sh +kubectl -n mono-system logs pod/cmm-xxxxx -c dpa-app +``` + +The wrapper should print enough information to identify the selected config directory and XML paths. If it does not, add explicit logging before `exec /bin/init_dpa.sh`. + +## Recommended policy + +Keep CMM optional. It is hardware-specific, operationally sharp, and not required for a generic Kubernetes node. The base monok8s node should boot and join the cluster without CMM. Enable CMM only on hardware profiles where the ASK offload stack is expected and tested. diff --git a/docs/vendor-resources.md b/docs/vendor-resources.md index 41d6ee1..be822ec 100644 --- a/docs/vendor-resources.md +++ b/docs/vendor-resources.md @@ -1,3 +1,6 @@ +## ASK +The most important one is vendor's ASK. Which provides all the required resources to build the device-specific kernel + ## Updating build.env You can find the latest package versions in here * [kernel](https://github.com/nxp-qoriq/linux/archive/refs/tags/) @@ -10,3 +13,112 @@ You can find the latest package versions in here https://github.com/we-are-mono/OpenWRT-ASK/tree/mono-25.12.0-rc3/target/linux/layerscape/files/arch/arm64/boot/dts/freescale * We need both `mono-gateway-dk-sdk.dts` and `mono-gateway-dk.dts` since the sdk one includes the non-sdk one. * The actual dts being used is the `mono-gateway-dk-sdk.dts` + +## Testing dpa_app +Run this on the gateway device +```bash +while true; do nc -l -p 1234 -e sh; done +``` + +Run this script on your dev machine +```bash +#!/bin/bash +FILES=" +bin/dpa_app +rootfs-cfg/etc/dpa/cdx_cfg.xml +rootfs-cfg/etc/dpa/cdx_pcd.xml +rootfs-cfg/etc/dpa/cdx_sp.xml +rootfs-cfg/etc/dpa/cdx_cfg_ls1046_rdb.xml +rootfs-cfg/etc/fmc/config/hxs_pdl_v3.xml +rootfs-cfg/etc/fmc/config/cfgdata.xsd +rootfs-cfg/etc/fmc/config/netpcd.xsd +" + +SIZE=$( + tar -C ./out/ASK -czf - $FILES | wc -c +) + +( + echo 'set -eu' + + echo 'rm -rf /var/dpa-test' + echo 'mkdir -p /var/dpa-test/bin' + echo 'mkdir -p /etc/dpa' + echo 'mkdir -p /etc/fmc/config' + + echo 'base64 -d > /tmp/dpa-test.tar.gz <<'"'"'EOF'"'"'' + tar -C ./out/ASK -czf - $FILES | pv -s "$SIZE" | base64 + echo 'EOF' + + echo 'tar -xzf /tmp/dpa-test.tar.gz -C /var/dpa-test' + + echo 'cp /var/dpa-test/bin/dpa_app /var/dpa_app' + echo 'cp /var/dpa-test/rootfs-cfg/etc/dpa/cdx_cfg.xml /etc/dpa/cdx_cfg.xml' + echo 'cp /var/dpa-test/rootfs-cfg/etc/dpa/cdx_pcd.xml /etc/dpa/cdx_pcd.xml' + echo 'cp /var/dpa-test/rootfs-cfg/etc/dpa/cdx_sp.xml /etc/dpa/cdx_sp.xml' + echo 'cp /var/dpa-test/rootfs-cfg/etc/fmc/config/hxs_pdl_v3.xml /etc/fmc/config/hxs_pdl_v3.xml' + echo 'cp /var/dpa-test/rootfs-cfg/etc/fmc/config/cfgdata.xsd /etc/fmc/config/cfgdata.xsd' + echo 'cp /var/dpa-test/rootfs-cfg/etc/fmc/config/netpcd.xsd /etc/fmc/config/netpcd.xsd' + + echo 'chmod +x /var/dpa_app' + + echo 'export CDX_CFG_FILE=/etc/dpa/cdx_cfg.xml' + echo 'export CDX_PCD_FILE=/etc/dpa/cdx_pcd.xml' + echo 'export CDX_SP_FILE=/etc/dpa/cdx_sp.xml' + echo 'export CDX_PDL_FILE=/etc/fmc/config/hxs_pdl_v3.xml' + + echo 'echo "CDX_CFG_FILE=$CDX_CFG_FILE"' + echo 'echo "CDX_PCD_FILE=$CDX_PCD_FILE"' + echo 'echo "CDX_SP_FILE=$CDX_SP_FILE"' + echo 'echo "CDX_PDL_FILE=$CDX_PDL_FILE"' + + echo 'ls -l /var/dpa_app /etc/dpa /etc/fmc/config' + echo 'echo Running /var/dpa_app' + echo '/var/dpa_app' + echo 'echo exit=$?' +) | nc 10.0.0.10 1234 +``` + +## Testing cmm +You'll need to run dpa_app first before running this + +```base +#!/bin/bash +set -eu + +FILES=" +bin/cmm +rootfs-cfg/etc/cmm/fastforward.vendor.orig +" + +SIZE=$( + tar -C ./out/ASK -czf - $FILES | wc -c +) + +( + echo 'set -eux' + + echo 'rm -rf /var/cmm-test' + echo 'mkdir -p /var/cmm-test' + echo 'mkdir -p /var/ask/bin' + echo 'mkdir -p /var/ask/etc/cmm' + + echo 'base64 -d > /tmp/cmm-test.tar.gz <<'"'"'EOF'"'"'' + tar -C ./out/ASK -czf - $FILES | pv -s "$SIZE" | base64 + echo 'EOF' + + echo 'tar -xzf /tmp/cmm-test.tar.gz -C /var/cmm-test' + + echo 'install -m 0755 /var/cmm-test/bin/cmm /var/ask/bin/cmm' + echo 'install -m 0644 /var/cmm-test/rootfs-cfg/etc/cmm/fastforward.vendor.orig /var/ask/etc/cmm/fastforward' + + echo 'ls -l /var/ask/bin/cmm /var/ask/etc/cmm/fastforward /dev/cdx_ctrl' + echo 'ldd /var/ask/bin/cmm || true' + + echo 'test -e /sys/class/vwd/vwd0/vwd_fast_path_enable && echo 1 > /sys/class/vwd/vwd0/vwd_fast_path_enable || true' + + echo 'echo Running cmm' + echo '/var/ask/bin/cmm -D -f /var/ask/etc/cmm/fastforward -n 131072' + echo 'echo exit=$?' +) | nc 10.0.0.10 1234 +``` diff --git a/kernel-build/dts/mono-gateway-dk-sdk.dts b/kernel-build/dts/mono-gateway-dk-sdk.dts index 8270375..0c1ac11 100644 --- a/kernel-build/dts/mono-gateway-dk-sdk.dts +++ b/kernel-build/dts/mono-gateway-dk-sdk.dts @@ -263,22 +263,23 @@ * ethernet@8 = enet6 = MAC9 (10G) */ - ethernet@8 { - compatible = "fsl,dpa-ethernet-init"; - fsl,bman-buffer-pools = <&bp7 &bp8 &bp9>; - fsl,qman-frame-queues-rx = <0x5c 1 0x5d 1>; - fsl,qman-frame-queues-tx = <0x7c 1 0x7d 1>; - }; + ethernet@8 { + compatible = "fsl,dpa-ethernet"; + /delete-property/ fsl,bman-buffer-pools; + /delete-property/ fsl,qman-frame-queues-rx; + /delete-property/ fsl,qman-frame-queues-tx; + dma-coherent; + }; /* Add MAC10 - not in qoriq-dpaa-eth.dtsi */ - ethernet@9 { - compatible = "fsl,dpa-ethernet-init"; - fsl,fman-mac = <&enet7>; - fsl,bman-buffer-pools = <&bp7 &bp8 &bp9>; - fsl,qman-frame-queues-rx = <0x5e 1 0x5f 1>; - fsl,qman-frame-queues-tx = <0x7e 1 0x7f 1>; - dma-coherent; - }; + ethernet@9 { + compatible = "fsl,dpa-ethernet"; + fsl,fman-mac = <&enet7>; + /delete-property/ fsl,bman-buffer-pools; + /delete-property/ fsl,qman-frame-queues-rx; + /delete-property/ fsl,qman-frame-queues-tx; + dma-coherent; + }; }; &fman0 { @@ -336,33 +337,19 @@ }; ethernet@f0000 { + status = "okay"; /delete-property/ managed; fixed-link = <0 1 10000 0 0>; phy-connection-type = "xgmii"; }; ethernet@f2000 { + status = "okay"; /delete-property/ managed; fixed-link = <0 1 10000 0 0>; phy-connection-type = "xgmii"; }; - /* DPA Offline port bindings - required for CDX. - * Use phandles fman0_oh_0x3 (port@83000) and fman0_oh_0x4 (port@84000). - * Cell-index overridden to SDK-style (1 and 2) in port nodes above. - */ - dpa-fman0-oh@2 { - compatible = "fsl,dpa-oh"; - fsl,qman-frame-queues-oh = <0x60 0x01 0x61 0x01>; - fsl,fman-oh-port = <&fman0_oh_0x3>; - }; - - dpa-fman0-oh@3 { - compatible = "fsl,dpa-oh"; - fsl,qman-frame-queues-oh = <0x62 0x01 0x63 0x01>; - fsl,fman-oh-port = <&fman0_oh_0x4>; - }; - /* Override OH port cell-index values for SDK driver compatibility. * SDK driver expects cell-index 0 for HC (Host Command/PCD) port. * Mainline qoriq-fman3-0.dtsi uses cell-index 2-7, but SDK needs 0-5. @@ -373,8 +360,8 @@ }; port@83000 { - cell-index = <1>; compatible = "fsl,fman-port-oh"; + cell-index = <1>; }; port@84000 { @@ -568,4 +555,18 @@ &fsldpaa { dma-coherent; + /* CDX / DPA IPsec offline port */ + dpa-fman0-oh@2 { + compatible = "fsl,dpa-oh"; + fsl,qman-frame-queues-oh = <0x60 0x01 0x61 0x01>; + fsl,fman-oh-port = <&fman0_oh_0x3>; + dma-coherent; + }; + /* CDX / VWD WiFi offline port; optional */ + dpa-fman0-oh@3 { + compatible = "fsl,dpa-oh"; + fsl,qman-frame-queues-oh = <0x62 0x01 0x63 0x01>; + fsl,fman-oh-port = <&fman0_oh_0x4>; + dma-coherent; + }; }; diff --git a/kernel-build/dts/mono-gateway-dk.dts b/kernel-build/dts/mono-gateway-dk.dts index d2d7aea..df7c1ae 100644 --- a/kernel-build/dts/mono-gateway-dk.dts +++ b/kernel-build/dts/mono-gateway-dk.dts @@ -500,14 +500,14 @@ status = "okay"; }; - fm1_mac9: ethernet@f0000 { /* 10GEC1 */ + fm0_mac9: ethernet@f0000 { /* 10GEC1 */ sfp = <&sfp_xfi0>; phy-connection-type = "10gbase-r"; managed = "in-band-status"; pcs-handle-names = "xfi"; /* Match enet7 for consistency */ }; - fm1_mac10: ethernet@f2000 { /* 10GEC2 */ + fm0_mac10: ethernet@f2000 { /* 10GEC2 */ sfp = <&sfp_xfi1>; phy-connection-type = "10gbase-r"; managed = "in-band-status"; diff --git a/kernel-extra.config b/kernel-extra.config index d9d7f04..5b8ef26 100644 --- a/kernel-extra.config +++ b/kernel-extra.config @@ -247,3 +247,9 @@ CONFIG_TASK_DELAY_ACCT=y CONFIG_TASK_IO_ACCOUNTING=y CONFIG_TASKSTATS=y CONFIG_TASK_XACCT=y + +############################################################################### +# NXP +############################################################################### + +# CONFIG_CDX_BUS=y diff --git a/makefile b/makefile index 7c7187e..32500a6 100644 --- a/makefile +++ b/makefile @@ -7,16 +7,28 @@ TAG ?= dev PACKAGES_DIR := packages OUT_DIR := out -E2FSPROGS_TAR := $(PACKAGES_DIR)/e2fsprogs-$(E2FSPROGS_VERSION).tar.gz +E2FSPROGS_TAR := $(PACKAGES_DIR)/e2fsprogs-v$(E2FSPROGS_VERSION).tar.gz BUSYBOX_TAR := $(PACKAGES_DIR)/busybox-$(BUSYBOX_VERSION).tar.gz ALPINE_TAR := $(PACKAGES_DIR)/alpine-minirootfs-$(ALPINE_VER)-$(ALPINE_ARCH).tar.gz NXP_TAR := $(PACKAGES_DIR)/nxp/kernel/$(NXP_VERSION).tar.gz -FMLIB_TAR := $(PACKAGES_DIR)/nxp/fmlib/$(FMLIB_VERSION).tar.gz -FMC_TAR := $(PACKAGES_DIR)/nxp/fmc/$(FMC_VERSION).tar.gz VPP_TAR := $(PACKAGES_DIR)/nxp/vpp/$(VPP_VERSION).tar.gz DPDK_TAR := $(PACKAGES_DIR)/nxp/dpdk/$(DPDK_VERSION).tar.gz CRIO_TAR := $(PACKAGES_DIR)/$(CRIO_VERSION).tar.gz +AARCH64_MUSL_CC_TAR := $(PACKAGES_DIR)/aarch64-linux-musl-cross.tgz + +# ASK-specific +MONO_ASK_TAR := $(PACKAGES_DIR)/ask/$(MONO_ASK_VERSION).tar.gz +FMLIB_TAR := $(PACKAGES_DIR)/nxp/fmlib/$(FMLIB_VERSION).tar.gz +FMC_TAR := $(PACKAGES_DIR)/nxp/fmc/$(FMC_VERSION).tar.gz +LIBNFNETLINK_TAR := $(PACKAGES_DIR)/ask/libnfnetlink/$(LIBNFNETLINK_VERSION).tar.bz2 +LIBNFCT_TAR := $(PACKAGES_DIR)/ask/libnfct/$(LIBNFCT_VERSION).tar.xz +LIBMNL_TAR := $(PACKAGES_DIR)/ask/libmnl/$(LIBMNL_VERSION).tar.bz2 +LIBCLI_TAR := $(PACKAGES_DIR)/ask/libcli/$(LIBCLI_VERSION).tar.gz +TCLAP_TAR := $(PACKAGES_DIR)/ask/tclap/$(TCLAP_VERSION).tar.gz +LIBXML2_TAR := $(PACKAGES_DIR)/ask/libxml2/$(LIBXML2_VERSION).tar.xz +LIBPCAP_TAR := $(PACKAGES_DIR)/ask/libpcap/$(LIBPCAP_VERSION).tar.xz + # Kubernetes components KUBELET_BIN := $(PACKAGES_DIR)/kubernetes/kubelet-$(KUBE_VERSION) KUBEADM_BIN := $(PACKAGES_DIR)/kubernetes/kubeadm-$(KUBE_VERSION) @@ -93,6 +105,7 @@ RELEASE_DEPS := \ $(BOARD_ITB) \ $(CLITOOLS_BIN) \ docker/alpine.Dockerfile \ + cmm-image \ $(ALPINE_SRCS) \ build.env \ makefile @@ -115,19 +128,46 @@ $(OUT_DIR): $(DOWNLOAD_PACKAGES_STAMP): docker/download-packages.Dockerfile build.env makefile | $(PACKAGES_DIR) docker build \ -f docker/download-packages.Dockerfile \ + --build-arg DEP_PKG_MIRROR=$(DEP_PKG_MIRROR) \ --build-arg KUBE_VERSION=$(KUBE_VERSION) \ --build-arg ARCH=$(ARCH) \ --build-arg BUSYBOX_VERSION=$(BUSYBOX_VERSION) \ + --build-arg DEVICE_TREE_TARGET=$(DEVICE_TREE_TARGET) \ --build-arg E2FSPROGS_VERSION=$(E2FSPROGS_VERSION) \ --build-arg DPDK_VERSION=$(DPDK_VERSION) \ --build-arg FMLIB_VERSION=$(FMLIB_VERSION) \ --build-arg FMC_VERSION=$(FMC_VERSION) \ --build-arg VPP_VERSION=$(VPP_VERSION) \ + --build-arg MONO_ASK_VERSION=$(MONO_ASK_VERSION) \ + --build-arg LIBNFNETLINK_VERSION=$(LIBNFNETLINK_VERSION) \ + --build-arg LIBMNL_VERSION=$(LIBMNL_VERSION) \ + --build-arg LIBNFCT_VERSION=$(LIBNFCT_VERSION) \ + --build-arg LIBCLI_VERSION=$(LIBCLI_VERSION) \ + --build-arg LIBXML2_VERSION=$(LIBXML2_VERSION) \ + --build-arg LIBPCAP_VERSION=$(LIBPCAP_VERSION) \ + --build-arg TCLAP_VERSION=$(TCLAP_VERSION) \ --build-arg ALPINE_SERIES=$(ALPINE_SERIES) \ --build-arg ALPINE_ARCH=$(ALPINE_ARCH) \ --build-arg ALPINE_VER=$(ALPINE_VER) \ --build-arg NXP_VERSION=$(NXP_VERSION) \ --build-arg CRIO_VERSION=$(CRIO_VERSION) \ + --build-arg BUSYBOX_TAR=$(BUSYBOX_TAR) \ + --build-arg E2FSPROGS_TAR=$(E2FSPROGS_TAR) \ + --build-arg DPDK_TAR=$(DPDK_TAR) \ + --build-arg FMLIB_TAR=$(FMLIB_TAR) \ + --build-arg FMC_TAR=$(FMC_TAR) \ + --build-arg VPP_TAR=$(VPP_TAR) \ + --build-arg MONO_ASK_TAR=$(MONO_ASK_TAR) \ + --build-arg LIBNFNETLINK_TAR=$(LIBNFNETLINK_TAR) \ + --build-arg LIBMNL_TAR=$(LIBMNL_TAR) \ + --build-arg LIBNFCT_TAR=$(LIBNFCT_TAR) \ + --build-arg LIBCLI_TAR=$(LIBCLI_TAR) \ + --build-arg LIBXML2_TAR=$(LIBXML2_TAR) \ + --build-arg LIBPCAP_TAR=$(LIBPCAP_TAR) \ + --build-arg TCLAP_TAR=$(TCLAP_TAR) \ + --build-arg ALPINE_TAR=$(ALPINE_TAR) \ + --build-arg NXP_TAR=$(NXP_TAR) \ + --build-arg CRIO_TAR=$(CRIO_TAR) \ --output type=local,dest=./$(PACKAGES_DIR) . @touch $@ @@ -155,17 +195,11 @@ $(BUILD_BASE_STAMP): $(BUILD_BASE_DEPS) | $(OUT_DIR) docker tag monok8s/build-base:$(TAG) monok8s/build-base:$$iid; \ touch $@ -$(KERNEL_IMAGE): $(KERNEL_DEPS) $(DOWNLOAD_PACKAGES_STAMP) | $(OUT_DIR) - docker build \ - -f docker/kernel-build.Dockerfile \ - --build-arg DOCKER_IMAGE_ROOT=$(DOCKER_IMAGE_ROOT) \ - --build-arg TAG=$(TAG) \ - --build-arg ARCH=$(ARCH) \ - --build-arg CROSS_COMPILE=$(CROSS_COMPILE) \ - --build-arg NXP_VERSION=$(NXP_VERSION) \ - --build-arg DEVICE_TREE_TARGET=$(DEVICE_TREE_TARGET) \ - --output type=local,dest=./$(OUT_DIR) . - test -f $@ +$(KERNEL_IMAGE): $(KERNEL_DEPS) $(DOWNLOAD_PACKAGES_STAMP) ASK | $(OUT_DIR) + rm -f "$@" + cp $(OUT_DIR)/ASK/rootfs.tar.gz $(OUT_DIR)/rootfs.tar.gz + cp $(OUT_DIR)/ASK/kernel/* $(OUT_DIR)/ + test -f "$@" $(INITRAMFS): $(INITRAMFS_DEPS) $(DOWNLOAD_PACKAGES_STAMP) | $(OUT_DIR) docker build \ @@ -183,6 +217,49 @@ $(INITRAMFS): $(INITRAMFS_DEPS) $(DOWNLOAD_PACKAGES_STAMP) | $(OUT_DIR) $(CLITOOLS_BIN): $(CLITOOLS_SRCS) $(MAKE) -C clitools build-local VERSION="$(TAG)" +ASK: $(ASK_TAR) $(LIBNFCT_TAR) $(LIBNFNETLINK_TAR) $(TCLAP_TAR) $(LIBXML2_TAR) | $(OUT_DIR) + @echo "Building NXP ASK components and Kernel..." + rm -rf "$(OUT_DIR)/ASK" + mkdir -p "$(OUT_DIR)/ASK" + @build_base_tag=$$(docker image inspect \ + --format '{{.Id}}' \ + $(DOCKER_IMAGE_ROOT)/build-base:$(TAG) \ + | cut -d':' -f2 \ + | cut -c -8); \ + docker build \ + -f docker/ask.Dockerfile \ + --platform linux/amd64 \ + --build-arg DOCKER_IMAGE_ROOT=$(DOCKER_IMAGE_ROOT) \ + --build-arg BUILD_BASE_TAG=$$build_base_tag \ + --build-arg MONO_ASK_TAR=$(MONO_ASK_TAR) \ + --build-arg AARCH64_MUSL_CC_TAR=$(AARCH64_MUSL_CC_TAR) \ + --build-arg NXP_TAR=$(NXP_TAR) \ + --build-arg FMLIB_TAR=$(FMLIB_TAR) \ + --build-arg FMC_TAR=$(FMC_TAR) \ + --build-arg LIBNFNETLINK_TAR=$(LIBNFNETLINK_TAR) \ + --build-arg LIBMNL_TAR=$(LIBMNL_TAR) \ + --build-arg LIBNFCT_TAR=$(LIBNFCT_TAR) \ + --build-arg LIBXML2_TAR=$(LIBXML2_TAR) \ + --build-arg LIBPCAP_TAR=$(LIBPCAP_TAR) \ + --build-arg TCLAP_TAR=$(TCLAP_TAR) \ + --build-arg LIBCLI_TAR=$(LIBCLI_TAR) \ + --build-arg LIBNFNETLINK_VERSION=$(LIBNFNETLINK_VERSION) \ + --build-arg LIBNFCT_VERSION=$(LIBNFCT_VERSION) \ + --build-arg DEVICE_TREE_TARGET=$(DEVICE_TREE_TARGET) \ + --output type=local,dest=./$(OUT_DIR)/ASK . + +cmm-image: ASK + docker buildx build \ + --platform linux/arm64 \ + -f docker/cmm.Dockerfile \ + --build-arg ALPINE_SERIES=$(ALPINE_SERIES) \ + --load \ + -t localhost/monok8s/cmm:$(TAG) . + +push-cmm-image: cmm-image + docker tag -t localhost/monok8s/cmm:$(TAG) $(IMAGE_REPOSITORY)/cmm:$(KUBE_VERSION)-$(TAG) . + docker push $(IMAGE_REPOSITORY)/cmm:$(KUBE_VERSION)-$(TAG) + vpp: $(BUILD_BASE_STAMP) $(VPP_TAR) $(DPDK_TAR) $(FMLIB_TAR) $(FMC_TAR) $(NXP_TAR) @build_base_tag=$$(docker image inspect \ --format '{{.Id}}' \ @@ -273,7 +350,6 @@ $(RELEASE_IMAGE): $(RELEASE_DEPS) $(DOWNLOAD_PACKAGES_STAMP) | $(OUT_DIR) test -f $@ - # ---- config targets ------------------------------------------------------------ cluster-config: $(CLUSTER_ENV_DEFAULT) $(CLUSTER_ENV_WORK) $(SCRIPTS_DIR)/merge-env.sh | $(OUT_DIR) @@ -339,5 +415,5 @@ pkgclean: rm -rf $(PACKAGES_DIR) .PHONY: release kernel initramfs itb build-base clitools clean distclean pkgclean \ - vpp \ + vpp ASK cmm-image \ cluster-config cluster-defconfig cluster-print diff --git a/patches/ask/auto_bridge/0001-auto-bridge-adapt-timer-api-to-6.18.patch b/patches/ask/auto_bridge/0001-auto-bridge-adapt-timer-api-to-6.18.patch new file mode 100644 index 0000000..e4c6cf9 --- /dev/null +++ b/patches/ask/auto_bridge/0001-auto-bridge-adapt-timer-api-to-6.18.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: builder +Date: Sun, 10 May 2026 00:00:00 +0000 +Subject: [PATCH] auto_bridge: adapt timer API names for Linux 6.18 + +Linux 6.18 uses the renamed timer helpers. Update the vendor +module to use timer_delete() and timer_container_of(). + +--- + auto_bridge/auto_bridge.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/auto_bridge/auto_bridge.c ++++ b/auto_bridge/auto_bridge.c +@@ -243,7 +243,7 @@ + list_add(&table_entry->list_msg_to_send, &l2flow_list_msg_to_send); + work_to_do = 1; + } +- if (del_timer(&table_entry->timeout) || no_timer) ++ if (timer_delete(&table_entry->timeout) || no_timer) + __abm_go_dying(table_entry); + } + } +@@ -604,7 +604,7 @@ + ****************************************************************************/ + static void abm_death_by_timeout(struct timer_list *t) + { +- struct l2flowTable *table_entry = from_timer(table_entry, t, timeout); ++ struct l2flowTable *table_entry = timer_container_of(table_entry, t, timeout); + + spin_lock_bh(&abm_lock); + __abm_go_dying(table_entry); +@@ -671,7 +671,7 @@ + /* Flow is programmed in FPP */ + table_entry->state = L2FLOW_STATE_FF; + /* If timer already expired we'll die, it's ok though... */ +- del_timer(&table_entry->timeout); ++ timer_delete(&table_entry->timeout); + } + else if(flags & L2FLOW_DENIED){ + /* Flow is not programmed in FPP */ +@@ -735,7 +735,7 @@ + } + + /* Die soon or now */ +- if(del_timer(&table_entry->timeout) || (table_entry->state == L2FLOW_STATE_FF)) ++ if(timer_delete(&table_entry->timeout) || (table_entry->state == L2FLOW_STATE_FF)) + __abm_go_dying(table_entry); + } + else{ +@@ -1093,7 +1093,7 @@ + list_for_each_safe(entry, tmp, &l2flow_table[i]){ + table_entry = container_of(entry, struct l2flowTable, list); + table_entry->flags |= L2FLOW_FL_DEAD; +- if(del_timer(&table_entry->timeout) || table_entry->state == L2FLOW_STATE_FF) ++ if(timer_delete(&table_entry->timeout) || table_entry->state == L2FLOW_STATE_FF) + __abm_go_dying(table_entry); + } + } + +-- +2.43.0 diff --git a/patches/ask/cdx/0001-cdx-do-not-start-dpa_app-from-kernel-module.patch b/patches/ask/cdx/0001-cdx-do-not-start-dpa_app-from-kernel-module.patch new file mode 100644 index 0000000..39db4b5 --- /dev/null +++ b/patches/ask/cdx/0001-cdx-do-not-start-dpa_app-from-kernel-module.patch @@ -0,0 +1,24 @@ +From c772418b42580bcf9d9b863e742df7ae3f921176 Mon Sep 17 00:00:00 2001 +From: test +Date: Sat, 9 May 2026 16:59:25 +0000 +Subject: [PATCH 1/2] cdx: do not start dpa_app from kernel module + + +diff --git a/cdx/cdx_main.c b/cdx/cdx_main.c +index 2d7b72b..ec763cb 100644 +--- a/cdx/cdx_main.c ++++ b/cdx/cdx_main.c +@@ -8,8 +8,8 @@ + * + */ + +-//uncomment to start dpa_app from cdx module +-#define START_DPA_APP 1 ++// Start dpa_app from userspace service ordering instead of kernel module init. ++// #define START_DPA_APP 1 + + #define DEFINE_GLOBALS + #include "portdefs.h" +-- +2.47.3 + diff --git a/patches/ask/cdx/0002-cdx-do-not-fail-module-init-for-absent-optional-offl.patch b/patches/ask/cdx/0002-cdx-do-not-fail-module-init-for-absent-optional-offl.patch new file mode 100644 index 0000000..a4347b4 --- /dev/null +++ b/patches/ask/cdx/0002-cdx-do-not-fail-module-init-for-absent-optional-offl.patch @@ -0,0 +1,86 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: builder +Date: Sun, 10 May 2026 00:00:00 +0000 +Subject: [PATCH] cdx: keep optional offload init failures non-fatal + +The CDX module currently treats several board-specific acceleration paths as +mandatory. On boards without VWD/WiFi OH ports, fragmentation BMan pools, or +DPA IPsec OH ports, module init either fails or can crash before returning an +error. + +Keep the CDX core loadable when those optional resources are absent: + + - warn and continue when VWD/WiFi init fails; + - skip the vendor fragmentation module on this platform, because it can + dereference a missing BMan pool; + - warn and continue when DPA IPsec init fails; + - only initialize DPA IPsec follow-up buffer pools when IPsec init succeeds. +--- + cdx/cdx_main.c | 47 +++++++++++++++++++++-------------------------- + 1 file changed, 21 insertions(+), 26 deletions(-) + +diff --git a/cdx/cdx_main.c b/cdx/cdx_main.c +--- a/cdx/cdx_main.c ++++ b/cdx/cdx_main.c +@@ -178,31 +178,36 @@ static int __init cdx_module_init(void) + #ifdef CFG_WIFI_OFFLOAD + rc = dpaa_vwd_init(); + if (rc != 0) { +- printk("%s::vwd_driver_init failed\n", __func__); +- goto exit; ++ printk(KERN_WARNING "%s::vwd_driver_init failed rc %d; continuing without wifi offload\n", ++ __func__, rc); ++ rc = 0; + } + #endif +- // initialize global fragmentation params +- if (cdx_init_frag_module()) { +- printk("%s::cdx_init_frag_module failed\n", __func__); +- rc = -EIO; +- goto exit; +- } ++ ++ /* ++ * The vendor fragmentation path is optional for this platform and can ++ * dereference a missing BMan pool before returning an error. Do not let a ++ * missing fragmentation pool prevent the CDX core from loading. ++ */ ++ printk(KERN_WARNING "%s::skipping cdx_init_frag_module on this platform\n", ++ __func__); + + #ifdef DPA_IPSEC_OFFLOAD +- if (cdx_dpa_ipsec_init()) { +- printk("%s::dpa_ipsec start failed\n", __func__); +- goto exit; +- } +- +- if (cdx_init_scatter_gather_bpool()) { +- printk("%s::cdx_init_scatter_gather_bpool failed\n",__func__); +- rc = -ENOMEM; +- goto exit; +- } +- if (cdx_init_skb_2bfreed_bpool()) { +- printk("%s(%d) : cdx_init_skb_2bfreed_bpool failed\n", __func__,__LINE__); +- rc = -ENOMEM; +- goto exit; ++ rc = cdx_dpa_ipsec_init(); ++ if (rc) { ++ printk(KERN_WARNING "%s::dpa_ipsec start failed rc %d; continuing without DPA IPsec offload\n", ++ __func__, rc); ++ rc = 0; ++ } else { ++ if (cdx_init_scatter_gather_bpool()) { ++ printk("%s::cdx_init_scatter_gather_bpool failed\n", __func__); ++ rc = -ENOMEM; ++ goto exit; ++ } ++ if (cdx_init_skb_2bfreed_bpool()) { ++ printk("%s(%d) : cdx_init_skb_2bfreed_bpool failed\n", __func__, __LINE__); ++ rc = -ENOMEM; ++ goto exit; ++ } + } + #endif + +-- +2.45.0 diff --git a/patches/ask/cdx/0003-cdx-adapt-dpa-wifi-a050385-to-6.18.patch b/patches/ask/cdx/0003-cdx-adapt-dpa-wifi-a050385-to-6.18.patch new file mode 100644 index 0000000..f018088 --- /dev/null +++ b/patches/ask/cdx/0003-cdx-adapt-dpa-wifi-a050385-to-6.18.patch @@ -0,0 +1,76 @@ +--- a/cdx/dpa_wifi.c 2026-05-08 15:39:41.418608108 +0000 ++++ b/cdx/dpa_wifi.c 2026-05-08 15:39:41.434536563 +0000 +@@ -840,17 +840,15 @@ + /* Get a page frag to store the SGTable, or a full page if the errata + * is in place and we need to avoid crossing a 4k boundary. + */ +-#ifdef FM_ERRATUM_A050385 + if (unlikely(fm_has_errata_a050385())) { + struct page *new_page = alloc_page(GFP_ATOMIC); + + if (unlikely(!new_page)) + return -ENOMEM; + sgt_buf = page_address(new_page); +- } +- else +-#endif ++ } else { + sgt_buf = netdev_alloc_frag(priv->eth_priv->tx_headroom + sgt_size); ++ } + + if (unlikely(!sgt_buf)) { + dev_err(dpa_bp->dev, "netdev_alloc_frag() failed\n"); +@@ -1082,11 +1080,7 @@ + /* We do not support Jumbo frames on LS1043 and thus we edit + * the skb truesize only when the 4k errata is not present. + */ +-#ifdef FM_ERRATUM_A050385 + if (likely(!fm_has_errata_a050385())) { +-#else +- if (likely(!dpaa_errata_a010022)) { +-#endif + skb->truesize = SKB_TRUESIZE(dpa_fd_length(fd)); + } + } +@@ -1152,11 +1146,8 @@ + err = custom_vwd_skb_to_sg_fd(priv, skb, &fd); + INCR_PER_CPU_STAT(vap_dev->vap_stats, pkts_tx_sg); + #else +-#ifdef FM_ERRATUM_A050385 + if (unlikely(fm_has_errata_a050385()) && a050385_check_skb(skb, priv->eth_priv)) + skb_need_wa = true; +-#endif +- + + nonlinear = skb_is_nonlinear(skb); + +@@ -1217,7 +1208,6 @@ + * more fragments than we support. In this case, + * we have no choice but to linearize it ourselves. + */ +-#ifdef FM_ERRATUM_A050385 + /* No point in linearizing the skb now if we are going + * to realign and linearize it again further down due + * to the A050385 errata +@@ -1226,13 +1216,11 @@ + skb_need_wa = true; + else + err = __skb_linearize(skb); +-#endif + } + if (unlikely(!skb || err < 0)) + /* Common out-of-memory error path */ + goto skb_to_fd_failed; + +-#ifdef FM_ERRATUM_A050385 + /* Verify the skb a second time if it has been updated since + * the previous check + */ +@@ -1248,7 +1236,6 @@ + dev_kfree_skb(skb); + skb = nskb; + } +-#endif + + err = vwd_skb_to_contig_fd(priv, skb, &fd, &offset); + } diff --git a/patches/ask/cdx/0004-cdx-add-oh-port-registration-debug.patch b/patches/ask/cdx/0004-cdx-add-oh-port-registration-debug.patch new file mode 100644 index 0000000..08833f2 --- /dev/null +++ b/patches/ask/cdx/0004-cdx-add-oh-port-registration-debug.patch @@ -0,0 +1,52 @@ +--- a/cdx/devoh.c ++++ b/cdx/devoh.c +@@ -313,6 +313,7 @@ + uint32_t port_idx; + uint8_t oh_iface_name[8]=""; + ++ printk(KERN_INFO "%s::adding OH iface name=%s\n", __func__, name); + + if (sscanf(name, "dpa-fman%d-oh@%d", &fman_idx, + &port_idx) != 2) { +@@ -331,6 +332,8 @@ + DPA_ERROR("%s::oh_port_driver_get_port_info failed\n", __func__); + return FAILURE; + } ++ printk(KERN_INFO "%s::OH port info name=%s channel=%u default_fqid=0x%x err_fqid=0x%x\n", ++ __func__, name, info.channel_id, info.default_fqid, info.err_fqid); + //ethernet/physical iface type + iface_info = (struct dpa_iface_info *) + kzalloc(sizeof(struct dpa_iface_info), GFP_KERNEL); +@@ -358,6 +361,10 @@ + __func__, name); + goto err_ret; + } ++ printk(KERN_INFO "%s::CDX OH iface config found name=%s fman=%u port_idx=%u portid=%u max_dist=%u\n", ++ __func__, name, ++ iface_info->oh_info.fman_idx, iface_info->oh_info.port_idx, ++ iface_info->oh_info.portid, iface_info->oh_info.max_dist); + if (cdx_create_dir_in_procfs(&iface_info->pcd_proc_entry, oh_iface_name, PCD_DIR)) { + DPA_ERROR("%s:: create pcd proc entry failed %s\n", + __func__, name); +@@ -581,6 +588,10 @@ + #endif + } + //add fqid information into of port list ++ printk(KERN_INFO "%s::created OH FQs name=%s fman=%u port_idx=%u rx_default_fqid=0x%x rx_err_fqid=0x%x channel=0x%x\n", ++ __func__, dpa_oh_iface_info->name, iface_info->fman_idx, ++ iface_info->port_idx, iface_info->fqinfo[RX_DEFA_FQ].fq_base, ++ iface_info->fqinfo[RX_ERR_FQ].fq_base, iface_info->channel_id); + port_info->fm_idx = iface_info->fman_idx; + port_info->ohinfo = iface_info; + port_info->channel = iface_info->channel_id; +@@ -600,6 +611,10 @@ + } + offline_port_info[iface_info->fman_idx][iface_info->port_idx].flags |= + (OF_FQID_VALID | PORT_VALID); ++ printk(KERN_INFO "%s::OH port registered name=%s fman=%u port_idx=%u flags=0x%x\n", ++ __func__, port_info->name, iface_info->fman_idx, ++ iface_info->port_idx, ++ offline_port_info[iface_info->fman_idx][iface_info->port_idx].flags); + return 0; + } + diff --git a/patches/ask/cmm/0001-cmm-add-foreground-mode.patch b/patches/ask/cmm/0001-cmm-add-foreground-mode.patch new file mode 100644 index 0000000..ab59ddd --- /dev/null +++ b/patches/ask/cmm/0001-cmm-add-foreground-mode.patch @@ -0,0 +1,75 @@ +From 7b6ff0e4a7b5e7d422c787d55225ecaa32afc8e4 Mon Sep 17 00:00:00 2001 +From: Mono +Date: Sun, 10 May 2026 15:53:21 +0000 +Subject: [PATCH] cmm: add foreground mode + +--- + cmm/src/cmm.c | 24 +++++++++++++++++------- + 1 file changed, 17 insertions(+), 7 deletions(-) + +diff --git a/cmm/src/cmm.c b/cmm/src/cmm.c +index 6452476..1bbc73e 100644 +--- a/cmm/src/cmm.c ++++ b/cmm/src/cmm.c +@@ -339,6 +339,7 @@ int main (int argc, char ** argv) + struct sigaction action; + int option,ii; + char *buf; ++ int foreground = 0; + int ret = 0; + int ch; + +@@ -402,7 +403,7 @@ int main (int argc, char ** argv) + } + + // Analyse the command line +- while ((option = getopt(argc, argv, "c:f:n:hv")) != -1) ++ while ((option = getopt(argc, argv, "c:f:n:hvD")) != -1) + { + switch (option) + { +@@ -424,6 +425,10 @@ int main (int argc, char ** argv) + } + break; + ++ case 'D': // Do not daemonize; run in foreground ++ foreground = 1; ++ break; ++ + case 'h': // Print help + cmmHelp(); + return 0; +@@ -443,9 +448,11 @@ int main (int argc, char ** argv) + goto err0; + } + +- // Daemonize the application +- if(daemon(0, 1) == -1) +- goto err0; ++ // Daemonize the application unless foreground mode was requested ++ if (!foreground) { ++ if(daemon(0, 1) == -1) ++ goto err0; ++ } + //Ensure clean termination + action.sa_handler = sig_term_hdlr; + sigemptyset(&action.sa_mask); +@@ -471,9 +478,12 @@ int main (int argc, char ** argv) + //schedParams.sched_priority = 99; + //sched_setscheduler(0, SCHED_FIFO, &schedParams); + +- //Init process does not set stdout on console +- if(freopen("/dev/console", "w", stdout) == NULL) +- goto err0; ++ // Init process does not set stdout on console. ++ // In foreground mode, keep stdout attached to the caller/container. ++ if (!foreground) { ++ if(freopen("/dev/console", "w", stdout) == NULL) ++ goto err0; ++ } + sigemptyset(&block_mask); + sigaddset(&block_mask, SIGTERM); + sigaddset(&block_mask, SIGPIPE); +-- +2.47.3 + diff --git a/patches/ask/cmm/0002-cmm-support-stdout-log-target.patch b/patches/ask/cmm/0002-cmm-support-stdout-log-target.patch new file mode 100644 index 0000000..818836b --- /dev/null +++ b/patches/ask/cmm/0002-cmm-support-stdout-log-target.patch @@ -0,0 +1,36 @@ +From 787cf734c807eecc479776ab6ac5c2c43c72e93d Mon Sep 17 00:00:00 2001 +From: Patch +Date: Sun, 10 May 2026 17:37:49 +0000 +Subject: [PATCH] cmm: support stdout log target + + +diff --git a/cmm/src/ffcontrol.c b/cmm/src/ffcontrol.c +index 4c9bdf1..b2b6b53 100644 +--- a/cmm/src/ffcontrol.c ++++ b/cmm/src/ffcontrol.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + /* bits/sockaddr.h is glibc internal, use sys/socket.h (already included) */ + #include +@@ -865,7 +866,13 @@ static int section_logging_option_hdlr(void *data, int argc, char **argv) + + if (!strcasecmp(option, "file")) + { +- globalConf.logFile = fopen(value, "a"); ++ if (!strcasecmp(value, "stdout") || !strcmp(value, "-")) ++ globalConf.logFile = fdopen(dup(STDOUT_FILENO), "a"); ++ else if (!strcasecmp(value, "stderr")) ++ globalConf.logFile = fdopen(dup(STDERR_FILENO), "a"); ++ else ++ globalConf.logFile = fopen(value, "a"); ++ + if (!globalConf.logFile) + { + cmm_print(DEBUG_CRIT, "cmmFcParser: Opening logfile %s returned error %s.\n", value, strerror(errno)); +-- +2.47.3 + diff --git a/patches/ask/cmm/0003-cmm-ignore-non-fastpath-conntracks.patch b/patches/ask/cmm/0003-cmm-ignore-non-fastpath-conntracks.patch new file mode 100644 index 0000000..65f478c --- /dev/null +++ b/patches/ask/cmm/0003-cmm-ignore-non-fastpath-conntracks.patch @@ -0,0 +1,74 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: monok8s authors +Date: Mon, 11 May 2026 00:00:00 +0000 +Subject: [PATCH] cmm: ignore conntracks without fastpath metadata + +CMM receives conntrack notifications for the whole system conntrack table. +On a Kubernetes node, many entries are unrelated to Comcerto/NXP fastpath: +loopback traffic, local control-plane traffic, Cilium traffic, broadcast, +multicast, and ordinary slow-path flows. + +Those entries do not necessarily carry the private fastpath attributes CMM +expects. Treat them as non-fastpathable instead of trying to process them. + +--- + cmm/src/ffcontrol.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/cmm/src/ffcontrol.c b/cmm/src/ffcontrol.c +--- a/cmm/src/ffcontrol.c ++++ b/cmm/src/ffcontrol.c +@@ -75,6 +75,25 @@ + return 1; + } + ++/***************************************************************** ++* cmmFcHasFastpathAttrs() ++* ++* CMM receives all conntrack notifications, including entries that ++* never passed through the Comcerto/NXP fastpath hooks. Those entries ++* do not have the private fastpath attributes needed below. Treat them ++* as ordinary slow-path conntracks and ignore them. ++******************************************************************/ ++static int cmmFcHasFastpathAttrs(struct nf_conntrack *ct) ++{ ++ if (!nfct_attr_is_set(ct, ATTR_ORIG_COMCERTO_FP_IIF)) ++ return 0; ++ ++ if (!nfct_attr_is_set(ct, ATTR_ORIG_COMCERTO_FP_IFINDEX)) ++ return 0; ++ ++ return 1; ++} ++ + /***************************************************************** + * cmmIsConntrack4Allowed() + * +@@ -92,6 +111,12 @@ + sAddr = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC); + dAddr = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC); + ++ if (!cmmFcHasFastpathAttrs(ct)) { ++ cmm_print(DEBUG_INFO, "%s: conntrack has no fastpath metadata, ignored\n", ++ __func__); ++ goto refused; ++ } ++ + /* Multicast connections are not forwarded */ + if (MULTICAST(dAddr)) { + cmm_print(DEBUG_WARNING, "%s: conntrack multicast dst:%s:%x src:%s:%x\n", __func__, +@@ -197,6 +222,12 @@ + Saddr = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC); + SaddrReply = nfct_get_attr(ct, ATTR_REPL_IPV6_SRC); + ++ if (!Saddr || !SaddrReply || !cmmFcHasFastpathAttrs(ct)) { ++ cmm_print(DEBUG_INFO, "%s: conntrack has no fastpath metadata, ignored\n", ++ __func__); ++ goto refused; ++ } ++ + if ((SaddrReply[0] & ntohl(0xff000000)) == ntohl(0xff000000)) + { + goto refused; +-- +2.45.0 \ No newline at end of file diff --git a/patches/ask/config/0001-gateway-dk-cdx-cfg-use-standard-offline-portids.patch b/patches/ask/config/0001-gateway-dk-cdx-cfg-use-standard-offline-portids.patch new file mode 100644 index 0000000..35bb9a4 --- /dev/null +++ b/patches/ask/config/0001-gateway-dk-cdx-cfg-use-standard-offline-portids.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: builder +Date: Sun, 10 May 2026 00:00:00 +0000 +Subject: [PATCH] gateway-dk: use standard CDX offline port IDs + +The CDX soft-parser logic treats logical port IDs >= 9 as offline +ports, and the bundled CDX configs consistently use portid 9 and 10 +for dpa-fman0-oh@2 and dpa-fman0-oh@3. + +Keep the Gateway DK OFFLINE port numbers unchanged, because number 1 +and 2 are what dpa_app maps to dpa-fman0-oh@2 and dpa-fman0-oh@3. +Only adjust the logical port IDs. +--- + config/gateway-dk/cdx_cfg.xml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/config/gateway-dk/cdx_cfg.xml b/config/gateway-dk/cdx_cfg.xml +index 1111111..2222222 100644 +--- a/config/gateway-dk/cdx_cfg.xml ++++ b/config/gateway-dk/cdx_cfg.xml +@@ -11,8 +11,8 @@ + + + +- +- ++ ++ + + + +-- +2.43.0 diff --git a/patches/ask/dpa/0001-dpa-app-allow-xml-config-path-env-overrides.patch b/patches/ask/dpa/0001-dpa-app-allow-xml-config-path-env-overrides.patch new file mode 100644 index 0000000..786f022 --- /dev/null +++ b/patches/ask/dpa/0001-dpa-app-allow-xml-config-path-env-overrides.patch @@ -0,0 +1,72 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: monok8s +Date: Sun, 10 May 2026 00:00:00 +0000 +Subject: [PATCH 1/4] dpa_app: allow XML config paths to be overridden by env + +Keep the vendor default XML paths, but allow deployments to override them +without patching the binary or placing board-specific XML files directly +under /etc. + +Supported environment variables: + + CDX_CFG_FILE + CDX_PCD_FILE + CDX_PDL_FILE + CDX_SP_FILE + +This is useful for monok8s/OpenRC integration where board-specific DPA +configuration can live under a managed config directory. +--- + dpa_app/dpa.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/dpa_app/dpa.c b/dpa_app/dpa.c +index 91ca1d4..960afcd 100644 +--- a/dpa_app/dpa.c ++++ b/dpa_app/dpa.c +@@ -91,6 +91,34 @@ char *pcd_file = DEFAULT_PCD_FILE; + char *pdl_file = DEFAULT_PDL_FILE; + char *sp_file = DEFAULT_SP_FILE; + ++static void dpa_load_env_paths(void) ++{ ++ char *v; ++ ++ v = getenv("CDX_CFG_FILE"); ++ if (v && *v) ++ cfg_file = v; ++ ++ v = getenv("CDX_PCD_FILE"); ++ if (v && *v) ++ pcd_file = v; ++ ++ v = getenv("CDX_PDL_FILE"); ++ if (v && *v) ++ pdl_file = v; ++ ++ v = getenv("CDX_SP_FILE"); ++ if (v && *v) ++ sp_file = v; ++ ++#ifdef DPA_C_DEBUG ++ printf("%s::cfg_file %s\n", __func__, cfg_file); ++ printf("%s::pcd_file %s\n", __func__, pcd_file); ++ printf("%s::pdl_file %s\n", __func__, pdl_file); ++ printf("%s::sp_file %s\n", __func__, sp_file); ++#endif ++} ++ + //fmc model from xml files + static struct fmc_model_t cmodel; + +@@ -752,6 +780,8 @@ int dpa_init(void) + char devname[64]; + int retval; + ++ dpa_load_env_paths(); ++ + //open cdx control device + sprintf(devname, "/dev/%s", CDX_CTRL_CDEVNAME); + cdx_dev_handle = open(devname, O_RDWR); +-- +2.39.5 diff --git a/patches/ask/dpa/0002-cdx-harden-fman-port-lookup.patch b/patches/ask/dpa/0002-cdx-harden-fman-port-lookup.patch new file mode 100644 index 0000000..9cede6a --- /dev/null +++ b/patches/ask/dpa/0002-cdx-harden-fman-port-lookup.patch @@ -0,0 +1,152 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: monok8s +Date: Sun, 10 May 2026 00:00:00 +0000 +Subject: [PATCH 2/4] cdx: harden FMAN port lookup + +The DPA userspace loader passes FMC-derived port metadata into the CDX +ioctl. If an Ethernet netdev has a partially initialized DPAA private +structure, find_osdev_by_fman_params() can dereference a missing mac_dev +or FM wrapper pointer while trying to match the FMC port. + +Add lookup diagnostics and hard NULL guards so a mismatched board +model returns -ENODEV instead of oopsing the kernel. +--- + cdx/devman.c | 85 +++++++++++++++++++++++++++++++++++++-------------- + cdx/dpa_cfg.c | 18 ++++++++--- + 2 files changed, 76 insertions(+), 27 deletions(-) + +diff -urN a/cdx/devman.c b/cdx/devman.c +--- a/cdx/devman.c 2026-05-10 00:32:28.745375897 +0000 ++++ b/cdx/devman.c 2026-05-10 00:32:29.834300280 +0000 +@@ -396,39 +396,74 @@ + uint32_t speed) + { + struct net_device *device; +- struct dpa_priv_s *priv; +- struct mac_device *macdev; ++ ++ DPA_INFO("%s::lookup fm %u port %u speed %uG\n", ++ __func__, fm_idx, port_idx, speed); + + device = first_net_device(&init_net); +- while(1) { +- if (!device) +- break; +- if (device->type == ARPHRD_ETHER) { +- t_LnxWrpFmDev *p_LnxWrpFmDev; +- priv = netdev_priv(device); +- macdev = priv->mac_dev; +- if (macdev) { +- p_LnxWrpFmDev = (t_LnxWrpFmDev*)macdev->fm; +- if (speed == 10) { +- //10 gig interfaces upports only SUPPORTED_10000baseT_Full +- /*DGW board has 2 fixed-link interfaces +- 1 - (eth2)(xDSL)1G Fixed link interface linked to rgmii-txid +- 2 - eth5(G.fast)- 1G Fixed link interface linked to sgmii and +- connected to 10G link of the board. +- sgmii - considered as 1000baseT_Full and this has cell_index = 0*/ +- +- if ( (!macdev->fixed_link) && (macdev->if_support != SUPPORTED_10000baseT_Full) ) +- goto next_device; +- } +- if ((fm_idx == p_LnxWrpFmDev->id) && +- (port_idx == macdev->cell_index)) +- return device; +- } ++ while (device) { ++ struct dpa_priv_s *priv; ++ struct mac_device *macdev; ++ t_LnxWrpFmDev *p_LnxWrpFmDev; ++ ++ if (device->type != ARPHRD_ETHER) ++ goto next_device; ++ ++ priv = netdev_priv(device); ++ if (!priv) { ++ DPA_INFO("%s::skip %s: null private data\n", ++ __func__, device->name); ++ goto next_device; ++ } ++ ++ macdev = priv->mac_dev; ++ if (!macdev) { ++ DPA_INFO("%s::skip %s: null mac_dev\n", ++ __func__, device->name); ++ goto next_device; ++ } ++ ++ if (!macdev->fm) { ++ DPA_INFO("%s::skip %s: null mac_dev->fm cell_index %u max_speed %u fixed_link %u if_support 0x%x\n", ++ __func__, device->name, macdev->cell_index, ++ macdev->max_speed, macdev->fixed_link, ++ macdev->if_support); ++ goto next_device; + } ++ ++ p_LnxWrpFmDev = (t_LnxWrpFmDev *)macdev->fm; ++ DPA_INFO("%s::candidate %s fm %u cell_index %u max_speed %u fixed_link %u if_support 0x%x\n", ++ __func__, device->name, p_LnxWrpFmDev->id, ++ macdev->cell_index, macdev->max_speed, ++ macdev->fixed_link, macdev->if_support); ++ ++ if (speed == 10) { ++ //10 gig interfaces upports only SUPPORTED_10000baseT_Full ++ /*DGW board has 2 fixed-link interfaces ++ 1 - (eth2)(xDSL)1G Fixed link interface linked to rgmii-txid ++ 2 - eth5(G.fast)- 1G Fixed link interface linked to sgmii and ++ connected to 10G link of the board. ++ sgmii - considered as 1000baseT_Full and this has cell_index = 0*/ ++ ++ if ((!macdev->fixed_link) && ++ (macdev->if_support != SUPPORTED_10000baseT_Full)) ++ goto next_device; ++ } ++ ++ if ((fm_idx == p_LnxWrpFmDev->id) && ++ (port_idx == macdev->cell_index)) { ++ DPA_INFO("%s::matched %s for fm %u port %u speed %uG\n", ++ __func__, device->name, fm_idx, port_idx, speed); ++ return device; ++ } ++ + next_device: + device = next_net_device(device); + } +- return device; ++ ++ DPA_ERROR("%s::no OS device found for fm %u port %u speed %uG\n", ++ __func__, fm_idx, port_idx, speed); ++ return NULL; + } + + +diff -urN a/cdx/dpa_cfg.c b/cdx/dpa_cfg.c +--- a/cdx/dpa_cfg.c 2026-05-10 00:32:28.757992164 +0000 ++++ b/cdx/dpa_cfg.c 2026-05-10 00:32:51.954850425 +0000 +@@ -301,15 +301,21 @@ + struct net_device *dev; + + if (port_info->type) { ++ DPA_INFO("%s::mapping user port %s fm %u index %u portid %u type %uG\n", ++ __func__, port_info->name, port_info->fm_index, ++ port_info->index, port_info->portid, port_info->type); + dev = find_osdev_by_fman_params(port_info->fm_index, + port_info->index, port_info->type); + if (!dev) { +- DPA_ERROR("%s::could not map port %s\n", +- __func__, port_info->name); +- return -EIO; +- } else { +- strcpy(port_info->name, dev->name); ++ DPA_ERROR("%s::could not map port %s fm %u index %u portid %u type %uG\n", ++ __func__, port_info->name, ++ port_info->fm_index, port_info->index, ++ port_info->portid, port_info->type); ++ return -ENODEV; + } ++ DPA_INFO("%s::mapped user port %s to netdev %s\n", ++ __func__, port_info->name, dev->name); ++ strscpy(port_info->name, dev->name, sizeof(port_info->name)); + } + #ifdef DPA_CFG_DEBUG + DPA_INFO("%s::port %s, fmindex %d, port index %d, port id %d\n", diff --git a/patches/ask/dpa/0003-cdx-avoid-kfree-of-userspace-dist-info-pointers.patch b/patches/ask/dpa/0003-cdx-avoid-kfree-of-userspace-dist-info-pointers.patch new file mode 100644 index 0000000..c6b0f5d --- /dev/null +++ b/patches/ask/dpa/0003-cdx-avoid-kfree-of-userspace-dist-info-pointers.patch @@ -0,0 +1,120 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: monok8s +Date: Sun, 10 May 2026 00:00:00 +0000 +Subject: [PATCH 3/4] cdx: avoid kfree of userspace dist_info pointers + +get_port_info() copies an array of cdx_port_info from userspace. At that +point each cdx_port_info.dist_info field is still a userspace pointer. +However release_cfg_info() treats any non-NULL dist_info as a kernel +allocation and kfree()s it on error paths. + +If a later step fails before get_dist_info() has replaced every dist_info +with a kernel allocation, release_cfg_info() can kfree a raw userspace +pointer and oops in kfree()/virt_to_folio(). + +Stash the userspace dist_info pointers in a temporary array, clear the +kernel-side cdx_port_info.dist_info fields immediately after copy_from_user(), +and pass the saved userspace pointer explicitly to get_dist_info(). This +keeps release_cfg_info() safe on partial-initialization failures. +--- + cdx/dpa_cfg.c | 47 +++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 37 insertions(+), 10 deletions(-) + +diff -urN a/cdx/dpa_cfg.c b/cdx/dpa_cfg.c +--- a/cdx/dpa_cfg.c 2026-05-10 00:46:34.295813594 +0000 ++++ b/cdx/dpa_cfg.c 2026-05-10 00:46:35.558487337 +0000 +@@ -169,11 +169,10 @@ + } + + //allocate and copy distribution info from uspace +-static int get_dist_info(struct cdx_port_info *port_info) ++static int get_dist_info(struct cdx_port_info *port_info, void *uspace_info) + { + uint32_t mem_size; + struct cdx_dist_info *dist_info; +- void *uspace_info; + + #ifdef DPA_CFG_DEBUG + DPA_INFO("%s::port %s dist %d\n", __func__, +@@ -187,7 +186,6 @@ + return -ENOMEM; + } + memset(dist_info, 0, mem_size); +- uspace_info = port_info->dist_info; + port_info->dist_info = dist_info; + if (copy_from_user(dist_info, uspace_info, + mem_size)) { +@@ -273,6 +271,7 @@ + { + struct cdx_port_info *port_info; + void *uspace_info; ++ void **uspace_dist_info; + uint32_t mem_size; + uint32_t ii; + +@@ -289,13 +288,40 @@ + return -ENOMEM; + } + memset(port_info, 0, mem_size); ++ ++ uspace_dist_info = kcalloc(finfo->max_ports, sizeof(*uspace_dist_info), ++ GFP_KERNEL); ++ if (!uspace_dist_info) { ++ DPA_ERROR("%s::memalloc for uspace_dist_info failed\n", ++ __func__); ++ kfree(port_info); ++ return -ENOMEM; ++ } ++ + uspace_info = finfo->portinfo; + finfo->portinfo = port_info; + if (copy_from_user(port_info, uspace_info, mem_size)) { + DPA_ERROR("%s::Read port_info failed\n", + __func__); ++ finfo->portinfo = NULL; ++ kfree(uspace_dist_info); ++ kfree(port_info); + return -EIO; + } ++ ++ /* ++ * port_info has just been copied from userspace, so each dist_info ++ * member is still a userspace pointer. release_cfg_info() kfree()s ++ * non-NULL dist_info members, therefore keeping those raw userspace ++ * pointers in the kernel copy turns any later error path into an ++ * invalid kfree(). Stash the userspace pointers separately and clear ++ * the struct fields until get_dist_info() replaces them with real ++ * kernel allocations. ++ */ ++ for (ii = 0; ii < finfo->max_ports; ii++) { ++ uspace_dist_info[ii] = port_info[ii].dist_info; ++ port_info[ii].dist_info = NULL; ++ } + //put the linux name for the port + for (ii = 0; ii < finfo->max_ports; ii++) { + struct net_device *dev; +@@ -311,6 +337,7 @@ + __func__, port_info->name, + port_info->fm_index, port_info->index, + port_info->portid, port_info->type); ++ kfree(uspace_dist_info); + return -ENODEV; + } + DPA_INFO("%s::mapped user port %s to netdev %s\n", +@@ -330,11 +357,14 @@ + for (ii = 0; ii < finfo->max_ports; ii++) { + int retval; + //get dist info for this port +- retval = get_dist_info(port_info); +- if (retval) ++ retval = get_dist_info(port_info, uspace_dist_info[ii]); ++ if (retval) { ++ kfree(uspace_dist_info); + return retval; ++ } + port_info++; + } ++ kfree(uspace_dist_info); + return 0; + } + diff --git a/patches/ask/dpa/0004-cdx-stash-userspace-fman-nested-pointers.patch b/patches/ask/dpa/0004-cdx-stash-userspace-fman-nested-pointers.patch new file mode 100644 index 0000000..2344959 --- /dev/null +++ b/patches/ask/dpa/0004-cdx-stash-userspace-fman-nested-pointers.patch @@ -0,0 +1,205 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: monok8s +Date: Sun, 10 May 2026 00:00:00 +0000 +Subject: [PATCH 4/4] cdx: stash userspace fman nested pointers before cleanup + +cdx_ioc_set_dpa_params() copies struct cdx_fman_info from userspace. +The nested portinfo and tbl_info members are userspace pointers at that +point, but release_cfg_info() treats non-NULL nested pointers as +kernel-owned allocations. + +If setup fails before get_port_info() or get_cctbl_info() replaces those +members with kernel allocations, the error path can kfree a userspace +address and panic in kfree()/virt_to_folio(). + +Stash the userspace pointers in temporary arrays, clear the nested +members in fman_info immediately, and pass the stashed pointers into the +copy helpers explicitly. Also route early setup failures through the same +cleanup path so partial state is released consistently. +--- +diff --git a/cdx/dpa_cfg.c b/cdx/dpa_cfg.c +index c678d5d..55b910d 100644 +--- a/cdx/dpa_cfg.c ++++ b/cdx/dpa_cfg.c +@@ -267,10 +267,9 @@ static void *get_dist_info_by_fman_params(struct cdx_fman_info *finfo, uint32_t + #endif //CDX_RTP_RELAY + + //allocate and copy port releated info from uspace +-static int get_port_info(struct cdx_fman_info *finfo) ++static int get_port_info(struct cdx_fman_info *finfo, void *uspace_info) + { + struct cdx_port_info *port_info; +- void *uspace_info; + void **uspace_dist_info; + uint32_t mem_size; + uint32_t ii; +@@ -298,7 +297,6 @@ static int get_port_info(struct cdx_fman_info *finfo) + return -ENOMEM; + } + +- uspace_info = finfo->portinfo; + finfo->portinfo = port_info; + if (copy_from_user(port_info, uspace_info, mem_size)) { + DPA_ERROR("%s::Read port_info failed\n", +@@ -355,7 +353,7 @@ static int get_port_info(struct cdx_fman_info *finfo) + } + port_info = finfo->portinfo; + for (ii = 0; ii < finfo->max_ports; ii++) { +- int retval; ++ int retval = 0; + //get dist info for this port + retval = get_dist_info(port_info, uspace_dist_info[ii]); + if (retval) { +@@ -369,11 +367,10 @@ static int get_port_info(struct cdx_fman_info *finfo) + } + + //allocate and copy cc table infor from uspace +-static int get_cctbl_info(struct cdx_fman_info *finfo) ++static int get_cctbl_info(struct cdx_fman_info *finfo, void *uspace_info) + { + struct table_info *tbl_info; + uint32_t mem_size; +- void *uspace_info; + + //allocate table information area + mem_size = (sizeof(struct table_info) * finfo->num_tables); +@@ -384,7 +381,6 @@ static int get_cctbl_info(struct cdx_fman_info *finfo) + return -ENOMEM; + } + memset(tbl_info, 0, mem_size); +- uspace_info = finfo->tbl_info; + finfo->tbl_info = tbl_info; + //copy table related info from user space + if (copy_from_user(tbl_info, (void *)uspace_info, mem_size)) { +@@ -625,9 +621,11 @@ int cdx_ioc_set_dpa_params(unsigned long args) + { + struct cdx_ctrl_set_dpa_params params; + struct cdx_fman_info *finfo; ++ void **uspace_port_info = NULL; ++ void **uspace_tbl_info = NULL; + uint32_t ii; + uint32_t mem_size; +- int retval; ++ int retval = 0; + + if (copy_from_user(¶ms, (void *)args, + sizeof(struct cdx_ctrl_set_dpa_params))) { +@@ -655,6 +653,35 @@ int cdx_ioc_set_dpa_params(unsigned long args) + retval = -EIO; + goto err_ret; + } ++ uspace_port_info = kcalloc(num_fmans, sizeof(*uspace_port_info), ++ GFP_KERNEL); ++ uspace_tbl_info = kcalloc(num_fmans, sizeof(*uspace_tbl_info), ++ GFP_KERNEL); ++ if (!uspace_port_info || !uspace_tbl_info) { ++ DPA_ERROR("%s::unable to allocate user pointer stash\n", ++ __func__); ++ for (ii = 0; ii < num_fmans; ii++) { ++ fman_info[ii].portinfo = NULL; ++ fman_info[ii].tbl_info = NULL; ++ } ++ retval = -ENOMEM; ++ goto err_ret; ++ } ++ ++ /* ++ * fman_info is copied from userspace. Its nested portinfo and ++ * tbl_info members are userspace pointers until get_port_info() and ++ * get_cctbl_info() replace them with kernel allocations. Never leave ++ * raw userspace pointers in fman_info, because release_cfg_info() owns ++ * and frees non-NULL nested pointers on error paths. ++ */ ++ for (ii = 0; ii < num_fmans; ii++) { ++ uspace_port_info[ii] = fman_info[ii].portinfo; ++ uspace_tbl_info[ii] = fman_info[ii].tbl_info; ++ fman_info[ii].portinfo = NULL; ++ fman_info[ii].tbl_info = NULL; ++ } ++ + if (copy_from_user(&ipr_info, (void *)params.ipr_info, + sizeof(struct cdx_ipr_info))) { + DPA_ERROR("%s::Read iprv_info failed\n", +@@ -665,22 +688,26 @@ int cdx_ioc_set_dpa_params(unsigned long args) + //init the fman handles + finfo = fman_info; + for (ii = 0; ii < num_fmans; ii++) { +- if (cdxdrv_get_fman_handles(finfo)) +- return -1; ++ if (cdxdrv_get_fman_handles(finfo)) { ++ retval = -EIO; ++ goto err_ret; ++ } + finfo++; + } + finfo = fman_info; + //init interface stats module +- if (cdxdrv_init_stats(finfo->muram_handle)) +- return -1; ++ if (cdxdrv_init_stats(finfo->muram_handle)) { ++ retval = -EIO; ++ goto err_ret; ++ } + + for (ii = 0; ii < num_fmans; ii++) { + //get port info +- retval = get_port_info(finfo); ++ retval = get_port_info(finfo, uspace_port_info[ii]); + if (retval) + goto err_ret; + //get cc table info +- retval = get_cctbl_info(finfo); ++ retval = get_cctbl_info(finfo, uspace_tbl_info[ii]); + if (retval) + goto err_ret; + finfo++; +@@ -727,29 +754,43 @@ int cdx_ioc_set_dpa_params(unsigned long args) + finfo++; + } + +- if (cdx_create_port_fqs()) +- return -1; ++ if (cdx_create_port_fqs()) { ++ retval = -EIO; ++ goto err_ret; ++ } + //create cp rate limit policier profiles + if (cdxdrv_create_missaction_policer_profiles(fman_info)) { ++ retval = -EIO; + goto err_ret; + } + #ifdef ENABLE_INGRESS_QOS + if (cdxdrv_create_ingress_qos_policer_profiles(fman_info)) { ++ retval = -EIO; + goto err_ret; + } + #endif + #ifdef ENABLE_EGRESS_QOS +- if(ceetm_init_cq_plcr()) ++ if(ceetm_init_cq_plcr()) { ++ retval = -EIO; + goto err_ret; ++ } + #endif + //init the fman and its ports + for (ii = 0; ii < num_fmans; ii++) { +- if (cdxdrv_set_miss_action(ii)) ++ if (cdxdrv_set_miss_action(ii)) { ++ retval = -EIO; + goto err_ret; ++ } + } + display_dpa_cfg(); ++ kfree(uspace_port_info); ++ kfree(uspace_tbl_info); + return 0; + err_ret: ++ DPA_ERROR("%s::error path retval %d, releasing partial DPA cfg\n", ++ __func__, retval); ++ kfree(uspace_port_info); ++ kfree(uspace_tbl_info); + release_cfg_info(); + return retval; + } +-- +2.39.5 diff --git a/patches/ask/split-kernel-patch.sh b/patches/ask/split-kernel-patch.sh new file mode 100644 index 0000000..142833e --- /dev/null +++ b/patches/ask/split-kernel-patch.sh @@ -0,0 +1,249 @@ +#!/usr/bin/env bash +set -euo pipefail + +# split-kernel-patch.sh +# Split one big git-style patch into one patch file per touched file, then +# optionally apply them one by one and stop at the first failure. +# +# Defaults match your ASK kernel patch workflow. +# +# Usage: +# ./split-kernel-patch.sh split +# ./split-kernel-patch.sh apply +# ./split-kernel-patch.sh check +# ./split-kernel-patch.sh reset-output +# +# Env overrides: +# PATCH_FILE=/src/ASK/patches/kernel/002-mono-gateway-ask-kernel_linux_6_12.patch +# LINUX_DIR=/src/linux +# OUT_DIR=/src/ASK/patches/kernel/split-002 +# FUZZ=0 +# USE_PATCH=0 # 0 = git apply, 1 = patch utility +# THREEWAY=0 # git apply --3way when USE_PATCH=0 +# UPDATED_PATCH_DIR=/src/ASK/patches/kernel/updated-patch +# # If a file with the same basename exists here, use it +# # instead of the generated split fragment. + +PATCH_FILE="${PATCH_FILE:-/src/ASK/patches/kernel/002-mono-gateway-ask-kernel_linux_6_12.patch}" +LINUX_DIR="${LINUX_DIR:-/src/linux}" +OUT_DIR="${OUT_DIR:-/src/ASK/patches/kernel/split-002}" +FUZZ="${FUZZ:-0}" +USE_PATCH="${USE_PATCH:-0}" +THREEWAY="${THREEWAY:-0}" +UPDATED_PATCH_DIR="${UPDATED_PATCH_DIR:-$(dirname "$OUT_DIR")/updated-patch}" +SERIES_FILE="${OUT_DIR}/series" +LOG_FILE="${OUT_DIR}/apply.log" + +usage() { + sed -n '1,35p' "$0" >&2 +} + +need_file() { + [ -f "$1" ] || { echo "ERROR: missing file: $1" >&2; exit 1; } +} + +need_dir() { + [ -d "$1" ] || { echo "ERROR: missing directory: $1" >&2; exit 1; } +} + +resolve_patch_file() { + # Generated split fragments are immutable-ish. During porting, put a + # corrected replacement patch in UPDATED_PATCH_DIR with the exact same + # basename, e.g.: + # updated-patch/0005-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth.h.patch + # The apply/check loop will use that replacement instead. + local generated="$1" + local replacement="${UPDATED_PATCH_DIR}/$(basename "$generated")" + + if [ -f "$replacement" ]; then + printf '%s\n' "$replacement" + else + printf '%s\n' "$generated" + fi +} + +patch_target_file() { + # Best-effort target display for one split fragment. Prefer the b/ path + # from the diff header, fall back to +++ if needed. + awk ' + /^diff --git / { + p = $4 + sub(/^b\//, "", p) + print p + exit + } + /^\+\+\+ / && $2 != "/dev/null" { + p = $2 + sub(/^b\//, "", p) + print p + exit + } + ' "$1" +} + +reset_output() { + rm -rf "$OUT_DIR" + mkdir -p "$OUT_DIR" +} + +split_patch() { + need_file "$PATCH_FILE" + reset_output + + # Lossless split: do NOT trim trailing whitespace and do NOT rewrite content. + # Each output starts at a 'diff --git ...' boundary. + awk -v outdir="$OUT_DIR" -v series="$SERIES_FILE" ' + function sanitize(s) { + sub(/^b\//, "", s) + gsub(/^\"|\"$/, "", s) + gsub(/\//, "__", s) + gsub(/[^A-Za-z0-9._+-]/, "_", s) + if (length(s) > 160) s = substr(s, 1, 160) + return s + } + BEGIN { + n = 0 + out = "" + } + /^diff --git / { + if (out != "") close(out) + n++ + path = $4 + safe = sanitize(path) + out = sprintf("%s/%04d-%s.patch", outdir, n, safe) + print out >> series + } + { + if (out != "") print $0 > out + } + END { + if (out != "") close(out) + if (n == 0) { + print "ERROR: no diff --git sections found" > "/dev/stderr" + exit 2 + } + print n > outdir "/count" + } + ' "$PATCH_FILE" + + echo "Split $(cat "$OUT_DIR/count") patch fragments into: $OUT_DIR" + echo "Series file: $SERIES_FILE" +} + +ensure_split_exists() { + if [ ! -s "$SERIES_FILE" ]; then + echo "=> No split series found, splitting first..." + split_patch + fi +} + +apply_one_git() { + patch_file="$1" + + args=(--verbose) + if [ "$THREEWAY" = "1" ]; then + args+=(--3way) + fi + + git apply "${args[@]}" --check "$patch_file" + git apply "${args[@]}" "$patch_file" +} + +apply_one_patch_utility() { + patch_file="$1" + + # --forward avoids reapplying already-applied hunks. + # --reject leaves .rej files for manual whack-a-mole. + patch -p1 --forward --batch --fuzz="$FUZZ" --dry-run < "$patch_file" + patch -p1 --forward --batch --fuzz="$FUZZ" --reject < "$patch_file" +} + +check_or_apply() { + mode="$1" + ensure_split_exists + need_dir "$LINUX_DIR" + + : > "$LOG_FILE" + cd "$LINUX_DIR" + + i=0 + total=$(wc -l < "$SERIES_FILE" | tr -d ' ') + + while IFS= read -r patch_file; do + i=$((i + 1)) + generated_patch_file="$patch_file" + patch_file=$(resolve_patch_file "$generated_patch_file") + base=$(basename "$generated_patch_file") + patching_target=$(patch_target_file "$patch_file") + if [ -z "$patching_target" ]; then + patching_target="unknown" + fi + + if [ "$patch_file" != "$generated_patch_file" ]; then + echo "=> [$i/$total] $base (using updated-patch override)" | tee -a "$LOG_FILE" + echo " override: $patch_file" >>"$LOG_FILE" + else + echo "=> [$i/$total] $base" | tee -a "$LOG_FILE" + fi + + if [ "$mode" = "check" ]; then + if git apply --check "$patch_file" >>"$LOG_FILE" 2>&1; then + echo " OK" | tee -a "$LOG_FILE" + continue + fi + echo " FAIL: $patch_file" | tee -a "$LOG_FILE" + echo "Stopped at: $patch_file" + echo "Inspect log: $LOG_FILE" + echo "Target file: $patching_target" + echo "" + exit 1 + fi + + if [ "$USE_PATCH" = "1" ]; then + if apply_one_patch_utility "$patch_file" >>"$LOG_FILE" 2>&1; then + echo " applied" | tee -a "$LOG_FILE" + continue + fi + else + if apply_one_git "$patch_file" >>"$LOG_FILE" 2>&1; then + echo " applied" | tee -a "$LOG_FILE" + continue + fi + fi + + echo " FAILED: $patch_file" | tee -a "$LOG_FILE" + echo "" + echo "Stopped at: $patch_file" + echo "Inspect log: $LOG_FILE" + echo "Target file: $patching_target" + echo "" + echo "Useful next commands:" + echo " cd $LINUX_DIR" + echo " git diff" + echo " git status --short" + echo " ${USE_PATCH:+find . -name '*.rej' -o -name '*.orig'}" + exit 1 + done < "$SERIES_FILE" + + echo "All $total patch fragments ${mode}ed successfully." +} + +cmd="${1:-split}" +case "$cmd" in + split) + split_patch + ;; + check) + check_or_apply check + ;; + apply) + check_or_apply apply + ;; + reset-output) + reset_output + ;; + *) + usage + exit 2 + ;; +esac diff --git a/patches/ask/upstream/README.md b/patches/ask/upstream/README.md new file mode 100644 index 0000000..4d534d0 --- /dev/null +++ b/patches/ask/upstream/README.md @@ -0,0 +1 @@ +This is for when vendor is already patching upstream source. And we are patching on top of it. diff --git a/patches/ask/upstream/kernel/0005-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth.h.patch b/patches/ask/upstream/kernel/0005-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth.h.patch new file mode 100644 index 0000000..69c897f --- /dev/null +++ b/patches/ask/upstream/kernel/0005-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth.h.patch @@ -0,0 +1,71 @@ +diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h +index 12a409d..740793d 100644 +--- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h +@@ -37,6 +37,7 @@ + #include /* struct qman_fq */ + + #include "fm_ext.h" ++#include "fm_ehash.h" + #include "dpaa_eth_trace.h" + + extern int dpa_rx_extra_headroom; +@@ -88,8 +89,13 @@ static inline void DPA_BUG_ON(bool cond) + (FM_PORT_FRM_ERR_UNSUPPORTED_FORMAT | \ + FM_PORT_FRM_ERR_LENGTH | FM_PORT_FRM_ERR_DMA) + +-/* The raw buffer size must be cacheline aligned. */ +-#define DPA_BP_RAW_SIZE 2048 ++/* The raw buffer size must be cacheline aligned. ++ * As 1518 byte packets are received in scatter gather buffers from DPAA, ++ * and these buffers are used by Wi-Fi which requires contiguous buffers, ++ * increase the raw buffer size from 2048 to 2176 to accommodate them in a ++ * contiguous FD. ++ */ ++#define DPA_BP_RAW_SIZE 2176 + + /* This is what FMan is ever allowed to use. + * FMan-DMA requires 16-byte alignment for Rx buffers, but SKB_DATA_ALIGN is +@@ -174,6 +180,7 @@ static inline void DPA_BUG_ON(bool cond) + #endif + + #define DPAA_ETH_RX_QUEUES 128 ++#define DPAA_IP_VERSION_4 4 + + /* Convenience macros for storing/retrieving the skb back-pointers. They must + * accommodate both recycling and confirmation paths - i.e. cases when the buf +@@ -304,6 +311,10 @@ struct dpa_percpu_priv_s { + u64 tx_frag_skbuffs; + /* number of S/G frames received */ + u64 rx_sg; ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) && defined(CONFIG_CPE_FAST_PATH) ++ u64 tx_caam_enc; ++ u64 tx_caam_dec; ++#endif + + struct rtnl_link_stats64 stats; + struct dpa_rx_errors rx_errors; +@@ -375,9 +386,13 @@ struct dpa_priv_s { + int loop_id; + int loop_to; + #endif +-#ifdef CONFIG_FSL_DPAA_CEETM ++#if defined(CONFIG_FSL_DPAA_CEETM) || defined(CONFIG_CPE_FAST_PATH) + bool ceetm_en; /* CEETM QoS enabled */ ++#ifdef CONFIG_CPE_FAST_PATH ++ void *qm_ctx; /* CEETM context */ ++#endif + #endif ++ void *ifinfo; + }; + + struct fm_port_fqs { +@@ -392,7 +407,7 @@ struct fm_port_fqs { + extern struct net_device *dpa_loop_netdevs[20]; + #endif + +-int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *count_ptr); ++int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *count_ptr, int threshold); + void __hot _dpa_rx(struct net_device *net_dev, + struct qman_portal *portal, + const struct dpa_priv_s *priv, diff --git a/patches/ask/upstream/kernel/0009-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth_sg.c.patch b/patches/ask/upstream/kernel/0009-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth_sg.c.patch new file mode 100644 index 0000000..d335909 --- /dev/null +++ b/patches/ask/upstream/kernel/0009-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth_sg.c.patch @@ -0,0 +1,1113 @@ +diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c +index a993a93..48d9609 100644 +--- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c +@@ -52,6 +52,55 @@ + #endif + #ifdef CONFIG_FSL_DPAA_CEETM + #include "dpaa_eth_ceetm.h" ++#endif ++#if defined(CONFIG_IP_NF_CONNTRACK_MARK) || defined(CONFIG_NF_CONNTRACK_MARK) ++#include "net/netfilter/nf_conntrack.h" ++#endif // CONFIG_IP_NF_CONNTRACK_MARK || CONFIG_NF_CONNTRACK_MARK ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++#include ++#include ++#include ++#include ++#endif ++ ++#define DPAA_EXTRA_BUF_SIZE_4_SKB SMP_CACHE_BYTES + DPA_MAX_FD_OFFSET + \ ++ sizeof(struct skb_shared_info) + 128 ++ ++#ifndef EXCLUDE_FMAN_IPR_OFFLOAD ++//added for IPR offload to FMAN ++static dpaa_eth_bpool_replenish_hook_t dpaa_eth_bpool_replenish_hook; ++#endif ++ ++/* registered function to get ceetm Fqs */ ++#ifdef CONFIG_CPE_FAST_PATH ++static cdx_get_ceetm_egressfq ceetm_fqget_func; ++static cdx_get_ceetm_dscp_fq ceetm_dscp_fqget_func; ++/* This function registers two cdx functions, which are to get the egress fq. ++ One is based on channel and classqueue and another one is based on dscp. */ ++int dpa_register_ceetm_get_egress_fq(cdx_get_ceetm_egressfq egress_fq_func, cdx_get_ceetm_dscp_fq dscp_fq_func) ++{ ++ ceetm_fqget_func = egress_fq_func; ++ ceetm_dscp_fqget_func = dscp_fq_func; ++ return 0; ++} ++EXPORT_SYMBOL(dpa_register_ceetm_get_egress_fq); ++#endif ++ ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++cdx_get_ipsec_fq_hook_t cdx_get_ipsec_fq_hookfn; ++ ++int dpa_register_ipsec_fq_handler(cdx_get_ipsec_fq_hook_t hookfn) ++{ ++ if (cdx_get_ipsec_fq_hookfn) { ++ printk("%s::hook already registered\n", __func__); ++ return -1; ++ } ++ cdx_get_ipsec_fq_hookfn = hookfn; ++ return 0; ++} ++ ++EXPORT_SYMBOL(dpa_register_ipsec_fq_handler); ++ + #endif + + /* DMA map and add a page frag back into the bpool. +@@ -81,6 +130,109 @@ static void dpa_bp_recycle_frag(struct dpa_bp *dpa_bp, unsigned long vaddr, + (*count_ptr)++; + } + ++// adding a new function which allocates memory for buffers and adds bman pool ++int dpaa_bp_alloc_n_add_buffs(const struct dpa_bp *dpa_bp, uint32_t nbuffs, bool act_skb) ++{ ++ struct bm_buffer bmb[8]; ++ char *new_buf, *ptr_buf; ++ dma_addr_t addr; ++ int ii,entries_in_bmb,buffs2fill; ++ struct device *dev = dpa_bp->dev; ++ struct sk_buff *skb = NULL, **skbh; ++ uint32_t bufsize; ++ ++ if (act_skb) ++ bufsize = DPAA_EXTRA_BUF_SIZE_4_SKB + dpa_bp->size; ++ else ++ bufsize = SMP_CACHE_BYTES + dpa_bp->size + DPA_MAX_FD_OFFSET + 128; ++ ++ /* aligning to SMP_CACHE_BYTES */ ++ bufsize = DPA_SKB_SIZE(bufsize); ++ ++ buffs2fill= 0; ++ while (buffs2fill < nbuffs) ++ { ++ memset(bmb, 0, sizeof(struct bm_buffer) * 8); ++ ++ if ((nbuffs - buffs2fill) < 8) ++ entries_in_bmb = (nbuffs - buffs2fill); ++ else ++ entries_in_bmb = 8; ++ for (ii= 0; iisize, DMA_BIDIRECTIONAL); ++ if (unlikely(dma_mapping_error(dev, addr))) ++ { ++ pr_err("%s(%d) ii %d buffs2fill %d dma_mapping_error failed \n", ++ __func__,__LINE__,ii,buffs2fill); ++ if (act_skb) ++ { ++ kfree_skb(skb); ++ } ++ kfree(new_buf); ++ goto handle_fail; ++ } ++ bm_buffer_set64(&bmb[ii], addr); ++ ++ bmb[ii].bpid = cpu_to_be16(dpa_bp->bpid & 0xff); ++ } ++ while (unlikely(bman_release(dpa_bp->pool, bmb, entries_in_bmb, 0))) ++ cpu_relax(); ++ buffs2fill+= entries_in_bmb; ++ /* printk("%s(%d) adrsses %lx %lx %lx %lx %lx %lx %lx %lx\n", ++ __func__,__LINE__,bm_buffer_get64(&bmb[0]),bm_buffer_get64(&bmb[1]), ++ bm_buffer_get64(&bmb[2]),bm_buffer_get64(&bmb[3]),bm_buffer_get64(&bmb[4]), ++ bm_buffer_get64(&bmb[5]),bm_buffer_get64(&bmb[6]),bm_buffer_get64(&bmb[7])); */ ++ } ++ /* printk("%s(%d) buff allocation and bman release success buffs2fill %d\n", ++ __func__,__LINE__,buffs2fill); */ ++ return 0; ++ ++handle_fail: ++ if (ii) ++ { ++ while (unlikely(bman_release(dpa_bp->pool, bmb, ii, 0))) ++ cpu_relax(); ++ } ++ return ii+buffs2fill; ++ ++} ++EXPORT_SYMBOL(dpaa_bp_alloc_n_add_buffs); ++ ++ + static int _dpa_bp_add_8_bufs(const struct dpa_bp *dpa_bp) + { + void *new_buf, *fman_buf; +@@ -189,12 +341,13 @@ err_release_previous_bufs: + /* Add buffers/(pages) for Rx processing whenever bpool count falls below + * REFILL_THRESHOLD. + */ +-int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *countptr) ++int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *countptr, int threshold) + { + int count = *countptr; + int new_bufs; ++ int percpu_buf_cfg_count = dpa_bp->config_count; + +- if (unlikely(count < CONFIG_FSL_DPAA_ETH_REFILL_THRESHOLD)) { ++ if (unlikely(count <= (percpu_buf_cfg_count - threshold))) { + do { + new_bufs = _dpa_bp_add_8_bufs(dpa_bp); + if (unlikely(!new_bufs)) { +@@ -205,10 +358,10 @@ int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *countptr) + break; + } + count += new_bufs; +- } while (count < CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT); ++ } while (count < percpu_buf_cfg_count); + + *countptr = count; +- if (unlikely(count < CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT)) ++ if (unlikely(count < percpu_buf_cfg_count)) + return -ENOMEM; + } + +@@ -372,7 +525,7 @@ EXPORT_SYMBOL(dpa_buf_is_recyclable); + * We are guaranteed there is enough room at the end of the data buffer to + * accommodate the shared info area of the skb. + */ +-static struct sk_buff *__hot contig_fd_to_skb(const struct dpa_priv_s *priv, ++struct sk_buff *__hot contig_fd_to_skb(const struct dpa_priv_s *priv, + const struct qm_fd *fd, + bool *use_gro, bool dcl4c_valid) + { +@@ -410,14 +563,14 @@ static struct sk_buff *__hot contig_fd_to_skb(const struct dpa_priv_s *priv, + + return skb; + } +- ++EXPORT_SYMBOL(contig_fd_to_skb); + + /* Build an skb with the data of the first S/G entry in the linear portion and + * the rest of the frame as skb fragments. + * + * The page fragment holding the S/G Table is recycled here. + */ +-static struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv, ++struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv, + const struct qm_fd *fd, bool *use_gro, + int *count_ptr, bool dcl4c_valid) + { +@@ -437,7 +590,8 @@ static struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv, + vaddr = phys_to_virt(addr); + DPA_BUG_ON(!IS_ALIGNED((unsigned long)vaddr, SMP_CACHE_BYTES)); + +- dpa_bp = priv->dpa_bp; ++/* dpa_bp = priv->dpa_bp; */ ++ dpa_bp = dpa_bpid2pool(fd->bpid); + /* Iterate through the SGT entries and add data buffers to the skb */ + sgt = vaddr + fd_off; + for (i = 0; i < DPA_SGT_MAX_ENTRIES; i++) { +@@ -445,9 +599,9 @@ static struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv, + DPA_BUG_ON(qm_sg_entry_get_ext(&sgt[i])); + + /* We use a single global Rx pool */ +- DPA_BUG_ON(dpa_bp != ++/* DPA_BUG_ON(dpa_bp != + dpa_bpid2pool(qm_sg_entry_get_bpid(&sgt[i]))); +- ++*/ + sg_addr = qm_sg_addr(&sgt[i]); + sg_vaddr = phys_to_virt(sg_addr); + DPA_BUG_ON(!IS_ALIGNED((unsigned long)sg_vaddr, +@@ -530,6 +684,7 @@ static struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv, + dpa_bp_recycle_frag(dpa_bp, (unsigned long)vaddr, count_ptr); + return skb; + } ++EXPORT_SYMBOL(sg_fd_to_skb); + + #ifdef CONFIG_FSL_DPAA_DBG_LOOP + static inline int dpa_skb_loop(const struct dpa_priv_s *priv, +@@ -547,6 +702,14 @@ static inline int dpa_skb_loop(const struct dpa_priv_s *priv, + } + #endif + ++#ifndef EXCLUDE_FMAN_IPR_OFFLOAD ++void register_dpaa_eth_bpool_replenish_hook(dpaa_eth_bpool_replenish_hook_t func) ++{ ++ dpaa_eth_bpool_replenish_hook = func; ++} ++EXPORT_SYMBOL(register_dpaa_eth_bpool_replenish_hook); ++#endif ++ + void __hot _dpa_rx(struct net_device *net_dev, + struct qman_portal *portal, + const struct dpa_priv_s *priv, +@@ -566,7 +729,7 @@ void __hot _dpa_rx(struct net_device *net_dev, + + if (unlikely(fd_status & FM_FD_STAT_RX_ERRORS) != 0) { + if (netif_msg_hw(priv) && net_ratelimit()) +- netdev_warn(net_dev, "FD status = 0x%08x\n", ++ netdev_warn(net_dev, "_dpa_rx FD status = 0x%08x\n", + fd_status & FM_FD_STAT_RX_ERRORS); + + percpu_stats->rx_errors++; +@@ -592,6 +755,15 @@ void __hot _dpa_rx(struct net_device *net_dev, + /* won't count the rx bytes in */ + return; + } ++#endif ++#ifndef EXCLUDE_FMAN_IPR_OFFLOAD ++ if (dpaa_eth_bpool_replenish_hook) { ++ if (dpaa_eth_bpool_replenish_hook(net_dev, fd->bpid)) { ++ //could not replenish buffer, drop packet ?? ++ printk("%s::could not replenish buffer to pool\n", ++ __func__); ++ } ++ } + #endif + skb = contig_fd_to_skb(priv, fd, &use_gro, dcl4c_valid); + } else { +@@ -638,6 +810,7 @@ void __hot _dpa_rx(struct net_device *net_dev, + _release_frame: + dpa_fd_release(net_dev, fd); + } ++EXPORT_SYMBOL(_dpa_rx); + + int __hot skb_to_contig_fd(struct dpa_priv_s *priv, + struct sk_buff *skb, struct qm_fd *fd, +@@ -738,7 +911,7 @@ EXPORT_SYMBOL(skb_to_contig_fd); + * - SG fragments that aren't mod 16 bytes in size (except for the last + * fragment) + */ +-static bool a050385_check_skb(struct sk_buff *skb, struct dpa_priv_s *priv) ++bool a050385_check_skb(struct sk_buff *skb, struct dpa_priv_s *priv) + { + skb_frag_t *frag; + int i, nr_frags; +@@ -800,12 +973,13 @@ static bool a050385_check_skb(struct sk_buff *skb, struct dpa_priv_s *priv) + + return false; + } ++EXPORT_SYMBOL(a050385_check_skb); + + /* Realign the skb by copying its contents at the start of a newly allocated + * page. Build a new skb around the new buffer and release the old one. + * A performance drop should be expected. + */ +-static struct sk_buff *a050385_realign_skb(struct sk_buff *skb, ++struct sk_buff *a050385_realign_skb(struct sk_buff *skb, + struct dpa_priv_s *priv) + { + int trans_offset = skb_transport_offset(skb); +@@ -874,7 +1048,212 @@ err: + put_page(npage); + return NULL; + } ++EXPORT_SYMBOL(a050385_realign_skb); ++ ++struct dpa_bp *sg_bpool_g; ++EXPORT_SYMBOL(sg_bpool_g); ++ ++struct dpa_bp *skb_2bfreed_bpool_g; //if no recyclable skbs exist in skb fraglist, those should be freed back, SEC engine will add to this bman pool ++EXPORT_SYMBOL(skb_2bfreed_bpool_g); ++ ++struct device* dpa_get_bp_device(void) ++{ ++ if (skb_2bfreed_bpool_g) ++ return skb_2bfreed_bpool_g->dev; ++ else ++ return NULL; ++} ++ ++/* ++ *This functions unmaps all the mapped skb sg so far and stored in sgt. ++*/ ++static void dma_unmap_skb_sg_addrs(struct device *dev, struct sk_buff *skb, ++ struct qm_sg_entry *sgt, int skb_list_frags) ++{ ++ dma_addr_t addr; ++ struct sk_buff *list_skb; ++ struct sk_buff *cur_skb; ++ const enum dma_data_direction dma_dir = DMA_TO_DEVICE; ++ int sgt_idx = 0; ++ int nr_frags = 0; ++ + ++ for (cur_skb = skb, list_skb = skb_shinfo(skb)->frag_list; ++ (sgt_idx < skb_list_frags) && (cur_skb); ) ++ { ++ addr = qm_sg_addr(&sgt[sgt_idx]); ++ dma_unmap_single(dev, addr, qm_sg_entry_get_len(&sgt[sgt_idx]), dma_dir); ++ sgt_idx++; ++ ++ nr_frags = skb_shinfo(cur_skb)->nr_frags; ++ while(nr_frags) ++ { ++ addr = qm_sg_addr(&sgt[sgt_idx]); ++ dma_unmap_page(dev, addr, qm_sg_entry_get_len(&sgt[sgt_idx]), dma_dir); ++ nr_frags--; ++ sgt_idx++; ++ } ++ cur_skb = list_skb; ++ if (list_skb) ++ list_skb = list_skb->next; ++ } ++ ++ return; ++} ++ ++/* ++ * This inline function updates the sgt entry parameters. ++ */ ++static inline void prepare_qm_sg_entry(struct qm_sg_entry *sgt, int sgt_idx, ++ unsigned int len, dma_addr_t addr) ++{ ++ qm_sg_entry_set_ext(&sgt[sgt_idx], 0); ++ qm_sg_entry_set_final(&sgt[sgt_idx], 0); ++ /* fill the first entry with skb details */ ++ qm_sg_entry_set_bpid(&sgt[sgt_idx], 0xff); ++ /* set the offset and addr pointers such that ++ in next alloc, that addr should point to skb*/ ++ qm_sg_entry_set_offset(&sgt[sgt_idx],0); ++ qm_sg_entry_set_len(&sgt[sgt_idx], len); ++ qm_sg_entry_set64(&sgt[sgt_idx], addr); ++ ++ return; ++} ++ ++int __hot skb_fraglist_to_sg_fd(struct device *dev, struct net_device *net_dev , struct sk_buff *skb, struct qm_fd *fd, u32 fd_cmd) ++{ ++ dma_addr_t addr, sg_addr; ++ struct sk_buff *list_skb; ++ struct sk_buff *cur_skb; ++ int sgt_size; ++ int err; ++ struct qm_sg_entry *sgt; ++ struct bm_buffer bmb; ++ int sgt_index = 0, nr_frags = 0; ++ const enum dma_data_direction dma_dir = DMA_TO_DEVICE; ++ int dma_map_size; ++ skb_frag_t *frag; ++ ++ // calculate the number of fragments ++ // check if any of skb is not recyclable ++ nr_frags = 1 + skb_shinfo(skb)->nr_frags; ++ skb_walk_frags(skb, list_skb) ++ { ++ nr_frags += 1 + skb_shinfo(list_skb)->nr_frags; ++ } ++ ++ /* The below condition covers nr_frags + 1(This is to store skb address in opaque variable at the end.)*/ ++ if (nr_frags > (DPA_SGT_MAX_ENTRIES)) ++ { ++ /* TODO linearize */ ++ pr_err("%s()::%d number of skb frags %d crossed the MAX SGT entries%d", __func__, __LINE__, nr_frags, DPA_SGT_MAX_ENTRIES); ++ return -1; ++ } ++ ++ fd->format = qm_fd_sg; ++ /* allocate an SG buffer from SG BMAN POOL ++ if any entries in skb_2bfreed_bpool_g, acquire and free skb and use the SG buffer */ ++ if (bman_acquire(skb_2bfreed_bpool_g->pool, &bmb, 1, 0) == 1) ++ { ++ sgt = (struct qm_sg_entry *)phys_to_virt(bmb.addr); ++ while (!qm_sg_entry_get_final(&sgt[sgt_index])) ++ sgt_index++; ++ addr = sgt[sgt_index+1].opaque; ++ if (addr) /* free the old skb */ ++ { ++ list_skb = (struct sk_buff *)(addr); ++ kfree_skb(list_skb); ++ } ++ sgt[sgt_index+1].opaque = 0; /* set to 0*/ ++ } ++ else if (bman_acquire(sg_bpool_g->pool, &bmb, 1, 0) == 1) ++ { ++ sgt = (struct qm_sg_entry *)phys_to_virt(bmb.addr); ++ } ++ else ++ { ++ printk("%s(%d) bman_acquire of SG bman buffer failed\n", ++ __func__,__LINE__); ++ return -1; ++ } ++ ++ ++ /* net_crit_ratelimited("%s(%d) nr_frags %d, MAX frags %d \n", __func__,__LINE__,nr_frags,DPA_SGT_MAX_ENTRIES);*/ ++ /* After nr_frags, in next index of sgt array storing skb address in opaque. So doing memset for (nr_frags+1).*/ ++ sgt_size = sizeof(struct qm_sg_entry) * (nr_frags + 1); ++ ++ ++ /* memset to 0s of size sgt_size*/ ++ memset(sgt, 0, sgt_size); ++ ++ ++ ++ sgt_index = 0; ++ for (cur_skb = skb, list_skb = skb_shinfo(skb)->frag_list; cur_skb;) ++ { ++ dma_map_size = skb_headlen(cur_skb); ++ addr = dma_map_single(dev, cur_skb->data, dma_map_size, dma_dir); ++ if (unlikely(dma_mapping_error(dev, addr))) { ++ if (net_ratelimit()) ++ netdev_err(net_dev, "skb_fraglist_to_sg_fd : 2.DMA mappping error"); ++ err = -EINVAL; ++ dma_unmap_skb_sg_addrs(dev, skb, sgt, sgt_index); ++ bman_release(sg_bpool_g->pool, &bmb, 1, 0); ++ return -1; ++ } ++ prepare_qm_sg_entry(sgt, sgt_index, dma_map_size, addr); ++ sgt_index++; ++ ++ nr_frags = skb_shinfo(cur_skb)->nr_frags; ++ frag = skb_shinfo(cur_skb)->frags; ++ while(nr_frags) ++ { ++ addr = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), dma_dir); ++ if (unlikely(dma_mapping_error(dev, addr))) { ++ if (net_ratelimit()) ++ netdev_err(net_dev, "skb_fraglist_to_sg_fd : 2.DMA mappping error"); ++ dma_unmap_skb_sg_addrs(dev, skb, sgt, sgt_index); ++ err = -EINVAL; ++ bman_release(sg_bpool_g->pool, &bmb, 1, 0); ++ return -1; ++ } ++ prepare_qm_sg_entry(sgt, sgt_index, skb_frag_size(frag), addr); ++ frag++; ++ nr_frags--; ++ sgt_index++; ++ } ++ cur_skb = list_skb; ++ if (list_skb) ++ list_skb = list_skb->next; ++ } ++ ++ qm_sg_entry_set_final(&sgt[sgt_index-1], 1); ++ ++ addr = (dma_addr_t) (skb); ++ sgt[sgt_index].opaque = addr; ++ ++ sg_addr = dma_map_single(dev, sgt, sgt_size, dma_dir); ++ if (unlikely(dma_mapping_error(dev, sg_addr))) { ++ /*if (netif_msg_tx_err(priv) && net_ratelimit())*/ ++ dma_unmap_skb_sg_addrs(dev, skb, sgt, sgt_index); ++ if (net_ratelimit()) ++ netdev_err(net_dev, "skb_fraglist_to_sg_fd : 2.DMA mappping error"); ++ err = -EINVAL; ++ bman_release(sg_bpool_g->pool, &bmb, 1, 0); ++ return -1; ++ } ++ qm_fd_addr_set64(fd, sg_addr); ++ fd->bpid = skb_2bfreed_bpool_g->bpid; /*sg_bpool_g->bpid;*/ ++ fd->cmd = fd_cmd; ++ fd->length20 = skb->len; ++ fd->offset = 0; ++ ++ /*net_crit_ratelimited("%s(%d) sgt_index %d, addr %p \n",__func__, __LINE__,sgt_index,(void *)(sgt[sgt_index].opaque)); */ ++ /* percpu_priv->tx_returned++; */ ++ return 0; ++} ++ ++EXPORT_SYMBOL(skb_fraglist_to_sg_fd); + int __hot skb_to_sg_fd(struct dpa_priv_s *priv, + struct sk_buff *skb, struct qm_fd *fd) + { +@@ -1019,9 +1398,532 @@ csum_failed: + } + EXPORT_SYMBOL(skb_to_sg_fd); + +-int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev) ++#define EMAC_QUEUENUM_MASK 0xff ++ ++#ifdef CONFIG_CPE_FAST_PATH ++static int pfe_eth_get_queuenum( struct sk_buff *skb ) ++{ ++ ++#if defined(CONFIG_IP_NF_CONNTRACK_MARK) || defined(CONFIG_NF_CONNTRACK_MARK) ++ /* Use conntrack mark (if conntrack exists) */ ++ if (skb->_nfct) { ++ enum ip_conntrack_info cinfo; ++ struct nf_conn *ct; ++ ++ ct = nf_ct_get(skb, &cinfo); ++ if (ct) { ++ u_int64_t markval; ++ ++ markval = ct->qosconnmark; ++ if (cinfo >= IP_CT_IS_REPLY) { ++ if (markval & ((uint64_t)1 << 63)) ++ markval >>= 32; ++ else ++ markval = 0; ++ } ++ markval &= 0x7fffffff; ++ return markval; ++ ++ } ++ } ++#endif ++ /* use packet mark (if any) */ ++ if (skb->mark) { ++ return (skb->mark & EMAC_QUEUENUM_MASK); ++ } ++ /* These are packets through control path, assign highest priority */ ++ return (QOS_DEFAULT_QUEUE); ++} ++#endif /*endif for CONFIG_CPE_FAST_PATH */ ++ ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++unsigned int ipsec_offload_pkt_cnt; ++void print_ipsec_offload_pkt_count(void) ++{ ++ printk("%s:: Ipsec offload slow path packet count = %d\n",__func__,ipsec_offload_pkt_cnt); ++ ipsec_offload_pkt_cnt = 0; ++} ++EXPORT_SYMBOL(print_ipsec_offload_pkt_count); ++#endif ++ ++#ifdef CONFIG_XFRM ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ ++#define ETH_HDR_SIZE 14 ++#define VLAN_HDR_SIZE 4 ++#define PPPOE_HDR_SIZE 8 ++#define ETHHDR_MACEND_OFFSET 12 ++ ++#define DPOVRD_ENABLE 0x80000000 ++ ++unsigned char* dpa_get_skb_nh(struct sk_buff* skb, unsigned short *l3_proto, unsigned short* l3_offset) ++{ ++ unsigned short type_id; ++ unsigned short offset = ETH_HDR_SIZE; ++ type_id = *((unsigned short*) (skb->data + offset - 2)); ++ while (1) ++ { ++ switch (type_id) ++ { ++ case htons(ETH_P_PPP_SES): ++ offset += PPPOE_HDR_SIZE; ++ type_id = *(unsigned short*)(skb->data + offset - 2); ++ ++ if (type_id == htons(PPP_IP)) ++ type_id = ntohs(ETH_P_IP); ++ else if (type_id == htons(PPP_IPV6)) ++ type_id = ntohs(ETH_P_IPV6); ++ break; ++ case htons(ETH_P_8021Q): ++ offset += VLAN_HDR_SIZE; ++ type_id = *(unsigned short*) (skb->data + offset - 2); ++ break; ++ case htons(ETH_P_IP): ++ case htons(ETH_P_IPV6): ++ *l3_proto = type_id; ++ *l3_offset = offset; ++ return (skb->data + offset); ++ default: ++ *l3_proto = type_id; ++ *l3_offset = offset; ++ return NULL; ++ } ++ } ++} ++ ++ __hot void dpaa_submit_outb_pkt_to_SEC(struct sk_buff *skb, struct net_device *net_dev, struct dpa_bp *dpa_bp) ++{ ++ struct xfrm_state *x; ++ struct qman_fq *egress_fq = NULL; ++ struct qm_fd fd; ++ int err = 0,ii; ++ int new_ethhdr_end, old_ethhdr_end; ++ unsigned short l3_proto, l3_offset; ++ unsigned char* skb_nh ; ++ struct sec_path *sp; ++ unsigned int dpovrd = 0; ++ ++ sp = skb_sec_path(skb); ++ ++ if ((cdx_get_ipsec_fq_hookfn) && (sp) && (sp->len)) ++ { ++ x = sp->xvec[0]; ++ ++ egress_fq = cdx_get_ipsec_fq_hookfn(x->handle); ++ ++ /* when a packet is submitting to SEC engine, ++ use the below code */ ++ if (!egress_fq) ++ { ++ printk("%s(%d) egress frame queue not found \n", ++ __func__, __LINE__); ++ goto sec_submit_failed; ++ } ++ ++ /* ++ * For packets with L2 headers other than ethernet header, L2 headers are ++ * stripped off and only ethernet header is maintained. These L2 headers ++ * will be added again in offline port classification table entry ++ * after SEC processing. ++ */ ++ ++ skb_nh = dpa_get_skb_nh(skb, &l3_proto, &l3_offset); ++ ++ if (skb_nh) ++ { ++ if(l3_offset != ETH_HDR_SIZE) ++ { ++ new_ethhdr_end = l3_offset - 2 ; /*l3 offset - protocol(2bytes) */ ++ old_ethhdr_end = ETHHDR_MACEND_OFFSET ; ++ while(old_ethhdr_end >= 0) ++ skb->data[new_ethhdr_end--] = skb->data[old_ethhdr_end--]; ++ ++ *((unsigned short*) (skb->data + l3_offset - 2) ) = l3_proto; ++ skb->data = skb->data + l3_offset - ETH_HDR_SIZE; ++ skb->len = skb->len - l3_offset + ETH_HDR_SIZE; ++ } ++ if ((*skb_nh >> 4) == IPVERSION) /* Extracting ip version from network header */ ++ { ++ dpovrd = IPPROTO_IPIP; /* ESP trailer NH type. Here traffic is IPv4 */ ++ } ++ else ++ { ++ dpovrd = IPPROTO_IPV6; /* ESP trailer NH type. Here traffic is IPv4 */ ++ } ++ dpovrd |= DPOVRD_ENABLE; /* OVRD enable */ ++ } ++ ++ ++ err = skb_fraglist_to_sg_fd(dpa_bp->dev, net_dev, skb, &fd, dpovrd); ++ if (unlikely(err < 0)) ++ { ++ printk("%s(%d) skb_fraglist_to_sg_fd \n",__func__,__LINE__); ++ goto sec_submit_failed; ++ } ++ ++ ++ for (ii = 0; ii < 100000; ii++) { ++ err = qman_enqueue(egress_fq, &fd, 0); ++ if (err != -EBUSY) ++ break; ++ } ++ ++ if (unlikely(err < 0)) { ++ struct bm_buffer bmb; ++ dma_addr_t addr; ++ ++ printk("%s(%d) dpa_xmit failed\n",__func__,__LINE__); ++ /* put the buffer back to sg pool */ ++ addr = qm_fd_addr(&fd); ++ bm_buffer_set64(&bmb, addr); ++ while (bman_release(sg_bpool_g->pool, &bmb, 1, 0)) ++ cpu_relax(); ++ /* release skb and retuen */ ++ goto sec_submit_failed; ++ } ++ ++ /* sucessful transmit to CAAM */ ++ netif_trans_update(net_dev); ++ ipsec_offload_pkt_cnt++; ++ return; ++ } ++sec_submit_failed: ++ dev_kfree_skb(skb); ++ return; ++ ++} ++ ++EXPORT_SYMBOL(dpaa_submit_outb_pkt_to_SEC); ++ ++/** ++ * dpa_add_dummy_eth_hdr - Add dummy Ethernet header space to SKB ++ * @skb_in: Pointer to SKB pointer (may be reallocated) ++ * @priv_headroom: Private headroom to reserve (not used, for API compatibility) ++ * @hdroom_realloc: Set to 1 if headroom was reallocated ++ * ++ * This function ensures there is space for an Ethernet header in the SKB ++ * headroom for cellular interfaces that don't have a MAC header. ++ * Returns 0 on success, negative on failure. ++ */ ++int dpa_add_dummy_eth_hdr(struct sk_buff **skb_in, int priv_headroom, ++ unsigned char *hdroom_realloc) ++{ ++ struct sk_buff *skb = *skb_in; ++ struct ethhdr *eth; ++ __be16 proto; ++ int headroom_needed = ETH_HLEN + priv_headroom; ++ int ipv; ++ ++ /* Check if we have enough headroom */ ++ if (skb_headroom(skb) < headroom_needed) { ++ struct sk_buff *new_skb; ++ ++ new_skb = skb_realloc_headroom(skb, headroom_needed); ++ if (!new_skb) ++ return -ENOMEM; ++ ++ dev_kfree_skb(skb); ++ *skb_in = new_skb; ++ skb = new_skb; ++ *hdroom_realloc = 1; ++ } ++ ++ /* Determine IP version from first byte of packet */ ++ ipv = (skb->data[0] >> 4) & 0xf; ++ ++ /* Set protocol based on IP version */ ++ if (ipv == 6) ++ proto = htons(ETH_P_IPV6); ++ else ++ proto = htons(ETH_P_IP); ++ ++ /* Push ethernet header space */ ++ eth = (struct ethhdr *)skb_push(skb, ETH_HLEN); ++ ++ /* Initialize with dummy MAC addresses (will be overwritten later) */ ++ memset(eth->h_dest, 0, ETH_ALEN); ++ memset(eth->h_source, 0, ETH_ALEN); ++ eth->h_proto = proto; ++ ++ /* Reset SKB pointers - caller will adjust data/len */ ++ skb_pull(skb, ETH_HLEN); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_add_dummy_eth_hdr); ++ ++#ifdef UNIT_TEST ++unsigned char temp_ethhdr[16]; ++#endif ++/* This function is invoked from xfrm_input.c to submit IPSEC packets ++ to SEC engine, if the corresponding SA is programmed in ucode ++*/ ++int dpaa_submit_inb_pkt_to_SEC(struct sk_buff *skb, uint16_t sagd) ++{ ++ struct qman_fq *sec_fq = NULL; ++ struct dpa_priv_s *priv = NULL; ++ struct dpa_percpu_priv_s *percpu_priv = NULL; ++ struct qm_fd fd; ++ int err, ii, ret; ++ uint8_t tmp_len = 0, data[ETH_HLEN],ipvsn; ++ struct net_device *netdev; ++ struct device* dev; ++ unsigned char hdroom_realloced = 0; ++#ifdef UNIT_TEST ++ unsigned short dev_type = skb->dev->type; ++#endif ++ struct net_device *real_dev = NULL; ++ ++ if (cdx_get_ipsec_fq_hookfn) ++ { ++ /*net_crit_ratelimited("%s(%d) x->handle %d, skb->data %p, machdr %p\n", ++ __func__,__LINE__, sagd,skb->data, skb_mac_header(skb)); */ ++ /* Get the SEC FQID corresponding to sagd */ ++ sec_fq = cdx_get_ipsec_fq_hookfn(sagd); ++ ++ /*if no SEC FQID found, return non-zero to return pkt to linux*/ ++ if (!sec_fq || !(skb->dev)) ++ { ++ return -1; /* give packet to linux */ ++ } ++#ifdef UNIT_TEST ++ { ++ skb->dev->type = ARPHRD_NONE; ++ skb->mac_len = 0; ++ } ++#endif ++ if (skb->dev->type == ARPHRD_ETHER) ++ { ++ /* Get the corresponding physical device info, if the skb->dev corresponds to vlan*/ ++ real_dev = is_vlan_dev(skb->dev) ? vlan_dev_real_dev(skb->dev) : skb->dev; ++ priv = netdev_priv(real_dev); ++ /* SEC shared descriptor designed to take packet including MAC hdr */ ++ /* modify skb->data to point to MAC hdr */ ++ tmp_len = (skb->data - skb_mac_header(skb)); ++ skb->len += tmp_len; ++ skb->data = skb_mac_header(skb); ++ } ++ else if (skb->dev->type == ARPHRD_PPP) ++ { ++ /* Get the corresponding physica device info */ ++ netdev = __dev_get_by_index(dev_net(skb->dev), skb->iif_index); ++ if (!netdev) ++ { ++ return -1; ++ } ++ priv = netdev_priv(netdev); ++ /* observation in case of PPPoE skb_mac_header is pointing to IP header*/ ++ /* get the IP version*/ ++ /*printk("%s(%d), MAC header ptr: \n", ++ __func__,__LINE__); ++ display_buf_data(&skb->head[skb->mac_header] ,16);*/ ++ ipvsn = ((skb->head[skb->mac_header]) & 0xf0) >> 4; ++ /*printk(KERN_INFO "iif_dev :%s, ifindex %d, ipvsn %d\n", netdev->name, netdev->ifindex, ipvsn);*/ ++ /* SEC shared descriptor designed to take packet including MAC hdr */ ++ /* modify skb->data to point to MAC hdr */ ++ tmp_len = (skb->data - skb_mac_header(skb) + ETH_HLEN); ++ skb->len += tmp_len; ++ skb->data = skb_mac_header(skb) - ETH_HLEN; ++ /* Copy MAC hdr to the skb->data */ ++ /* have a backup data in case of failure */ ++ memcpy(data, skb->data, ETH_HLEN); ++ /* copying 8 bytes and then 4 bytes from the backup , as there is overlap*/ ++ memcpy(skb->data, skb->data-PPPOE_SES_HLEN, PPPOE_SES_HLEN); ++ memcpy(skb->data+PPPOE_SES_HLEN, data, 4); ++ /* setting IPv4/IPv6 ethernet protocol value */ ++ if (ipvsn == DPAA_IP_VERSION_4) ++ { ++ skb->data[12] = 0x08; ++ skb->data[13] = 0x00; ++ } ++ else /* IP v6 */ ++ { ++ skb->data[12] = 0x86; ++ skb->data[13] = 0xdd; ++ } ++ /*printk("%s(%d), modified header : \n", ++ __func__,__LINE__); ++ display_buf_data(skb->data,16);*/ ++ } ++ ++ if (skb->dev->wifi_offload_dev) ++ { ++ dev = dpa_get_bp_device(); ++ netdev = skb->dev; ++ /* Following code is added for cellular interfaces */ ++ /* FIX :May be we can check for device type as ARPHRD_NONE */ ++ if (skb->mac_len == 0) ++ { ++ tmp_len = (skb->data - skb_network_header(skb)); ++ skb->len += tmp_len; ++ skb->data = skb_network_header(skb); ++#ifdef UNIT_TEST ++ memcpy(temp_ethhdr, (skb->data - ETH_HLEN), 12); ++#endif ++ /* Add mac_header */ ++ ret = dpa_add_dummy_eth_hdr(&skb, 0, &hdroom_realloced); ++ if (ret < 0) ++ return -1; ++ ++ skb->data -= ETH_HLEN; ++ skb->len += ETH_HLEN; ++ tmp_len += ETH_HLEN; ++#ifdef UNIT_TEST ++ memcpy(skb->data ,temp_ethhdr,12); ++ skb->dev->type = dev_type; ++ skb->mac_len = ETH_HLEN; ++#endif ++ } ++ } ++ else ++ { ++ if (!priv) ++ return -1; ++ dev = priv->dpa_bp->dev; ++ netdev = priv->net_dev; ++ } ++ /*FIXME: skb->dev->type other than ARPHRD_ETHER and ARPHRD_PPP priv is uninitialized. Here else case should add to address it. */ ++ /*net_crit_ratelimited("%s(%d) x->handle %d, skb->data %p, machdr %p, len %d, skb->dev %p\n", ++ __func__,__LINE__, sagd, skb->data, skb_mac_header(skb),skb->len, skb->dev); */ ++ err = skb_fraglist_to_sg_fd(dev, netdev, skb, &fd, 0); ++ if (unlikely(err < 0)) ++ { ++ printk("%s(%d) skb_fraglist_to_sg_fd failed\n", ++ __func__,__LINE__); ++ if (skb->dev->type == ARPHRD_PPP) ++ memcpy(skb->data, data, ETH_HLEN); ++ ++ skb->len -= tmp_len; ++ skb->data += tmp_len; ++ return -1; /* give packet to linux */ ++ } ++ ++ for (ii = 0; ii < 100000; ii++) { ++ err = qman_enqueue(sec_fq, &fd, 0); ++ if (err != -EBUSY) ++ break; ++ } ++ ++ if (unlikely(err < 0)) { ++ struct bm_buffer bmb; ++ dma_addr_t addr; ++ ++ printk("%s(%d) qman_enqueue failed\n", ++ __func__,__LINE__); ++ /* put the buffer back to sg pool */ ++ addr = qm_fd_addr(&fd); ++ bm_buffer_set64(&bmb, addr); ++ while (bman_release(sg_bpool_g->pool, &bmb, 1, 0)) ++ cpu_relax(); ++ return -1; /* give packet to linux */ ++ } ++ if (priv) ++ { ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ percpu_priv->tx_caam_dec++; ++ } ++ } ++ return 0; ++} ++#endif ++#endif ++ ++#ifdef CONFIG_CPE_FAST_PATH ++#define CHANNEL_BIT_POSITION 24 ++ ++int cpe_fp_tx(struct sk_buff *skb, struct net_device *net_dev) + { + struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ int channel_id; ++ int queuenum; ++ int markval,ff=0; ++ struct qman_fq *egress_fq, *conf_fq; ++ uint8_t get_ceetm_cm_egress_fq; ++ uint8_t dscp; ++ unsigned char* skb_nh ; ++ uint16_t l3_proto = 0, l3_offset = 0; ++ ++ priv = netdev_priv(net_dev); ++#ifdef CONFIG_XFRM ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ if(skb->ipsec_offload) { ++ dpaa_submit_outb_pkt_to_SEC(skb, net_dev, priv->dpa_bp); ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ percpu_priv->tx_caam_enc++; ++ return NETDEV_TX_OK; ++ } ++#endif ++#endif ++ if (priv->ceetm_en) { ++ /* markval also using to know qosconnmark is present for this packet. */ ++ /* If markval is 0 assuming qosconnmark is not present for this packet(connection). */ ++ markval = pfe_eth_get_queuenum(skb); ++ queuenum = markval & 0xf; ++ channel_id = (markval >> CHANNEL_BIT_POSITION) & 0xf; ++ egress_fq = NULL; ++ get_ceetm_cm_egress_fq = 1; /* This becomes 0 when qosconnark(markval is 0) is not present or ++ can't find egress fq for dscp */ ++ /* markval is 0 means qosconnmark is not configured, so getting dscp from skb. */ ++ /* using dscp get the egress fq. */ ++ if (!markval) { ++ skb_nh = dpa_get_skb_nh(skb, &l3_proto, &l3_offset); ++ if (skb_nh) ++ { ++ /* skb_nh is non NULL means either it is pointing to ipv4 or ipv6 header only */ ++ if (l3_proto == htons(ETH_P_IP)) ++ { ++ /* Getting the DSCP value from IPv4 header*/ ++ dscp = (((struct iphdr *)skb_nh)->tos) >> 2; /* tos 8 bit size, DSCP 6 bit value, shifting 2 bits right to get actual DSCP value*/ ++ } ++ else ++ { ++ /* Getting the DSCP value from IPv6 header*/ ++ dscp = ((((struct ipv6hdr *)skb_nh)->priority) << 2) /* priority is 4 bit size, shifting 2 bits left to give space for flow_lbl[0] 2 bits */ ++ + ((((struct ipv6hdr *)skb_nh)->flow_lbl[0]) >> 6); /* flow_lbl[0] is 8 bit size, DSCP 2 bits are here, so shifting 6 bits right*/ ++ } ++ egress_fq = ceetm_dscp_fqget_func(priv->qm_ctx, dscp); ++ if (egress_fq) ++ get_ceetm_cm_egress_fq = 0; ++ } ++ } ++ if (get_ceetm_cm_egress_fq) ++ { ++ if (ceetm_fqget_func) { ++ egress_fq = ceetm_fqget_func(priv->qm_ctx, ++ channel_id,queuenum,ff); ++ } ++ if (!egress_fq) { ++ printk(KERN_CRIT "%s::unable to get ceetm fq, mark %08x registered func %p" ++ "dropping packet\n", ++ __func__, markval , ceetm_fqget_func); ++ dev_kfree_skb(skb); ++ return NETDEV_TX_OK; ++ } ++ } ++ } else ++ { ++ queuenum = dpa_get_queue_mapping(skb); ++ if (unlikely(queuenum >= DPAA_ETH_TX_QUEUES)) ++ queuenum = queuenum % DPAA_ETH_TX_QUEUES; ++ egress_fq = priv->egress_fqs[queuenum]; ++ } ++ conf_fq = priv->conf_fqs[queuenum & (DPAA_ETH_TX_QUEUES-1)]; ++ return dpa_tx_extended(skb, net_dev, egress_fq, conf_fq); ++} ++#endif ++ ++static inline void skb_reset_truesize(struct sk_buff *skb, unsigned int size) ++{ ++ size -= SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ skb->truesize = SKB_TRUESIZE(size); ++ ++ return; ++} ++ ++int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv; + int queue_mapping = dpa_get_queue_mapping(skb); + struct qman_fq *egress_fq, *conf_fq; + +@@ -1033,6 +1935,10 @@ int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev) + return NETDEV_TX_OK; + #endif + ++#ifdef CONFIG_CPE_FAST_PATH ++ return cpe_fp_tx(skb, net_dev); ++#endif ++ + priv = netdev_priv(net_dev); + + #ifdef CONFIG_FSL_DPAA_CEETM +@@ -1101,6 +2007,7 @@ int __hot dpa_tx_extended(struct sk_buff *skb, struct net_device *net_dev, + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + #endif /* CONFIG_FSL_DPAA_TS */ + ++ + /* MAX_SKB_FRAGS is larger than our DPA_SGT_MAX_ENTRIES; make sure + * we don't feed FMan with more fragments than it supports. + * Btw, we're using the first sgt entry to store the linear part of +@@ -1195,13 +2102,18 @@ int __hot dpa_tx_extended(struct sk_buff *skb, struct net_device *net_dev, + * but we need the skb to look as if returned by build_skb(). + * We need to manually adjust the tailptr as well. + */ ++ /* skb_reset_truesize() resets the skb truesize, to avoid ++ * skb truesize gets increment continuously when the same ++ * skb used for next fragments. ++ */ ++ skb_reset_truesize(skb, SMP_CACHE_BYTES + DPA_SKB_SIZE(priv->dpa_bp->size) + ++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); + skb->data = skb->head + offset; + skb_reset_tail_pointer(skb); + + (*countptr)++; + percpu_priv->tx_returned++; + } +- + if (unlikely(dpa_xmit(priv, percpu_stats, &fd, egress_fq, conf_fq) < 0)) + goto xmit_failed; + diff --git a/patches/ask/upstream/kernel/0062-drivers__net__ppp__ppp_generic.c.patch b/patches/ask/upstream/kernel/0062-drivers__net__ppp__ppp_generic.c.patch new file mode 100644 index 0000000..0fddbb6 --- /dev/null +++ b/patches/ask/upstream/kernel/0062-drivers__net__ppp__ppp_generic.c.patch @@ -0,0 +1,30 @@ +diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c +index f9f0f16..1f94967 100644 +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -55,6 +55,10 @@ + #include + #include + ++#if defined(CONFIG_CPE_FAST_PATH) ++#include ++#endif ++ + #define PPP_VERSION "2.4.2" + + /* +@@ -3535,6 +3539,14 @@ ppp_connect_channel(struct channel *pch, int unit) + + outl: + spin_unlock(&pch->upl); ++#if defined(CONFIG_CPE_FAST_PATH) ++ if ((ppp->dev) && (!ppp->closing)) { ++ rtnl_lock(); ++ rtmsg_ifinfo(RTM_NEWLINK, ppp->dev, 0, GFP_KERNEL, 0, NULL); ++ rtnl_unlock(); ++ } ++#endif ++ + out: + mutex_unlock(&pn->all_ppp_mutex); + return ret; diff --git a/patches/ask/upstream/kernel/0068-drivers__staging__fsl_qbman__qman_high.c.patch b/patches/ask/upstream/kernel/0068-drivers__staging__fsl_qbman__qman_high.c.patch new file mode 100644 index 0000000..5e5b21c --- /dev/null +++ b/patches/ask/upstream/kernel/0068-drivers__staging__fsl_qbman__qman_high.c.patch @@ -0,0 +1,371 @@ +diff --git a/drivers/staging/fsl_qbman/qman_high.c b/drivers/staging/fsl_qbman/qman_high.c +index 4085aa9a2dcb..96c3122e665b 100644 +--- a/drivers/staging/fsl_qbman/qman_high.c ++++ b/drivers/staging/fsl_qbman/qman_high.c +@@ -34,6 +34,9 @@ + + #include "qman_low.h" + ++#include ++#include ++ + /* Compilation constants */ + #define DQRR_MAXFILL 15 + #define EQCR_ITHRESH 4 /* if EQCR congests, interrupt threshold */ +@@ -69,6 +72,33 @@ + spin_unlock(&__fq478->fqlock); \ + } while (0) + ++#if 1 ++#define display_ceetm_cmd(a,b,c) ++#else ++#define display_ceetm_cmd(a, b, c) _display_ceetm_cmd((char *)__func__, a, b, c) ++static void _display_ceetm_cmd(char *func, uint32_t verb, void *buf, uint32_t size) ++{ ++ uint8_t *ptr; ++ uint32_t ii,jj=0; ++ uint8_t buff[200]; ++ ++ ptr = buf; ++ jj = sprintf(buff, "%s::\n%02x ", func, verb); ++ for (ii = 1; ii <= size; ii++) { ++ if (ii && ((ii % 16) == 0)) ++ { ++ buff[jj] = 0; ++ printk("%s\n", buff); ++ jj = 0; ++ } ++ jj += sprintf(buff+jj, "%02x ", *ptr); ++ ptr++; ++ } ++ buff[jj] = 0; ++ printk("%s\n\n", buff); ++} ++#endif ++ + static inline void fq_set(struct qman_fq *fq, u32 mask) + { + set_bits(mask, &fq->flags); +@@ -128,6 +158,10 @@ struct qman_portal { + u8 alloced; + /* power management data */ + u32 save_isdr; ++#ifdef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI ++ struct net_device *dummy_dev; ++ struct napi_struct napi; ++#endif + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + /* Keep a shadow copy of the DQRR on LE systems as the SW needs to + * do byte swaps of DQRR read only memory. First entry must be aligned +@@ -473,7 +507,16 @@ static irqreturn_t portal_isr(__always_unused int irq, void *ptr) + + /* DQRR-handling if it's interrupt-driven */ + if (is & QM_PIRQ_DQRI) { ++#ifndef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI + __poll_portal_fast(p, CONFIG_FSL_QMAN_POLL_LIMIT); ++#else ++ /* Disable QMan IRQ and invoke NAPI */ ++ qman_p_irqsource_remove(p, QM_PIRQ_DQRI); ++ if (napi_schedule_prep(&p->napi)) ++ { ++ __napi_schedule(&p->napi); ++ } ++#endif + clear = QM_DQAVAIL_MASK | QM_PIRQ_DQRI; + } + +@@ -575,6 +618,27 @@ struct dev_pm_domain qman_portal_device_pm_domain = { + } + }; + ++ ++ ++#ifdef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI ++static int qman_portal_dqrr_poll(struct napi_struct *napi, int budget) ++{ ++ struct qman_portal *portal = container_of(napi, struct qman_portal, napi); ++ ++ int cleaned = qman_p_poll_dqrr(portal, budget); ++ ++ if (cleaned < budget) { ++ int tmp; ++ napi_complete(napi); ++ tmp = qman_p_irqsource_add(portal, QM_PIRQ_DQRI); ++ // DPA_BUG_ON(tmp); ++ } ++ ++ return cleaned; ++} ++#endif ++ ++ + struct qman_portal *qman_create_portal( + struct qman_portal *portal, + const struct qm_portal_config *config, +@@ -737,6 +801,15 @@ struct qman_portal *qman_create_portal( + goto fail_dqrr_mr_empty; + } + } ++#ifdef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI ++ /* Initialize NAPI for Rx processing */ ++ portal->dummy_dev = alloc_netdev_dummy(0); ++ if (!portal->dummy_dev) ++ goto fail_dqrr_mr_empty; ++ ++ netif_napi_add(portal->dummy_dev, &portal->napi, qman_portal_dqrr_poll); ++ napi_enable(&portal->napi); ++#endif + /* Success */ + portal->config = config; + /* +@@ -832,6 +902,15 @@ void qman_destroy_portal(struct qman_portal *qm) + const struct qm_portal_config *pcfg; + int i; + ++#ifdef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI ++ if (qm->dummy_dev) { ++ napi_disable(&qm->napi); ++ netif_napi_del(&qm->napi); ++ free_netdev(qm->dummy_dev); ++ qm->dummy_dev = NULL; ++ } ++#endif ++ + /* Stop dequeues on the portal */ + qm_dqrr_sdqcr_set(&qm->p, 0); + +@@ -3170,6 +3245,7 @@ static int qman_ceetm_configure_lfqmt(struct qm_mcc_ceetm_lfqmt_config *opts) + p = get_affine_portal(); + PORTAL_IRQ_LOCK(p, irqflags); + ++ display_ceetm_cmd(QM_CEETM_VERB_LFQMT_CONFIG, opts, sizeof(struct qm_mcc_ceetm_lfqmt_config)); + mcc = qm_mc_start(&p->p); + mcc->lfqmt_config = *opts; + qm_mc_commit(&p->p, QM_CEETM_VERB_LFQMT_CONFIG); +@@ -3233,6 +3309,7 @@ static int qman_ceetm_configure_cq(struct qm_mcc_ceetm_cq_config *opts) + + mcc = qm_mc_start(&p->p); + mcc->cq_config = *opts; ++ display_ceetm_cmd(QM_CEETM_VERB_CQ_CONFIG, opts, sizeof(struct qm_mcc_ceetm_cq_config)); + qm_mc_commit(&p->p, QM_CEETM_VERB_CQ_CONFIG); + while (!(mcr = qm_mc_result(&p->p))) + cpu_relax(); +@@ -3296,7 +3373,7 @@ static int qman_ceetm_configure_dct(struct qm_mcc_ceetm_dct_config *opts) + + p = get_affine_portal(); + PORTAL_IRQ_LOCK(p, irqflags); +- ++ display_ceetm_cmd(QM_CEETM_VERB_DCT_CONFIG, opts, sizeof(struct qm_mcc_ceetm_dct_config)); + mcc = qm_mc_start(&p->p); + mcc->dct_config = *opts; + qm_mc_commit(&p->p, QM_CEETM_VERB_DCT_CONFIG); +@@ -3360,6 +3437,8 @@ static int qman_ceetm_configure_class_scheduler( + + mcc = qm_mc_start(&p->p); + mcc->csch_config = *opts; ++ display_ceetm_cmd(QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG, opts, ++ sizeof(struct qm_mcc_ceetm_class_scheduler_config)); + qm_mc_commit(&p->p, QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG); + while (!(mcr = qm_mc_result(&p->p))) + cpu_relax(); +@@ -3410,7 +3489,7 @@ static int qman_ceetm_query_class_scheduler(struct qm_ceetm_channel *channel, + return 0; + } + +-static int qman_ceetm_configure_mapping_shaper_tcfc( ++int qman_ceetm_configure_mapping_shaper_tcfc( + struct qm_mcc_ceetm_mapping_shaper_tcfc_config *opts) + { + struct qm_mc_command *mcc; +@@ -3423,6 +3502,8 @@ static int qman_ceetm_configure_mapping_shaper_tcfc( + PORTAL_IRQ_LOCK(p, irqflags); + + mcc = qm_mc_start(&p->p); ++ display_ceetm_cmd(QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG, opts, ++ sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + mcc->mst_config = *opts; + qm_mc_commit(&p->p, QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG); + while (!(mcr = qm_mc_result(&p->p))) +@@ -3440,6 +3521,7 @@ static int qman_ceetm_configure_mapping_shaper_tcfc( + } + return 0; + } ++EXPORT_SYMBOL(qman_ceetm_configure_mapping_shaper_tcfc); + + static int qman_ceetm_query_mapping_shaper_tcfc( + struct qm_mcc_ceetm_mapping_shaper_tcfc_query *opts, +@@ -3485,7 +3567,7 @@ static int qman_ceetm_configure_ccgr(struct qm_mcc_ceetm_ccgr_config *opts) + + p = get_affine_portal(); + PORTAL_IRQ_LOCK(p, irqflags); +- ++ display_ceetm_cmd(QM_CEETM_VERB_CCGR_CONFIG, opts, sizeof(struct qm_mcc_ceetm_ccgr_config)); + mcc = qm_mc_start(&p->p); + mcc->ccgr_config = *opts; + +@@ -3903,6 +3985,7 @@ int qman_ceetm_lni_enable_shaper(struct qm_ceetm_lni *lni, int coupled, + lni->shaper_couple = coupled; + lni->oal = oal; + ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); + config_opts.dcpid = lni->dcp_idx; + config_opts.shaper_config.cpl = coupled; +@@ -3936,7 +4019,8 @@ int qman_ceetm_lni_disable_shaper(struct qm_ceetm_lni *lni) + pr_err("The shaper has been disabled\n"); + return -EINVAL; + } +- ++ ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); + config_opts.dcpid = lni->dcp_idx; + config_opts.shaper_config.cpl = lni->shaper_couple; +@@ -4173,6 +4257,7 @@ int qman_ceetm_lni_set_tcfcc(struct qm_ceetm_lni *lni, + return -EINVAL; + } + ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + query_opts.cid = cpu_to_be16(CEETM_COMMAND_TCFC | lni->idx); + query_opts.dcpid = lni->dcp_idx; + if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) { +@@ -4254,6 +4339,7 @@ int qman_ceetm_channel_claim(struct qm_ceetm_channel **channel, + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + p->idx = channel_idx; + p->dcp_idx = lni->dcp_idx; + p->lni_idx = lni->idx; +@@ -4264,7 +4350,7 @@ int qman_ceetm_channel_claim(struct qm_ceetm_channel **channel, + channel_idx); + config_opts.dcpid = lni->dcp_idx; + config_opts.channel_mapping.map_lni_id = lni->idx; +- config_opts.channel_mapping.map_shaped = 0; ++ config_opts.channel_mapping.map_shaped = 1; + if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts)) { + pr_err("Can't map channel#%d for LNI#%d\n", + channel_idx, lni->idx); +@@ -4296,7 +4382,7 @@ int qman_ceetm_channel_release(struct qm_ceetm_channel *channel) + channel->dcp_idx); + return -EINVAL; + } +- ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | + channel->idx); + config_opts.dcpid = channel->dcp_idx; +@@ -4334,7 +4420,7 @@ int qman_ceetm_channel_enable_shaper(struct qm_ceetm_channel *channel, + pr_err("This channel shaper has been enabled!\n"); + return -EINVAL; + } +- ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + channel->shaper_enable = 1; + channel->shaper_couple = coupled; + +@@ -4347,6 +4433,7 @@ int qman_ceetm_channel_enable_shaper(struct qm_ceetm_channel *channel, + return -EINVAL; + } + ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING | + channel->idx); + config_opts.dcpid = channel->dcp_idx; +@@ -4441,7 +4528,7 @@ int qman_ceetm_channel_set_commit_rate(struct qm_ceetm_channel *channel, + pr_err("Fail to get the current channel shaper setting\n"); + return -EINVAL; + } +- ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + channel->cr_token_rate.whole = token_rate->whole; + channel->cr_token_rate.fraction = token_rate->fraction; + channel->cr_token_bucket_limit = token_limit; +@@ -4534,7 +4621,8 @@ int qman_ceetm_channel_set_excess_rate(struct qm_ceetm_channel *channel, + pr_err("Fail to get the current channel shaper setting\n"); + return -EINVAL; + } +- ++ ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + channel->er_token_rate.whole = token_rate->whole; + channel->er_token_rate.fraction = token_rate->fraction; + channel->er_token_bucket_limit = token_limit; +@@ -4618,7 +4706,7 @@ int qman_ceetm_channel_set_weight(struct qm_ceetm_channel *channel, + pr_err("This channel is a shaped one\n"); + return -EINVAL; + } +- ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + channel->cr_token_bucket_limit = token_limit; + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | + channel->idx); +@@ -4668,7 +4756,7 @@ int qman_ceetm_channel_set_group(struct qm_ceetm_channel *channel, int group_b, + pr_err("Can't query channel#%d's scheduler!\n", channel->idx); + return -EINVAL; + } +- ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config)); + config_opts.cqcid = cpu_to_be16(channel->idx); + config_opts.dcpid = channel->dcp_idx; + config_opts.gpc_combine_flag = !group_b; +@@ -4759,6 +4847,7 @@ int qman_ceetm_channel_set_group_er_eligibility(struct qm_ceetm_channel + channel->idx); + return -EINVAL; + } ++ memset(&csch_config, 0, sizeof(struct qm_mcc_ceetm_class_scheduler_config)); + csch_config.cqcid = cpu_to_be16(channel->idx); + csch_config.dcpid = channel->dcp_idx; + csch_config.gpc_combine_flag = csch_query.gpc_combine_flag; +@@ -4806,6 +4895,7 @@ int qman_ceetm_channel_set_cq_cr_eligibility(struct qm_ceetm_channel *channel, + channel->idx); + return -EINVAL; + } ++ memset(&csch_config, 0, sizeof(struct qm_mcc_ceetm_class_scheduler_config)); + csch_config.cqcid = cpu_to_be16(channel->idx); + csch_config.dcpid = channel->dcp_idx; + csch_config.gpc_combine_flag = csch_query.gpc_combine_flag; +@@ -4889,7 +4979,7 @@ int qman_ceetm_cq_claim(struct qm_ceetm_cq **cq, + pr_err("Can't allocate memory for CQ#%d!\n", idx); + return -ENOMEM; + } +- ++ memset(&cq_config, 0, sizeof(struct qm_mcc_ceetm_cq_config)); + list_add_tail(&p->node, &channel->class_queues); + p->idx = idx; + p->is_claimed = 1; +@@ -4938,7 +5028,7 @@ int qman_ceetm_cq_claim_A(struct qm_ceetm_cq **cq, + pr_err("Can't allocate memory for CQ#%d!\n", idx); + return -ENOMEM; + } +- ++ memset(&cq_config, 0, sizeof(struct qm_mcc_ceetm_cq_config)); + list_add_tail(&p->node, &channel->class_queues); + p->idx = idx; + p->is_claimed = 1; +@@ -4986,7 +5076,7 @@ int qman_ceetm_cq_claim_B(struct qm_ceetm_cq **cq, + pr_err("Can't allocate memory for CQ#%d!\n", idx); + return -ENOMEM; + } +- ++ memset(&cq_config, 0, sizeof(struct qm_mcc_ceetm_cq_config)); + list_add_tail(&p->node, &channel->class_queues); + p->idx = idx; + p->is_claimed = 1; +@@ -5040,7 +5130,7 @@ int qman_ceetm_set_queue_weight(struct qm_ceetm_cq *cq, + cq->parent->idx); + return -EINVAL; + } +- ++ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_class_scheduler_config)); + config_opts.cqcid = cpu_to_be16(cq->parent->idx); + config_opts.dcpid = cq->parent->dcp_idx; + config_opts.crem = query_result.crem; +@@ -5257,6 +5347,7 @@ int qman_ceetm_lfq_claim(struct qm_ceetm_lfq **lfq, + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; ++ memset(&lfqmt_config, 0, sizeof(struct qm_mcc_ceetm_lfqmt_config)); + p->idx = lfqid; + p->dctidx = (u16)(lfqid & CEETM_LFQMT_LFQID_LSB); + p->parent = cq->parent; diff --git a/patches/ask/upstream/kernel/0071-include__linux__if_bridge.h.patch b/patches/ask/upstream/kernel/0071-include__linux__if_bridge.h.patch new file mode 100644 index 0000000..6bf854c --- /dev/null +++ b/patches/ask/upstream/kernel/0071-include__linux__if_bridge.h.patch @@ -0,0 +1,28 @@ +diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h +index 6d4e3d1b1111..7c0d4c2e2222 100644 +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -65,6 +65,23 @@ void brioctl_set(int (*hook)(struct net *net, unsigned int cmd, + void __user *uarg)); + int br_ioctl_call(struct net *net, unsigned int cmd, void __user *uarg); + ++#if defined(CONFIG_CPE_FAST_PATH) ++struct brevent_fdb_update { ++ char *mac_addr; ++ struct net_device *dev; ++ struct net_device *brdev; ++}; ++ ++enum brevent_notif_type { ++ BREVENT_PORT_DOWN = 1, /* arg is struct net_device ptr */ ++ BREVENT_FDB_UPDATE /* arg is struct brevent_fdb_update ptr */ ++}; ++ ++int register_brevent_notifier(struct notifier_block *nb); ++int unregister_brevent_notifier(struct notifier_block *nb); ++int call_brevent_notifiers(unsigned long val, void *v); ++#endif ++ + #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) + int br_multicast_list_adjacent(struct net_device *dev, + struct list_head *br_ip_list); diff --git a/patches/ask/upstream/kernel/0078-include__net__xfrm.h.patch b/patches/ask/upstream/kernel/0078-include__net__xfrm.h.patch new file mode 100644 index 0000000..5e11b93 --- /dev/null +++ b/patches/ask/upstream/kernel/0078-include__net__xfrm.h.patch @@ -0,0 +1,76 @@ +diff --git a/include/net/xfrm.h b/include/net/xfrm.h +index 0a14daa..ff8a1ad 100644 +--- a/include/net/xfrm.h ++++ b/include/net/xfrm.h +@@ -196,6 +196,12 @@ struct xfrm_state { + struct hlist_node bysrc; + }; + struct hlist_node byspi; ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ struct hlist_node byh; ++ u16 handle; ++ u16 in_byh_hash; ++ u16 parent_sa_handle; /* handle of the old SA from which this SA is created using rekey */ ++#endif + struct hlist_node byseq; + struct hlist_node state_cache; + struct hlist_node state_cache_input; +@@ -314,6 +320,11 @@ struct xfrm_state { + /* Private data of this transformer, format is opaque, + * interpreted by xfrm_type methods. */ + void *data; ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ /* Intended direction of this state, used for offloading */ ++ int offloaded; ++ u64 curr_time; ++#endif + u8 dir; + + const struct xfrm_mode_cbs *mode_cbs; +@@ -337,6 +348,13 @@ enum { + XFRM_STATE_EXPIRED, + XFRM_STATE_DEAD + }; ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++enum { ++ XFRM_STATE_DIR_UNKNOWN, ++ XFRM_STATE_DIR_IN, ++ XFRM_STATE_DIR_OUT, ++}; ++#endif + + /* callback structure passed from either netlink or pfkey */ + struct km_event { +@@ -1173,6 +1191,32 @@ struct sec_path { + + struct sec_path *secpath_set(struct sk_buff *skb); + ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++struct xfrm_input_shared { ++ struct sk_buff *skb; ++ int xfrm_nr, first, xfrm_encap; ++ struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; ++ __u16 encap_type; ++ int decaps; ++ u32 seq, spi; ++ unsigned int nhoff; ++ int nexthdr; ++ int (*callback)(struct xfrm_input_shared *sh); ++ atomic_t refcnt; ++}; ++ ++static inline void xfrm_shared_get(struct xfrm_input_shared *sh) ++{ ++ atomic_inc(&sh->refcnt); ++} ++ ++static inline void xfrm_shared_put(struct xfrm_input_shared *sh) ++{ ++ if (atomic_dec_and_test(&sh->refcnt)) ++ kfree(sh); ++} ++#endif ++ + static inline void + secpath_reset(struct sk_buff *skb) + { diff --git a/patches/ask/upstream/kernel/0086-include__uapi__linux__netfilter__nfnetlink_conntrack.h.patch b/patches/ask/upstream/kernel/0086-include__uapi__linux__netfilter__nfnetlink_conntrack.h.patch new file mode 100644 index 0000000..8ba47df --- /dev/null +++ b/patches/ask/upstream/kernel/0086-include__uapi__linux__netfilter__nfnetlink_conntrack.h.patch @@ -0,0 +1,36 @@ +diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h +index 43233af..2e401ae 100644 +--- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h ++++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h +@@ -58,6 +58,10 @@ enum ctattr_type { + CTA_FILTER, + CTA_STATUS_MASK, + CTA_TIMESTAMP_EVENT, ++ CTA_LAYERSCAPE_FP_ORIG, ++ CTA_LAYERSCAPE_FP_REPLY, ++ CTA_QOSCONNMARK, ++ CTA_QOSCONNMARK_PAD, + __CTA_MAX + }; + #define CTA_MAX (__CTA_MAX - 1) +@@ -243,6 +247,20 @@ enum ctattr_secctx { + }; + #define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1) + ++enum ctattr_comcerto_fp { ++ CTA_COMCERTO_FP_UNSPEC, ++ CTA_COMCERTO_FP_MARK, ++ CTA_COMCERTO_FP_IFINDEX, ++ CTA_COMCERTO_FP_IIF, ++ CTA_COMCERTO_FP_UNDERLYING_IIF, ++ CTA_COMCERTO_FP_UNDERLYING_VID, ++#ifndef IPSEC_FLOW_CACHE ++ CTA_COMCERTO_FP_XFRM_HANDLE, ++#endif ++ __CTA_COMCERTO_FP_MAX ++}; ++#define CTA_COMCERTO_FP_MAX (__CTA_COMCERTO_FP_MAX - 1) ++ + enum ctattr_stats_cpu { + CTA_STATS_UNSPEC, + CTA_STATS_SEARCHED, /* no longer used */ diff --git a/patches/ask/upstream/kernel/0100-net__bridge__br_private.h.patch b/patches/ask/upstream/kernel/0100-net__bridge__br_private.h.patch new file mode 100644 index 0000000..189ee5b --- /dev/null +++ b/patches/ask/upstream/kernel/0100-net__bridge__br_private.h.patch @@ -0,0 +1,28 @@ +diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h +index 9a3d6f2b8c1e..b4f7b2c1d9aa 100644 +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -620,6 +620,11 @@ struct br_input_skb_cb { + #endif + + u32 backup_nhid; ++ ++#ifdef CONFIG_CPE_FAST_PATH ++ u16 vid; ++ u8 untagged:1; ++#endif + }; + + #define BR_INPUT_SKB_CB(__skb) ((struct br_input_skb_cb *)(__skb)->cb) +@@ -859,6 +864,11 @@ int br_fdb_add_local(struct net_bridge *br, struct net_bridge_port *source, + void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, + const unsigned char *addr, u16 vid, unsigned long flags); + ++#if defined(CONFIG_CPE_FAST_PATH) ++extern void br_fdb_register_can_expire_cb(int(*cb)(unsigned char *mac_addr, struct net_device *dev)); ++extern void br_fdb_deregister_can_expire_cb(void); ++#endif ++ + int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr, u16 vid, + bool *notified, struct netlink_ext_ack *extack); diff --git a/patches/ask/upstream/kernel/0103-net__core__dev.c.patch b/patches/ask/upstream/kernel/0103-net__core__dev.c.patch new file mode 100644 index 0000000..78bdc3e --- /dev/null +++ b/patches/ask/upstream/kernel/0103-net__core__dev.c.patch @@ -0,0 +1,131 @@ +diff --git a/net/core/dev.c b/net/core/dev.c +index 2acfa44..02e304b 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -176,6 +176,10 @@ static int call_netdevice_notifiers_extack(unsigned long val, + struct net_device *dev, + struct netlink_ext_ack *extack); + ++#if defined(CONFIG_CPE_FAST_PATH) ++static fp_iface_stats_get fast_path_stats_get; ++#endif ++ + static DEFINE_MUTEX(ifalias_mutex); + + /* protects napi_hash addition/deletion and napi_gen_id */ +@@ -4002,9 +4006,15 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device + skb = segs; + } + } else { +- if (skb_needs_linearize(skb, features) && +- __skb_linearize(skb)) +- goto out_kfree_skb; ++ /* Linearize only if IPsec policy is not selected. */ ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ if (!skb->ipsec_offload) ++#endif ++ { ++ if (skb_needs_linearize(skb, features) && ++ __skb_linearize(skb)) ++ goto out_kfree_skb; ++ } + + /* If packet is not checksummed and device does not + * support checksumming for this protocol, complete +@@ -4792,6 +4802,40 @@ out: + } + EXPORT_SYMBOL(__dev_queue_xmit); + ++#if defined(CONFIG_CPE_FAST_PATH) ++/* WiFi IPsec offload hook - allows cdx to intercept packets for IPsec ++ * processing when the packet is transmitted on a wifi interface. ++ */ ++dpaa_wifi_xmit_local_hook_t dpaa_wifi_xmit_local_ipsec_handler; ++EXPORT_SYMBOL(dpaa_wifi_xmit_local_ipsec_handler); ++ ++/* Register a hook function for IPsec offload on wifi interfaces. */ ++int dpa_register_wifi_xmit_local_hook(dpaa_wifi_xmit_local_hook_t hookfn) ++{ ++ if (dpaa_wifi_xmit_local_ipsec_handler) { ++ pr_warn("%s: hook already registered\n", __func__); ++ return -1; ++ } ++ dpaa_wifi_xmit_local_ipsec_handler = hookfn; ++ return 0; ++} ++EXPORT_SYMBOL(dpa_register_wifi_xmit_local_hook); ++ ++/* Unregister the IPsec offload hook. */ ++void dpa_unregister_wifi_xmit_local_hook(void) ++{ ++ dpaa_wifi_xmit_local_ipsec_handler = NULL; ++} ++EXPORT_SYMBOL(dpa_unregister_wifi_xmit_local_hook); ++ ++/* Original dev_queue_xmit - called when wifi hook is not applicable. */ ++int original_dev_queue_xmit(struct sk_buff *skb) ++{ ++ return __dev_queue_xmit(skb, NULL); ++} ++EXPORT_SYMBOL(original_dev_queue_xmit); ++#endif ++ + int __dev_direct_xmit(struct sk_buff *skb, u16 queue_id) + { + struct net_device *dev = skb->dev; +@@ -5862,6 +5906,15 @@ static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc, + + trace_netif_receive_skb(skb); + ++#ifdef CONFIG_CPE_FAST_PATH ++ /* ifindex of device we arrived on, now skb->skb_iif ++ * always tracks skb->dev. ++ */ ++ if (!skb->iif_index) ++ skb->iif_index = skb->dev->ifindex; ++ if (!skb->underlying_iif) ++ skb->underlying_iif = skb->dev->ifindex; ++#endif + orig_dev = skb->dev; + + skb_reset_network_header(skb); +@@ -7627,9 +7680,9 @@ static int __napi_poll(struct napi_struct *n, bool *repoll) + return work; + } + + /* Flush too old packets. If HZ < 1000, flush all packets */ +- gro_flush_normal(&n->gro, HZ >= 1000); ++ gro_flush(&n->gro, HZ >= 1000); + + /* Some drivers may have called napi_schedule + * prior to exhausting their budget. + */ +@@ -11762,10 +11812,28 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, + storage->rx_otherhost_dropped += READ_ONCE(core_stats->rx_otherhost_dropped); + } + } ++#if defined(CONFIG_CPE_FAST_PATH) ++ if (fast_path_stats_get) ++ fast_path_stats_get(dev, storage); ++#endif + return storage; + } + EXPORT_SYMBOL(dev_get_stats); + ++#if defined(CONFIG_CPE_FAST_PATH) ++void dev_fp_stats_get_register(fp_iface_stats_get func) ++{ ++ fast_path_stats_get = func; ++} ++EXPORT_SYMBOL(dev_fp_stats_get_register); ++ ++void dev_fp_stats_get_deregister(void) ++{ ++ fast_path_stats_get = NULL; ++} ++EXPORT_SYMBOL(dev_fp_stats_get_deregister); ++#endif ++ + /** + * dev_fetch_sw_netstats - get per-cpu network device statistics + * @s: place to store stats diff --git a/patches/ask/upstream/kernel/0108-net__ipv4__ip_output.c.patch b/patches/ask/upstream/kernel/0108-net__ipv4__ip_output.c.patch new file mode 100644 index 0000000..6a16fb0 --- /dev/null +++ b/patches/ask/upstream/kernel/0108-net__ipv4__ip_output.c.patch @@ -0,0 +1,79 @@ +diff -uNr a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c +--- a/net/ipv4/ip_output.c 2026-05-08 18:06:42.017639432 +0000 ++++ b/net/ipv4/ip_output.c 2026-05-08 18:06:42.100831810 +0000 +@@ -103,6 +103,17 @@ + { + struct iphdr *iph = ip_hdr(skb); + ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) ++ /* ++ * The tunnel header is not added in slow path and packet may be ipv6 ++ * (IPv6 traffic and IPv4 IPSec tunnel) in case IPv6 over IPv4 IPsec ++ * tunnel. When it assumes as IPv4 and accessing IP header from SKB ++ * causing invalid accesses it leading to kernel panic. ++ * Avoiding ip header checks. ++ */ ++ if ((skb->ipsec_offload) && (iph->version == 6)) ++ goto sendout; ++#endif /* endif for CONFIG_INET_IPSEC_OFFLOAD */ + IP_INC_STATS(net, IPSTATS_MIB_OUTREQUESTS); + + iph_set_totlen(iph, skb->len); +@@ -115,8 +126,17 @@ + if (unlikely(!skb)) + return 0; + ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) ++sendout: ++#endif + skb->protocol = htons(ETH_P_IP); + ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) ++ if (skb->ipsec_offload) { ++ dst_output(net, sk, skb); ++ return 0; ++ } else ++#endif /* endif for CONFIG_INET_IPSEC_OFFLOAD */ + return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, + net, sk, skb, NULL, skb_dst_dev(skb), + dst_output); +@@ -309,7 +329,11 @@ + if (skb_is_gso(skb)) + return ip_finish_output_gso(net, sk, skb, mtu); + +- if (skb->len > mtu || IPCB(skb)->frag_max_size) ++ if ( ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) ++ (skb->ipsec_offload == 0) && ++#endif ++ (skb->len > mtu || IPCB(skb)->frag_max_size)) + return ip_fragment(net, sk, skb, mtu, ip_finish_output2); + + return ip_finish_output2(net, sk, skb); +@@ -435,6 +459,16 @@ + skb->dev = dev; + skb->protocol = htons(ETH_P_IP); + ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) ++ /* Bypass invoking post routing hooks since the tunnel header and ESP ++ * processing is not done in slow path for IPSec offloaded cases ++ */ ++ if (skb->ipsec_offload) { ++ ret_val = ip_finish_output(net, sk, skb); ++ rcu_read_unlock(); ++ return ret_val; ++ } ++#endif + ret_val = NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, + net, sk, skb, indev, dev, + ip_finish_output, +@@ -559,6 +593,9 @@ + skb_dst_copy(to, from); + to->dev = from->dev; + to->mark = from->mark; ++#if defined(CONFIG_CPE_FAST_PATH) ++ to->qosmark = from->qosmark; ++#endif + + skb_copy_hash(to, from); + diff --git a/patches/ask/upstream/kernel/0112-net__ipv6__ip6_output.c.patch b/patches/ask/upstream/kernel/0112-net__ipv6__ip6_output.c.patch new file mode 100644 index 0000000..3158c5f --- /dev/null +++ b/patches/ask/upstream/kernel/0112-net__ipv6__ip6_output.c.patch @@ -0,0 +1,73 @@ +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index f904739..e26e743 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -80,6 +80,13 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff * + + hdr = ipv6_hdr(skb); + daddr = &hdr->daddr; ++#if defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ /* For IPv4 over IPv6 IPsec tunnel cases, just send the packet out ++ * since the packet is IPv4 ++ */ ++ if((skb->ipsec_offload) && (hdr->version == 4)) ++ goto sendout; ++#endif /* endif for CONFIG_INET6_IPSEC_OFFLOAD */ + if (ipv6_addr_is_multicast(daddr)) { + if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) && + ((mroute6_is_socket(net, skb) && +@@ -111,6 +118,9 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff * + } + } + ++#if defined(CONFIG_INET6_IPSEC_OFFLOAD) ++sendout: ++#endif /* endif for CONFIG_INET6_IPSEC_OFFLOAD */ + if (lwtunnel_xmit_redirect(dst->lwtstate)) { + int res = lwtunnel_xmit(skb); + +@@ -202,8 +212,15 @@ static int __ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff + if (skb_is_gso(skb)) + return ip6_finish_output_gso(net, sk, skb, mtu); + +- if (skb->len > mtu || +- (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size)) ++ if ( ++#if defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ /* If ipsec offload is there, do not do fragment. So, when IPSec ++ * offload is enabled it directly calls ip6_finish_output2 ++ */ ++ (skb->ipsec_offload == 0) && ++#endif ++ ((skb->len > mtu) || ++ (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))) + return ip6_fragment(net, sk, skb, ip6_finish_output2); + + return ip6_finish_output2(net, sk, skb); +@@ -244,6 +261,16 @@ int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb) + return 0; + } + ++#if defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ /* Bypass invoking post routing hooks since the tunnel header and ESP ++ * processing is not done in slow path for IPSec offloaded cases ++ */ ++ if (skb->ipsec_offload) { ++ ret = ip6_finish_output(net, sk, skb); ++ rcu_read_unlock(); ++ return ret; ++ } ++#endif + ret = NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, + net, sk, skb, indev, dev, + ip6_finish_output, +@@ -697,6 +724,9 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) + skb_dst_set(to, dst_clone(skb_dst(from))); + to->dev = from->dev; + to->mark = from->mark; ++#if defined(CONFIG_CPE_FAST_PATH) ++ to->qosmark = from->qosmark; ++#endif + + skb_copy_hash(to, from); + diff --git a/patches/ask/upstream/kernel/0115-net__ipv6__output_core.c.patch b/patches/ask/upstream/kernel/0115-net__ipv6__output_core.c.patch new file mode 100644 index 0000000..65c7704 --- /dev/null +++ b/patches/ask/upstream/kernel/0115-net__ipv6__output_core.c.patch @@ -0,0 +1,38 @@ +diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c +index 1c9b283..5682505 100644 +--- a/net/ipv6/output_core.c ++++ b/net/ipv6/output_core.c +@@ -130,7 +130,19 @@ int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) + len = skb->len - sizeof(struct ipv6hdr); + if (len > IPV6_MAXPLEN) + len = 0; ++ ++#if defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ /* ++ * Since tunnel header is not added in slow path and route cache entry ++ * lookup is changed to perform lookup on tunnel header, dst_output() ++ * may reach IPv6 output for IPv4-over-IPv6 IPsec tunnel packets. Only ++ * update the IPv6 payload length when the skb really carries IPv6 here. ++ */ ++ if (ipv6_hdr(skb)->version == 6) ++ ipv6_hdr(skb)->payload_len = htons(len); ++#else + ipv6_hdr(skb)->payload_len = htons(len); ++#endif + IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); + + /* if egress device is enslaved to an L3 master device pass the +@@ -142,6 +154,13 @@ int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) + + skb->protocol = htons(ETH_P_IPV6); + ++#if defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ if (skb->ipsec_offload) { ++ dst_output(net, sk, skb); ++ return 0; ++ } ++#endif ++ + return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, + net, sk, skb, NULL, skb_dst_dev(skb), + dst_output); diff --git a/patches/ask/upstream/kernel/0119-net__key__af_key.c.patch b/patches/ask/upstream/kernel/0119-net__key__af_key.c.patch new file mode 100644 index 0000000..39a33ab --- /dev/null +++ b/patches/ask/upstream/kernel/0119-net__key__af_key.c.patch @@ -0,0 +1,949 @@ +diff --git a/net/key/af_key.c b/net/key/af_key.c +index c56bb4f451e6..7225be6880e1 100644 +--- a/net/key/af_key.c ++++ b/net/key/af_key.c +@@ -26,8 +26,184 @@ + #include + #include + #include ++#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD) ++#include ++#endif + + #include ++#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD) ++#include ++#define NLKEY_SUPPORT 1 ++#else ++#undef NLKEY_SUPPORT ++#endif ++ ++#ifdef NLKEY_SUPPORT ++#include ++#include ++#include ++#include ++ ++ ++extern int xfrm_get_tos(struct flowi *fl, int family); ++ ++ ++#define NLKEY_SA_CREATE 0x0A01 ++#define NLKEY_SA_DELETE 0x0A02 ++#define NLKEY_SA_FLUSH 0x0A03 ++#define NLKEY_SA_SET_KEYS 0x0A04 ++#define NLKEY_SA_SET_TUNNEL 0x0A05 ++#define NLKEY_SA_SET_NATT 0x0A06 ++#define NLKEY_SA_SET_STATE 0x0A07 ++#define NLKEY_SA_SET_LIFETIME 0x0A08 ++#define NLKEY_SA_NOTIFY 0x0A09 ++#define NLKEY_SA_INFO_UPDATE 0x0A0C ++#define NLKEY_SA_SET_OFFLOAD 0x0A0D ++#define NLKEY_FLOW_ADD 0x0A11 ++#define NLKEY_FLOW_REMOVE 0x0A12 ++#define NLKEY_FLOW_NOTIFY 0x0A13 ++#define NLKEY_NULL_MSG 0x0000 ++ ++#define NLKEY_HDR_LEN 4 ++#define NLKEY_MSG_LEN 256 ++ ++#define NLKEY_MAX_NUM_KEYS 2 ++#define NLKEY_MAX_KEY_LEN (512 / 8) ++ ++struct nlkey_msg { ++ /* message data */ ++ unsigned short fcode; ++ unsigned short length; ++ unsigned short payload[(NLKEY_MSG_LEN /sizeof(unsigned short))]; ++}; ++/* sizeof(nlkey_msg) = 4 + 256 */ ++ ++struct nlkey_sa_id { ++ unsigned int spi; ++ unsigned char sa_type; ++ unsigned char proto_family; ++ unsigned char replay_window; ++#define NLKEY_SAFLAGS_ESN 0x1 ++#define NLKEY_SAFLAGS_INBOUND 0x2 ++ unsigned char flags; ++ unsigned int dst_ip[4]; ++ unsigned int src_ip[4]; ++ unsigned short mtu; ++ unsigned short dev_mtu; ++ ++}; ++/* sizeof(nlkey_sa_id) = 24 */ ++ ++struct nlkey_sa_create { ++ unsigned short sagd; ++ unsigned short parent_sa_sagd; /*sagd value of old SA from which this SA is rekeyed.*/ ++ struct nlkey_sa_id said; ++}; ++/* sizeof(nlkey_sa_delete) = 28 */ ++ ++struct nlkey_sa_delete { ++ unsigned short sagd; ++ unsigned short rsvd; ++}; ++/* sizeof(nlkey_sa_delete) = 4 */ ++ ++struct nlkey_sa_set_tunnel { ++ unsigned short sagd; ++ unsigned char rsvd; ++ unsigned char proto_family; ++ union { ++ struct iphdr ipv4h; ++ struct ipv6hdr ipv6h; ++ } h; ++}; ++/* sizeof(nlkey_sa_set_tunnel) = 36 */ ++ ++struct nlkey_sa_set_natt { ++ unsigned short sagd; ++ unsigned short sport; ++ unsigned short dport; ++ unsigned short rsvd; ++}; ++/* sizeof(nlkey_sa_set_natt) = 4 */ ++ ++struct nlkey_sa_set_state { ++ unsigned short sagd; ++ unsigned short parent_sa_sagd; ++ unsigned short state; ++ unsigned short rsvd2; ++}; ++/* sizeof(nlkey_sa_set_natt) = 8 */ ++ ++struct nlkey_key_desc { ++ unsigned short key_bits; ++ unsigned char key_alg; ++ unsigned char key_type; ++ unsigned char key[NLKEY_MAX_KEY_LEN]; ++}; ++/* sizeof(nlkey_key_desc) = 36 */ ++ ++struct nlkey_sa_set_keys { ++ unsigned short sagd; ++ unsigned short rsvd; ++ unsigned short num_keys; ++ unsigned short rsvd2; ++ struct nlkey_key_desc keys[NLKEY_MAX_NUM_KEYS]; ++}; ++/* sizeof(nlkey_sa_set_keys) = 80 */ ++ ++struct nlkey_lifetime_desc { ++ unsigned int allocations; ++ unsigned int bytes[2]; ++}; ++/* sizeof(nlkey_sa_set_lifetime) = 12 */ ++ ++struct nlkey_sa_set_lifetime { ++ unsigned short sagd; ++ unsigned short rsvd; ++ struct nlkey_lifetime_desc hard_time; ++ struct nlkey_lifetime_desc soft_time; ++ struct nlkey_lifetime_desc current_time; ++}; ++/* sizeof(nlkey_sa_set_lifetime) = 40 */ ++ ++/* SA notifications */ ++#define IPSEC_SOFT_EXPIRE 0 ++#define IPSEC_HARD_EXPIRE 1 ++ ++struct nlkey_sa_notify { ++ unsigned short sagd; ++ unsigned short rsvd; ++ unsigned int action; ++}; ++/* sizeof(nlkey_sa_notify) = 8 */ ++ ++/* SA Info update */ ++ ++struct nlkey_sa_info { ++ unsigned short sagd; ++ unsigned short rsvd; ++ unsigned long long bytes; ++ unsigned long long packets; ++}; ++/* sizeof(nlkey_sa_info) = */ ++ ++ ++static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c); ++static void ipsec_nlkey_rcv(struct sk_buff *skb); ++static void ipsec_nlkey_init(void); ++static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code); ++static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x, ++ const struct km_event *c, unsigned short *msg_id); ++static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x, const struct km_event *c, struct nlkey_sa_id *said); ++ ++void flow_cache_remove(const struct flowi *fl, unsigned short family, ++ unsigned short dir); ++/* netlink NETLINK_KEY socket */ ++struct sock *nlkey_socket = NULL; ++ ++#endif ++/************************************************************************************/ ++ + + #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x)) + #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x)) +@@ -876,6 +1051,10 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, + sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP; + if (x->props.flags & XFRM_STATE_NOPMTUDISC) + sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC; ++#ifdef NLKEY_SUPPORT ++ if (x->props.flags & XFRM_STATE_ESN) ++ sa->sadb_sa_flags |= SADB_SAFLAGS_ESN; ++#endif + + /* hard time */ + if (hsc & 2) { +@@ -908,6 +1087,11 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, + lifetime->sadb_lifetime_bytes = x->curlft.bytes; + lifetime->sadb_lifetime_addtime = x->curlft.add_time; + lifetime->sadb_lifetime_usetime = x->curlft.use_time; ++ ++#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ lifetime->sadb_lifetime_usetime = x->curr_time; ++#endif ++ + /* src address */ + addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size); + addr->sadb_address_len = +@@ -1133,6 +1317,10 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, + x->props.flags |= XFRM_STATE_DECAP_DSCP; + if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC) + x->props.flags |= XFRM_STATE_NOPMTUDISC; ++#ifdef NLKEY_SUPPORT ++ if (sa->sadb_sa_flags & SADB_SAFLAGS_ESN) ++ x->props.flags |= XFRM_STATE_ESN; ++#endif + + lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD - 1]; + if (lifetime != NULL) { +@@ -3076,6 +3264,12 @@ static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c) + struct net *net = x ? xs_net(x) : c->net; + struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id); + ++ ++#ifdef NLKEY_SUPPORT ++ /* send message to the user space through NETLINK_KEY socket*/ ++ ipsec_nlkey_send(net, x, c); ++#endif ++ + if (atomic_read(&net_pfkey->socks_nr) == 0) + return 0; + +@@ -3863,6 +4057,687 @@ static struct xfrm_mgr pfkeyv2_mgr = + .is_alive = pfkey_is_alive, + }; + ++ ++#ifdef NLKEY_SUPPORT ++extern struct xfrm_state *xfrm_state_lookup_byhandle(struct net *net, u16 handle); ++ ++static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code) ++{ ++ unsigned nlkey_code; ++ ++ switch (sa_code) ++ { ++ case XFRM_MSG_DELSA: ++ nlkey_code = NLKEY_SA_DELETE; ++ break; ++ case XFRM_MSG_NEWSA: ++ case XFRM_MSG_UPDSA: ++ nlkey_code = NLKEY_SA_CREATE; ++ break; ++ case XFRM_MSG_FLUSHSA: ++ nlkey_code = NLKEY_SA_FLUSH; ++ break; ++ case XFRM_MSG_EXPIRE: ++ nlkey_code = NLKEY_SA_SET_STATE; ++ break; ++ default: ++ nlkey_code = NLKEY_NULL_MSG; ++ break; ++ } ++ ++ return nlkey_code; ++} ++ ++static void ipsec_nlkey_rcv(struct sk_buff *skb) ++{ ++ struct nlmsghdr *nlh = NULL; ++ struct nlkey_msg *msg = NULL; ++ struct flowi flow; ++ unsigned short *p; ++ unsigned short family, dir; ++ struct xfrm_state *x; ++ struct nlkey_sa_notify sa_notify_msg; ++ struct nlkey_sa_info sa_info_msg; ++ ++ /* extract message from skb */ ++ nlh = (struct nlmsghdr *)skb->data; ++ ++ msg = (struct nlkey_msg *)NLMSG_DATA(nlh); ++ ++ //printk(KERN_INFO "ipsec_nlkey_rcv fcode: 0x%x length: %d bytes\n",msg->fcode,msg->length); ++ ++ /* process command received from user space */ ++ switch(msg->fcode) ++ { ++ case NLKEY_FLOW_REMOVE: ++ //printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_FLOW_REMOVE\n"); ++ p = msg->payload; ++ memcpy(&flow, p, sizeof(struct flowi)); p += sizeof(struct flowi)/2; ++ family = *p; p++; ++ dir = *p; p++; ++ flow_cache_remove(&flow, family, dir); ++ break; ++ ++ case NLKEY_SA_NOTIFY: ++ //printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_SA_NOTIFY\n"); ++ memcpy(&sa_notify_msg, msg->payload, sizeof(struct nlkey_sa_notify)); ++ x = xfrm_state_lookup_byhandle(&init_net, sa_notify_msg.sagd); ++ if (x) { ++ spin_lock(&x->lock); ++ ++ if (sa_notify_msg.action) { ++ // hard expired ++ x->km.state = XFRM_STATE_EXPIRED; ++ hrtimer_start(&x->mtimer, ktime_set(0,0), HRTIMER_MODE_REL_SOFT); ++ } ++ else if (!x->km.dying) { ++ x->km.dying = 1; ++ km_state_expired(x, 0, 0); ++ } ++ ++ spin_unlock(&x->lock); ++ xfrm_state_put(x); ++ } ++ break; ++ ++ case NLKEY_SA_INFO_UPDATE: ++ memcpy(&sa_info_msg, msg->payload, sizeof(struct nlkey_sa_info)); ++ ++ x = xfrm_state_lookup_byhandle(&init_net,sa_info_msg.sagd); ++ if (x) { ++ spin_lock(&x->lock); ++ ++ if (x->curlft.bytes != sa_info_msg.bytes) ++ x->curr_time = ktime_get_real_seconds(); ++ ++ x->curlft.bytes = sa_info_msg.bytes; ++ x->curlft.packets = sa_info_msg.packets; ++ ++ spin_unlock(&x->lock); ++ xfrm_state_put(x); ++ } ++ break; ++ ++ case NLKEY_SA_SET_OFFLOAD: ++ memcpy(&sa_notify_msg, msg->payload, sizeof(struct nlkey_sa_notify)); ++ x = xfrm_state_lookup_byhandle(&init_net,sa_notify_msg.sagd); ++ if (x) { ++ spin_lock(&x->lock); ++ if(sa_notify_msg.action) ++ x->offloaded = 1; ++ else ++ x->offloaded = 0; ++ spin_unlock(&x->lock); ++ xfrm_state_put(x); ++ } ++ break; ++ default: ++ //printk(KERN_INFO "ipsec_nlkey_rcv fcode 0x%x not supported\n", msg->fcode); ++ break; ++ } ++ ++} ++static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x, ++ const struct km_event *c, struct nlkey_sa_id *said) ++{ ++ ++ struct flowi fl; ++ int tos; ++ xfrm_address_t saddr, daddr; ++ struct dst_entry *dst; ++ struct rt6_info *rt; ++ int rc = 0; ++ int oif = 0; ++ ++ memset(&fl, 0, sizeof(struct flowi)); ++ ++ /* SPI */ ++ said->spi = x->id.spi; ++ /* SA Type (AH or ESP) */ ++ said->sa_type = x->id.proto; ++ /* Protocol Family (IPv4 or IPv6) */ ++ said->proto_family = x->props.family; ++ /* Replay window */ ++ said->replay_window = x->props.replay_window; ++ /* Destination IP Address */ ++ if(x->props.family == AF_INET6) { ++ memcpy(&said->dst_ip, x->id.daddr.a6, sizeof(struct in6_addr)); ++ fl.u.ip6.daddr = *(struct in6_addr *)x->id.daddr.a6; ++ memcpy(&said->src_ip, x->props.saddr.a6, sizeof(struct in6_addr)); ++ } ++ else { ++ said->dst_ip[0] = x->id.daddr.a4; ++ fl.u.ip4.daddr = x->id.daddr.a4; ++ said->src_ip[0] = x->props.saddr.a4; ++ } ++ said->mtu = 0; ++ ++ if(x->props.flags & XFRM_STATE_ESN) ++ said->flags = NLKEY_SAFLAGS_ESN; ++ xfrm_flowi_addr_get(&fl, &saddr, &daddr, x->props.family); ++ ++ tos = xfrm_get_tos(&fl, x->props.family); ++ if (tos < 0) { ++ printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__); ++ rc = -1; ++ goto error; ++ } ++ ++ switch (x->props.family) ++ { ++ case AF_INET: ++ if (!__ip_route_output_key(net, &(fl.u.ip4))) ++ { ++ printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__); ++ rc = -1; ++ goto error; ++ } ++ oif = fl.u.ip4.flowi4_oif; ++ break; ++ ++ case AF_INET6: ++ rt = rt6_lookup(net, &fl.u.ip6.daddr, NULL, 0, NULL, 0); ++ if ((!rt) || (!rt->dst.dev)) ++ { ++ printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__); ++ rc = -1; ++ goto error; ++ } ++ oif = rt->dst.dev->ifindex; ++ break; ++ } ++ ++ { ++ struct xfrm_dst_lookup_params params = { ++ .net = net, ++ .dscp = inet_dsfield_to_dscp(tos), ++ .oif = oif, ++ .saddr = NULL, ++ .daddr = &daddr, ++ .mark = xfrm_smark_get(0, x), ++ }; ++ dst = __xfrm_dst_lookup(x->props.family, ¶ms); ++ } ++ if (IS_ERR(dst)) { ++ printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__); ++ rc = -1; ++ goto error; ++ } ++ ++ if (strcmp(dst->dev->name, "lo") == 0) ++ said->flags |= NLKEY_SAFLAGS_INBOUND; ++ ++ said->dev_mtu = dst_mtu(dst); ++ said->mtu = xfrm_state_mtu(x,dst_mtu(dst)); ++ ++ dst_release(dst); ++error: ++ return rc; ++} ++ ++static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x, ++ const struct km_event *c, unsigned short *msg_id) ++{ ++ struct nlkey_sa_id sa_id_msg; ++ struct nlkey_sa_create sa_create_msg; ++ struct nlkey_sa_delete sa_delete_msg; ++ struct nlkey_sa_set_keys sa_set_keys_msg; ++ struct nlkey_sa_set_tunnel sa_set_tunnel_msg; ++ struct nlkey_sa_set_natt sa_set_natt_msg; ++ struct nlkey_sa_set_state sa_set_state_msg; ++ struct nlkey_sa_set_lifetime sa_set_lifetime_msg; ++ struct nlkey_msg msg; ++ struct sk_buff *skb = NULL; ++ struct nlmsghdr *nlh = NULL; ++ gfp_t allocation = GFP_ATOMIC; //This may called from atomic context ++ unsigned char tunnel, keys, natt, state, lifetime; ++ ++ /* supported SA informations */ ++ keys = 1; state = 1; tunnel = 1; lifetime = 1; natt = 1; ++ ++ /* next message to build */ ++ memset(&msg, 0, sizeof(struct nlkey_msg)); ++ msg.fcode = *msg_id; ++ ++ //printk(KERN_INFO "\n\nipsec_xfrm2nlkey: processing event 0x%x\n", msg.fcode); ++ ++ switch (msg.fcode) ++ { ++ case NLKEY_SA_CREATE: ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_CREATE\n"); ++ if(x) { ++ /* some check before builing message */ ++ if(x->id.proto != IPPROTO_ESP) { ++ printk(KERN_ERR "protocol %d not supported in fast path.\n", x->id.proto); ++ *msg_id = NLKEY_NULL_MSG; ++ goto exit; ++ } ++ ++ memset(&sa_create_msg, 0, sizeof(struct nlkey_sa_create)); ++ ++ /* SA global handler */ ++ sa_create_msg.sagd = x->handle; ++ ++ sa_create_msg.parent_sa_sagd = x->parent_sa_handle; ++ ++ /* SA identifier */ ++ if(ipsec_nlkey_set_said(net, x, c, &sa_create_msg.said) < 0) ++ { ++ printk(KERN_ERR "%s: set sa ID failed\n", __func__); ++ *msg_id = NLKEY_NULL_MSG; /* next message */ ++ goto exit; ++ } ++ memcpy(msg.payload, &sa_create_msg, sizeof(struct nlkey_sa_create)); ++ msg.length = sizeof(struct nlkey_sa_create); ++ *msg_id = NLKEY_SA_SET_KEYS; /* next message */ ++ } else { ++ *msg_id = NLKEY_NULL_MSG; /* next message */ ++ goto exit; ++ } ++ ++ break; ++ ++ case NLKEY_SA_SET_KEYS: ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_KEYS\n"); ++ if(keys) { ++ memset(&sa_set_keys_msg, 0, sizeof(struct nlkey_sa_set_keys)); ++ ++ /* SA global handler */ ++ sa_set_keys_msg.sagd = x->handle; ++ ++ /* auth key */ ++ if(x->aalg) { ++ if (x->aalg->alg_key_len) { ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->aalg->alg_key_len; ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.aalgo; ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 0; ++ memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->aalg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8)); ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: AUTH - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits); ++ sa_set_keys_msg.num_keys++; ++ } ++ } ++ /* encrypt key */ ++ if(x->ealg) { ++ if (x->ealg->alg_key_len) { ++ ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->ealg->alg_key_len; ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.ealgo; ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 1; ++ memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->ealg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8)); ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: ENCRYPT - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits); ++ sa_set_keys_msg.num_keys++; ++ } ++ } ++ /* combined key */ ++ if (x->aead) { ++ if (x->aead->alg_key_len) { ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->aead->alg_key_len; ++ if (strstr(x->aead->alg_name, "rfc4106(gcm")) /* AES GCM support */ ++ { ++ if (x->aead->alg_icv_len == 64) ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_GCM_ICV8; ++ else if (x->aead->alg_icv_len == 96) ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_GCM_ICV12; ++ else if (x->aead->alg_icv_len == 128) ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_GCM_ICV16; ++ } ++ else if (strstr(x->aead->alg_name, "ccm")) /* AES CCM */ ++ { ++ if (x->aead->alg_icv_len == 64) ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_CCM_ICV8; ++ else if (x->aead->alg_icv_len == 96) ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_CCM_ICV12; ++ else if (x->aead->alg_icv_len == 128) ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_CCM_ICV16; ++ } ++ else if (strstr(x->aead->alg_name, "rfc4543(gcm")) /* AES GMAC defined in RFC 4543 derived from AES GCM */ ++ { ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_NULL_AES_GMAC; ++ } ++ ++ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 1; ++ memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->aead->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits/ 8)); ++ /* ++ printk(KERN_INFO "ipsec_xfrm2nlkey: ENCRYPT -alg name %s algo %d key %d bits\n", ++ x->aead->alg_name, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits); ++ */ ++ sa_set_keys_msg.num_keys++; ++ } ++ } ++ ++ memcpy(msg.payload, &sa_set_keys_msg, sizeof(struct nlkey_sa_set_keys)); ++ msg.length = sizeof(struct nlkey_sa_set_keys); ++ *msg_id = NLKEY_SA_SET_TUNNEL; /* next message */ ++ } else { ++ *msg_id = NLKEY_SA_SET_TUNNEL; /* next message */ ++ goto exit; ++ } ++ break; ++ ++ case NLKEY_SA_SET_TUNNEL: ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_TUNNEL\n"); ++ if(tunnel && (x->props.mode == XFRM_MODE_TUNNEL)) { ++ memset(&sa_set_tunnel_msg, 0, sizeof(struct nlkey_sa_set_tunnel)); ++ ++ /* SA global handler */ ++ sa_set_tunnel_msg.sagd = x->handle; ++ ++ /* Tunnel */ ++ sa_set_tunnel_msg.proto_family = x->props.family; ++ if(x->props.family == AF_INET6) { ++ struct ipv6hdr *top_iph = &sa_set_tunnel_msg.h.ipv6h; ++ int dsfield; ++ top_iph->version = 6; ++ top_iph->priority = 0; ++ top_iph->flow_lbl[0] = 0; ++ top_iph->flow_lbl[1] = 0; ++ top_iph->flow_lbl[2] = 0; ++ top_iph->nexthdr = IPPROTO_IPIP; ++ dsfield = ipv6_get_dsfield(top_iph); ++ dsfield = INET_ECN_encapsulate(dsfield, dsfield); ++ if (x->props.flags & XFRM_STATE_NOECN) ++ dsfield &= ~INET_ECN_MASK; ++ ipv6_change_dsfield(top_iph, 0, dsfield); ++ top_iph->hop_limit = 64; ++ memcpy(&top_iph->daddr, x->id.daddr.a6, sizeof(struct in6_addr)); ++ memcpy(&top_iph->saddr, x->props.saddr.a6, sizeof(struct in6_addr)); ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: IPv6 tunnel\n"); ++ //printk(KERN_INFO "dst: %x %x %x %x\n", x->id.daddr.a6[0], x->id.daddr.a6[1], x->id.daddr.a6[2], x->id.daddr.a6[3]); ++ //(KERN_INFO "src: %x %x %x %x\n", x->props.saddr.a6[0], x->props.saddr.a6[1], x->props.saddr.a6[2], x->props.saddr.a6[3]); ++ } ++ else { ++ struct iphdr *top_iph = &sa_set_tunnel_msg.h.ipv4h; ++ top_iph->ihl = 5; ++ top_iph->version = 4; ++ top_iph->tos = 0; ++ top_iph->frag_off = 0; ++ top_iph->ttl = 64; ++ top_iph->saddr = x->props.saddr.a4; ++ top_iph->daddr = x->id.daddr.a4; ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: IPv4 tunnel dst:%x - src:%x \n", x->id.daddr.a4, x->props.saddr.a4); ++ } ++ memcpy(msg.payload, &sa_set_tunnel_msg, sizeof(struct nlkey_sa_set_tunnel)); ++ msg.length = sizeof(struct nlkey_sa_set_tunnel); ++ *msg_id = NLKEY_SA_SET_NATT; /* next message */ ++ } else { ++ *msg_id = NLKEY_SA_SET_NATT; /* next message */ ++ goto exit; ++ } ++ break; ++ ++ case NLKEY_SA_SET_NATT: ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_NATT\n"); ++ if((natt) && (x->encap)){ ++ memset(&sa_set_natt_msg, 0, sizeof(struct nlkey_sa_set_natt)); ++ ++ /* SA global handler */ ++ sa_set_natt_msg.sagd = x->handle; ++ sa_set_natt_msg.sport = x->encap->encap_sport; ++ sa_set_natt_msg.dport = x->encap->encap_dport; ++ //printk(KERN_INFO "src port: %d dst port: %d \n", ntohs(sa_set_natt_msg.sport), ntohs( sa_set_natt_msg.dport)); ++ memcpy(msg.payload, &sa_set_natt_msg, sizeof(struct nlkey_sa_set_natt)); ++ msg.length = sizeof(struct nlkey_sa_set_natt); ++ *msg_id = NLKEY_SA_SET_LIFETIME; /* next message */ ++ } else { ++ *msg_id = NLKEY_SA_SET_LIFETIME; /* next message */ ++ goto exit; ++ } ++ break; ++ ++ case NLKEY_SA_SET_LIFETIME: ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_LIFETIME\n"); ++ if(lifetime) { ++ memset(&sa_set_lifetime_msg, 0, sizeof(struct nlkey_sa_set_lifetime)); ++ ++ /* SA global handler */ ++ sa_set_lifetime_msg.sagd = x->handle; ++ ++ /* hard time */ ++ sa_set_lifetime_msg.hard_time.allocations = _X2KEY(x->lft.hard_packet_limit); ++ if(_X2KEY(x->lft.hard_byte_limit)) ++ memcpy(sa_set_lifetime_msg.hard_time.bytes, &x->lft.hard_byte_limit, sizeof(uint64_t)); ++ ++ /* soft time */ ++ sa_set_lifetime_msg.soft_time.allocations = _X2KEY(x->lft.soft_packet_limit); ++ if(_X2KEY(x->lft.soft_byte_limit)) ++ memcpy(sa_set_lifetime_msg.soft_time.bytes, &x->lft.soft_byte_limit, sizeof(uint64_t)); ++ ++ /* current time */ ++ sa_set_lifetime_msg.current_time.allocations = x->curlft.packets; ++ memcpy(sa_set_lifetime_msg.current_time.bytes, &x->curlft.bytes, sizeof(uint64_t)); ++ ++ memcpy(msg.payload, &sa_set_lifetime_msg, sizeof(struct nlkey_sa_set_lifetime)); ++ msg.length = sizeof(struct nlkey_sa_set_lifetime); ++ *msg_id = NLKEY_SA_SET_STATE; /* next message */ ++ } else { ++ *msg_id = NLKEY_SA_SET_STATE; /* next message */ ++ goto exit; ++ } ++ break; ++ ++ case NLKEY_SA_SET_STATE: ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SET_STATE\n"); ++ if(state) { ++ memset(&sa_set_state_msg, 0, sizeof(struct nlkey_sa_set_state)); ++ memset(&sa_id_msg, 0, sizeof(struct nlkey_sa_id)); ++ ++ /* SA global handler */ ++ sa_set_state_msg.sagd = x->handle; ++ sa_set_state_msg.parent_sa_sagd = x->parent_sa_handle; ++ /* State */ ++ sa_set_state_msg.state = x->km.state; ++ // TODO: set the offloaded state once ack received ! ++ ++ memcpy(msg.payload, &sa_set_state_msg, sizeof(struct nlkey_sa_set_state)); ++ msg.length = sizeof(struct nlkey_sa_set_state); ++ *msg_id = NLKEY_NULL_MSG; /* next message */ ++ } else { ++ *msg_id = NLKEY_NULL_MSG; /* next message */ ++ goto exit; ++ } ++ break; ++ ++ case NLKEY_SA_DELETE: ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_DELETE\n"); ++ memset(&sa_delete_msg, 0, sizeof(struct nlkey_sa_delete)); ++ ++ /* SA global handler */ ++ sa_delete_msg.sagd = x->handle; ++ memcpy(msg.payload, &sa_delete_msg, sizeof(struct nlkey_sa_delete)); ++ msg.length = sizeof(struct nlkey_sa_delete); ++ ++ ++ *msg_id = NLKEY_NULL_MSG; /* next message */ ++ break; ++ ++ case NLKEY_SA_FLUSH: ++ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_FLUSH\n"); ++ /* No data required for flush SA command */ ++ ++ *msg_id = NLKEY_NULL_MSG; /* next message */ ++ break; ++ ++ default: ++ printk(KERN_ERR "ipsec_xfrm2nlkey: event 0x%x not supported\n", c->event); ++ *msg_id = NLKEY_NULL_MSG; /* next message */ ++ break; ++ } ++ ++ /* prepare netlink message for kernel to user space direction */ ++ if(msg.length > NLKEY_MSG_LEN) ++ { ++ printk(KERN_ERR "ipsec_xfrm2nlkey: maximum message size reached (%d bytes)\n", msg.length); ++ goto exit; ++ } ++ ++ skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation); ++ if (skb == NULL) ++ goto exit; ++ ++ nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length)); ++ memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length)); ++ ++ /* whole length of the message i.e. header + payload */ ++ nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length); ++ ++ /* from kernel */ ++ nlh->nlmsg_pid = 0; ++ nlh->nlmsg_flags = 0; ++ nlh->nlmsg_type = 0; ++ NETLINK_CB(skb).portid = 0; ++ NETLINK_CB(skb).dst_group = 1; ++exit: ++ return skb; ++} ++ ++static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c) ++{ ++ struct sk_buff *skb; ++ unsigned short msg_type; ++ int rc = 0; ++ ++ /* We may generate more than one message when adding new SA (sa_create + sa_set_state + sa_set_tunnel...) */ ++ msg_type = ipsec_sacode_to_nlkeycode((unsigned short)c->event); ++ ++ while(msg_type != NLKEY_NULL_MSG) ++ { ++ /* build nlkey message */ ++ skb = ipsec_xfrm2nlkey(net, x, c, &msg_type); ++ ++ if(skb != NULL) ++ if((rc = netlink_broadcast(nlkey_socket, skb, 0, 1, GFP_ATOMIC)) < 0) ++ return rc; ++ } ++ ++ return rc; ++} ++ ++ ++int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle, const struct flowi *fl, u16 family, u16 dir, u16 ignore_neigh) ++{ ++ struct sk_buff *skb; ++ struct nlkey_msg msg; ++ struct nlmsghdr *nlh = NULL; ++ unsigned short *p; ++ gfp_t allocation = GFP_ATOMIC; //This may called from atomic context ++ ++ //printk(KERN_INFO "ipsec_nlkey_flow \n"); ++ ++ /* next message to build */ ++ memset(&msg, 0, sizeof(struct nlkey_msg)); ++ msg.fcode = NLKEY_FLOW_ADD; ++ ++ // Number of SA for this flow ++ p = msg.payload; ++ *p++ = xfrm_nr; ++ msg.length += sizeof(unsigned short); ++ // SA handles list ++ memcpy(p, xfrm_handle, xfrm_nr*sizeof(unsigned short)); ++ msg.length += xfrm_nr*sizeof(unsigned short); ++ p+=xfrm_nr; ++ // flow family ++ *p++ = family; ++ msg.length += sizeof(unsigned short); ++ // flow family ++ *p++ = dir; ++ msg.length += sizeof(unsigned short); ++ // flow mode ++ *p++ = ignore_neigh; ++ msg.length += sizeof(unsigned short); ++ // flow descriptor ++ memcpy(p, fl, sizeof(struct flowi)); ++ msg.length +=sizeof(struct flowi); ++ p+=sizeof(struct flowi) / sizeof(u16); ++ ++ skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation); ++ if (skb == NULL) ++ return -ENOMEM; ++ ++ /* prepare netlink message for kernel to user space direction */ ++ nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length)); ++ memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length)); ++ ++ /* whole length of the message i.e. header + payload */ ++ nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length); ++ ++ /* from kernel */ ++ nlh->nlmsg_pid = 0; ++ nlh->nlmsg_flags = 0; ++ nlh->nlmsg_type = 0; ++ NETLINK_CB(skb).portid = 0; ++ NETLINK_CB(skb).dst_group = 1; ++ ++ return(netlink_broadcast(nlkey_socket, skb, 0, 1, allocation)); ++} ++EXPORT_SYMBOL(ipsec_nlkey_flow); ++ ++ ++int ipsec_nlkey_flow_remove(struct flowi *fl, u16 family, u16 dir) ++{ ++ struct sk_buff *skb; ++ struct nlkey_msg msg; ++ struct nlmsghdr *nlh = NULL; ++ unsigned short *p; ++ gfp_t allocation = GFP_ATOMIC; //This may called from atomic context ++ ++ ++ //printk(KERN_INFO "ipsec_nlkey_flow_remove\n"); ++ ++ /* next message to build */ ++ memset(&msg, 0, sizeof(struct nlkey_msg)); ++ msg.fcode = NLKEY_FLOW_REMOVE; ++ ++ p = msg.payload; ++ // flow family ++ *p++ = family; ++ msg.length += sizeof(unsigned short); ++ // flow family ++ *p++ = dir; ++ msg.length += sizeof(unsigned short); ++ // flow descriptor ++ memcpy(p, fl, sizeof(struct flowi)); ++ msg.length +=sizeof(struct flowi); ++ p+=sizeof(struct flowi) / sizeof(u16); ++ ++ skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation); ++ if (skb == NULL) ++ return -ENOMEM; ++ ++ /* prepare netlink message for kernel to user space direction */ ++ nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length)); ++ memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length)); ++ ++ /* whole length of the message i.e. header + payload */ ++ nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length); ++ ++ /* from kernel */ ++ nlh->nlmsg_pid = 0; ++ nlh->nlmsg_flags = 0; ++ nlh->nlmsg_type = 0; ++ NETLINK_CB(skb).portid = 0; ++ NETLINK_CB(skb).dst_group = 1; ++ ++ ++ return(netlink_broadcast(nlkey_socket, skb, 0, 1, allocation)); ++ ++ ++} ++EXPORT_SYMBOL(ipsec_nlkey_flow_remove); ++ ++ ++ ++static void ipsec_nlkey_init(void) ++{ ++ struct netlink_kernel_cfg cfg = { ++ .groups = 1, ++ .input = ipsec_nlkey_rcv, ++ }; ++ printk(KERN_INFO "Initializing NETLINK_KEY socket\n"); ++ nlkey_socket = netlink_kernel_create(&init_net, NETLINK_KEY, &cfg); ++} ++#endif ++ ++ + static int __net_init pfkey_net_init(struct net *net) + { + struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id); +@@ -3897,6 +4772,11 @@ static void __exit ipsec_pfkey_exit(void) + sock_unregister(PF_KEY); + unregister_pernet_subsys(&pfkey_net_ops); + proto_unregister(&key_proto); ++ ++#ifdef NLKEY_SUPPORT ++ /* release NETLINK_KEY socket */ ++ sock_release(nlkey_socket->sk_socket); ++#endif + } + + static int __init ipsec_pfkey_init(void) +@@ -3913,6 +4793,12 @@ static int __init ipsec_pfkey_init(void) + if (err != 0) + goto out_unregister_pernet; + xfrm_register_km(&pfkeyv2_mgr); ++ ++#ifdef NLKEY_SUPPORT ++ /* create NETLINK_KEY socket for IPSec offload on Comcerto */ ++ ipsec_nlkey_init(); ++#endif ++ + out: + return err; + diff --git a/patches/ask/upstream/kernel/0124-net__netfilter__nf_conntrack_netlink.c.patch b/patches/ask/upstream/kernel/0124-net__netfilter__nf_conntrack_netlink.c.patch new file mode 100644 index 0000000..c8e98cf --- /dev/null +++ b/patches/ask/upstream/kernel/0124-net__netfilter__nf_conntrack_netlink.c.patch @@ -0,0 +1,288 @@ +diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c +index 3a04665..7e7d13d 100644 +--- a/net/netfilter/nf_conntrack_netlink.c ++++ b/net/netfilter/nf_conntrack_netlink.c +@@ -28,6 +28,11 @@ + #include + #include + #include ++#if defined(CONFIG_CPE_FAST_PATH) ++#ifndef IPSEC_FLOW_CACHE ++#include ++#endif ++#endif + #include + #include + +@@ -202,9 +207,16 @@ static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct, + struct nlattr *nest_proto; + int ret; + ++#ifdef CONFIG_CPE_FAST_PATH ++ rcu_read_lock(); ++#endif + l4proto = nf_ct_l4proto_find(nf_ct_protonum(ct)); +- if (!l4proto->to_nlattr) ++ if (!l4proto->to_nlattr) { ++#ifdef CONFIG_CPE_FAST_PATH ++ rcu_read_unlock(); ++#endif + return 0; ++ } + + nest_proto = nla_nest_start(skb, CTA_PROTOINFO); + if (!nest_proto) +@@ -214,9 +226,15 @@ static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct, + + nla_nest_end(skb, nest_proto); + ++#ifdef CONFIG_CPE_FAST_PATH ++ rcu_read_unlock(); ++#endif + return ret; + + nla_put_failure: ++#ifdef CONFIG_CPE_FAST_PATH ++ rcu_read_unlock(); ++#endif + return -1; + } + +@@ -353,6 +371,18 @@ nla_put_failure: + #define ctnetlink_dump_mark(a, b, c) (0) + #endif + ++#if defined(CONFIG_CPE_FAST_PATH) ++static inline int ++ctnetlink_dump_qosconnmark(struct sk_buff *skb, const struct nf_conn *ct) ++{ ++ nla_put_be64(skb, CTA_QOSCONNMARK, cpu_to_be64(ct->qosconnmark), ++ CTA_QOSCONNMARK_PAD); ++ return 0; ++} ++#else ++#define ctnetlink_dump_qosconnmark(a, b) (0) ++#endif ++ + #ifdef CONFIG_NF_CONNTRACK_SECMARK + static int ctnetlink_dump_secctx(struct sk_buff *skb, const struct nf_conn *ct) + { +@@ -430,6 +460,59 @@ ctnetlink_dump_labels(struct sk_buff *skb, const struct nf_conn *ct) + return 0; + } + ++#if defined(CONFIG_CPE_FAST_PATH) ++static int ++ctnetlink_dump_comcerto_fp(struct sk_buff *skb, const struct nf_conn *ct) ++{ ++ struct nlattr *nest_count; ++ ++ nest_count = nla_nest_start(skb, CTA_LAYERSCAPE_FP_ORIG | NLA_F_NESTED); ++ if (!nest_count) ++ goto nla_put_failure; ++ ++ nla_put_u32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_ORIGINAL].mark); ++ nla_put_u32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_ORIGINAL].ifindex); ++ nla_put_u32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_ORIGINAL].iif); ++ nla_put_u32(skb, CTA_COMCERTO_FP_UNDERLYING_IIF, ct->fp_info[IP_CT_DIR_ORIGINAL].underlying_iif); ++ nla_put_u16(skb, CTA_COMCERTO_FP_UNDERLYING_VID, ct->fp_info[IP_CT_DIR_ORIGINAL].underlying_vlan_id); ++#ifndef IPSEC_FLOW_CACHE ++ if ((ct->fp_info[IP_CT_DIR_ORIGINAL].xfrm_handle[0]) || ++ (ct->fp_info[IP_CT_DIR_ORIGINAL].xfrm_handle[MAX_SUPPORTED_XFRMS_PER_DIR])) ++ { ++ nla_put(skb, CTA_COMCERTO_FP_XFRM_HANDLE, sizeof(ct->fp_info[IP_CT_DIR_ORIGINAL].xfrm_handle), ++ ct->fp_info[IP_CT_DIR_ORIGINAL].xfrm_handle); ++ } ++#endif /* IPSEC_FLOW_CACHE */ ++ nla_nest_end(skb, nest_count); ++ ++ nest_count = nla_nest_start(skb, CTA_LAYERSCAPE_FP_REPLY | NLA_F_NESTED); ++ if (!nest_count) ++ goto nla_put_failure; ++ ++ nla_put_u32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_REPLY].mark); ++ nla_put_u32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_REPLY].ifindex); ++ nla_put_u32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_REPLY].iif); ++ nla_put_u32(skb, CTA_COMCERTO_FP_UNDERLYING_IIF, ct->fp_info[IP_CT_DIR_REPLY].underlying_iif); ++ nla_put_u16(skb, CTA_COMCERTO_FP_UNDERLYING_VID, ct->fp_info[IP_CT_DIR_REPLY].underlying_vlan_id); ++#ifndef IPSEC_FLOW_CACHE ++ if ((ct->fp_info[IP_CT_DIR_REPLY].xfrm_handle[0]) || ++ (ct->fp_info[IP_CT_DIR_REPLY].xfrm_handle[MAX_SUPPORTED_XFRMS_PER_DIR])) ++ { ++ nla_put(skb, CTA_COMCERTO_FP_XFRM_HANDLE, sizeof(ct->fp_info[IP_CT_DIR_REPLY].xfrm_handle), ++ ct->fp_info[IP_CT_DIR_REPLY].xfrm_handle); ++ } ++#endif /* IPSEC_FLOW_CACHE */ ++ nla_nest_end(skb, nest_count); ++ ++ return 0; ++ ++nla_put_failure: ++ return -1; ++} ++#else ++#define ctnetlink_dump_comcerto_fp(a, b) (0) ++#endif ++ + #define master_tuple(ct) &(ct->master->tuplehash[IP_CT_DIR_ORIGINAL].tuple) + + static int ctnetlink_dump_master(struct sk_buff *skb, const struct nf_conn *ct) +@@ -570,7 +653,11 @@ static int ctnetlink_dump_info(struct sk_buff *skb, struct nf_conn *ct) + { + if (ctnetlink_dump_status(skb, ct) < 0 || + ctnetlink_dump_mark(skb, ct, true) < 0 || ++ ctnetlink_dump_qosconnmark(skb, ct) < 0 || + ctnetlink_dump_secctx(skb, ct) < 0 || ++#ifdef CONFIG_CPE_FAST_PATH ++ ctnetlink_dump_comcerto_fp(skb, ct) < 0 || ++#endif + ctnetlink_dump_id(skb, ct) < 0 || + ctnetlink_dump_use(skb, ct) < 0 || + ctnetlink_dump_master(skb, ct) < 0) +@@ -722,6 +809,13 @@ static size_t ctnetlink_nlmsg_size(const struct nf_conn *ct) + + nla_total_size(0) /* CTA_HELP */ + + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */ + + ctnetlink_secctx_size(ct) ++#ifdef CONFIG_CPE_FAST_PATH ++ + 2 * nla_total_size(0) /* CTA_LAYERSCAPE_FP_ORIG|REPL */ ++ + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_MARK */ ++ + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IFINDEX */ ++ + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IIF */ ++ + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_UNDERLYING_IIF */ ++#endif + #if IS_ENABLED(CONFIG_NF_NAT) + + 2 * nla_total_size(0) /* CTA_NAT_SEQ_ADJ_ORIG|REPL */ + + 6 * nla_total_size(sizeof(u_int32_t)) /* CTA_NAT_SEQ_OFFSET */ +@@ -729,6 +823,9 @@ static size_t ctnetlink_nlmsg_size(const struct nf_conn *ct) + #ifdef CONFIG_NF_CONNTRACK_MARK + + nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */ + #endif ++#if defined(CONFIG_CPE_FAST_PATH) ++ + nla_total_size(sizeof(u_int64_t)) /* CTA_QOSCONNMARK */ ++#endif + #ifdef CONFIG_NF_CONNTRACK_ZONES + + nla_total_size(sizeof(u_int16_t)) /* CTA_ZONE|CTA_TUPLE_ZONE */ + #endif +@@ -806,6 +903,11 @@ ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item) + NF_CT_DEFAULT_ZONE_DIR) < 0) + goto nla_put_failure; + ++#if defined(CONFIG_CPE_FAST_PATH) ++ if (ctnetlink_dump_comcerto_fp(skb, ct) < 0) ++ goto nla_put_failure; ++#endif ++ + if (ctnetlink_dump_id(skb, ct) < 0) + goto nla_put_failure; + +@@ -858,6 +960,11 @@ ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item) + if (ctnetlink_dump_mark(skb, ct, events & (1 << IPCT_MARK))) + goto nla_put_failure; + #endif ++#if defined(CONFIG_CPE_FAST_PATH) ++ if ((events & (1 << IPCT_QOSCONNMARK) || ct->qosconnmark) && ++ ctnetlink_dump_qosconnmark(skb, ct) < 0) ++ goto nla_put_failure; ++#endif + + if (ctnetlink_dump_event_timestamp(skb, ct)) + goto nla_put_failure; +@@ -1570,6 +1677,9 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { + [CTA_NAT_SRC] = { .type = NLA_NESTED }, + [CTA_TIMEOUT] = { .type = NLA_U32 }, + [CTA_MARK] = { .type = NLA_U32 }, ++#if defined(CONFIG_CPE_FAST_PATH) ++ [CTA_QOSCONNMARK] = { .type = NLA_U64 }, ++#endif + [CTA_ID] = { .type = NLA_U32 }, + [CTA_NAT_DST] = { .type = NLA_NESTED }, + [CTA_TUPLE_MASTER] = { .type = NLA_NESTED }, +@@ -1906,6 +2016,48 @@ ctnetlink_change_status(struct nf_conn *ct, const struct nlattr * const cda[]) + return nf_ct_change_status_common(ct, ntohl(nla_get_be32(cda[CTA_STATUS]))); + } + ++#if defined(CONFIG_CPE_FAST_PATH) ++/* ++ * This function detects ctnetlink messages that require ++ * to set the conntrack status to IPS_PERMANENT. ++ * It updates only this bit regardless of other possible ++ * changes. ++ * Return 0 if succesfull ++ */ ++static int ++ctnetlink_change_permanent(struct nf_conn *ct, const struct nlattr * const cda[]) ++{ ++ unsigned int status; ++ u_int32_t id; ++ __be32 conntrack_id = ntohl((__force __be32)nf_ct_get_id(ct)); ++ ++ if (cda[CTA_STATUS] && cda[CTA_ID]) { ++ status = ntohl(nla_get_be32(cda[CTA_STATUS])); ++ id = ntohl(nla_get_be32(cda[CTA_ID])); ++ ++ if (status & IPS_PERMANENT) { ++ if (conntrack_id == id) { ++ ct->status |= IPS_PERMANENT; ++ return 0; ++ } ++ else ++ return -ENOENT; ++ } ++ else if (nf_ct_is_permanent(ct)) ++ { ++ /* Clear the PERMANENT bit. */ ++ if (conntrack_id == id) { ++ clear_bit(IPS_PERMANENT_BIT, &ct->status); ++ return 0; ++ } ++ else ++ return -ENOENT; ++ } ++ } ++ return -1; ++} ++#endif ++ + static int + ctnetlink_setup_nat(struct nf_conn *ct, const struct nlattr * const cda[]) + { +@@ -2209,6 +2361,11 @@ ctnetlink_change_conntrack(struct nf_conn *ct, + return err; + } + ++#if defined(CONFIG_CPE_FAST_PATH) ++ if (cda[CTA_QOSCONNMARK]) ++ ct->qosconnmark = be64_to_cpu(nla_get_be64(cda[CTA_QOSCONNMARK])); ++#endif ++ + #if defined(CONFIG_NF_CONNTRACK_MARK) + if (cda[CTA_MARK]) + ctnetlink_change_mark(ct, cda); +@@ -2347,6 +2504,11 @@ ctnetlink_create_conntrack(struct net *net, + goto err2; + } + ++#if defined(CONFIG_CPE_FAST_PATH) ++ if (cda[CTA_QOSCONNMARK]) ++ ct->qosconnmark = be64_to_cpu(nla_get_be64(cda[CTA_QOSCONNMARK])); ++#endif ++ + #if defined(CONFIG_NF_CONNTRACK_MARK) + if (cda[CTA_MARK]) + ctnetlink_change_mark(ct, cda); +@@ -2473,6 +2635,15 @@ static int ctnetlink_new_conntrack(struct sk_buff *skb, + err = -EEXIST; + ct = nf_ct_tuplehash_to_ctrack(h); + if (!(info->nlh->nlmsg_flags & NLM_F_EXCL)) { ++#if defined(CONFIG_CPE_FAST_PATH) ++ /* If the permanent status has been set, this is a specific ++ * message. Don't broadcast the event and don't update the ct */ ++ err = ctnetlink_change_permanent(ct, cda); ++ if ((err == 0) || (err == -ENOENT)) { ++ nf_ct_put(ct); ++ return err; ++ } ++#endif + err = ctnetlink_change_conntrack(ct, cda); + if (err == 0) { + nf_conntrack_eventmask_report((1 << IPCT_REPLY) | diff --git a/patches/ask/upstream/kernel/0129-net__wireless__Kconfig.patch b/patches/ask/upstream/kernel/0129-net__wireless__Kconfig.patch new file mode 100644 index 0000000..468514c --- /dev/null +++ b/patches/ask/upstream/kernel/0129-net__wireless__Kconfig.patch @@ -0,0 +1,21 @@ +diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig +index 5d6a0b2b4f3a..7c3d10e2e8b1 100644 +--- a/net/wireless/Kconfig ++++ b/net/wireless/Kconfig +@@ -1,6 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + config WIRELESS_EXT +- bool ++ def_bool y + + config WEXT_CORE + def_bool y +@@ -11,7 +11,7 @@ config WEXT_PROC + depends on WEXT_CORE + + config WEXT_PRIV +- bool ++ def_bool y + + config CFG80211 + tristate "cfg80211 - wireless configuration API" diff --git a/patches/ask/upstream/kernel/0131-net__xfrm__Makefile.patch b/patches/ask/upstream/kernel/0131-net__xfrm__Makefile.patch new file mode 100644 index 0000000..7183410 --- /dev/null +++ b/patches/ask/upstream/kernel/0131-net__xfrm__Makefile.patch @@ -0,0 +1,9 @@ +diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile +index 0a0a0a0..0b0b0b0 100644 +--- a/net/xfrm/Makefile ++++ b/net/xfrm/Makefile +@@ -24,3 +24,4 @@ obj-$(CONFIG_XFRM_INTERFACE) += xfrm_interface.o + obj-$(CONFIG_XFRM_IPTFS) += xfrm_iptfs.o + obj-$(CONFIG_XFRM_ESPINTCP) += espintcp.o + obj-$(CONFIG_DEBUG_INFO_BTF) += xfrm_state_bpf.o ++obj-$(CONFIG_INET_IPSEC_OFFLOAD) += ipsec_flow.o diff --git a/patches/ask/upstream/kernel/0136-net__xfrm__xfrm_policy.c.patch b/patches/ask/upstream/kernel/0136-net__xfrm__xfrm_policy.c.patch new file mode 100644 index 0000000..30460f8 --- /dev/null +++ b/patches/ask/upstream/kernel/0136-net__xfrm__xfrm_policy.c.patch @@ -0,0 +1,163 @@ +diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c +index 62486f8..3cbe4f8 100644 +--- a/net/xfrm/xfrm_policy.c ++++ b/net/xfrm/xfrm_policy.c +@@ -48,6 +48,11 @@ + #include + + #include "xfrm_hash.h" ++#ifdef IPSEC_FLOW_CACHE ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++#include "ipsec_flow.h" ++#endif ++#endif + + #define XFRM_QUEUE_TMO_MIN ((unsigned)(HZ/10)) + #define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ)) +@@ -179,6 +184,15 @@ static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1] + + static struct kmem_cache *xfrm_dst_cache __ro_after_init; + ++#ifdef IPSEC_FLOW_CACHE ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++extern int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle, ++ const struct flowi *fl, u16 family, u16 dir, u16 ignore_neigh); ++int ipsec_flow_init(struct net *net); ++void ipsec_flow_fini(struct net *net); ++#endif ++#endif ++ + static struct rhashtable xfrm_policy_inexact_table; + static const struct rhashtable_params xfrm_pol_inexact_params; + +@@ -2599,6 +2613,17 @@ static dscp_t xfrm_get_dscp(const struct flowi *fl, int family) + return 0; + } + ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++int xfrm_get_tos(const struct flowi *fl, int family) ++{ ++ if (family == AF_INET) ++ return inet_dscp_to_dsfield(fl->u.ip4.flowi4_dscp) & INET_DSCP_MASK; ++ ++ return 0; ++} ++EXPORT_SYMBOL(xfrm_get_tos); ++#endif ++ + static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) + { + const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); +@@ -3295,6 +3320,37 @@ no_transform: + dst = dst_orig; + } + ++#ifdef IPSEC_FLOW_CACHE ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ { ++ struct dst_entry *dst1 = dst; ++ struct xfrm_state *x; ++ u16 xfrm_handle[XFRM_POLICY_TYPE_MAX]; ++ u16 ignore_neigh = 0; ++ ++ num_xfrms = 0; ++ memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX * sizeof(u16)); ++ while (((x = dst1->xfrm) != NULL) && ++ (num_xfrms < XFRM_POLICY_TYPE_MAX)) { ++ xfrm_handle[num_xfrms++] = x->handle; ++ if (x->props.mode == XFRM_MODE_TUNNEL) ++ ignore_neigh = 1; ++ dst1 = xfrm_dst_child(dst1); ++ ++ if (dst1 == NULL) { ++ err = -EHOSTUNREACH; ++ goto error; ++ } ++ } ++ if (ipsec_flow_add(net, fl, family, dir, xfrm_handle)) { ++ /* sent flow notification to cmm with sa_handle */ ++ ipsec_nlkey_flow(num_xfrms, xfrm_handle, fl, family, ++ (unsigned short)dir, ignore_neigh); ++ } ++ } ++#endif ++#endif ++ + ok: + xfrm_pols_put(pols, drop_pols); + if (dst->xfrm && +@@ -3853,6 +3909,34 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, + goto reject; + } + ++#ifdef IPSEC_FLOW_CACHE ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ { ++ struct xfrm_state *x; ++ u16 xfrm_handle[XFRM_POLICY_TYPE_MAX]; ++ ++ xfrm_nr = 0; ++ memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX * sizeof(u16)); ++ for (i = sp->len - 1; ++ (i >= 0) && (xfrm_nr < XFRM_POLICY_TYPE_MAX); i--) { ++ x = sp->xvec[i]; ++ xfrm_handle[xfrm_nr++] = x->handle; ++ } ++ if (ipsec_flow_add(net, (const struct flowi *)&fl, family, dir, ++ xfrm_handle)) { ++ /* sent flow notification to cmm with sa_handle */ ++ ipsec_nlkey_flow(xfrm_nr, xfrm_handle, ++ (const struct flowi *)&fl, family, dir, 0); ++ } ++ } ++ ++ /* Hub and spoke changes: Setting the POLICY_IN direction in the packet */ ++ skb->ipsec_xfrm_dir |= (1 << XFRM_POLICY_IN); ++ ++std_path: ++#endif ++#endif ++ + xfrm_pols_put(pols, npols); + sp->verified_cnt = k; + +@@ -4328,6 +4412,14 @@ static int __net_init xfrm_net_init(struct net *net) + if (rv < 0) + goto out_sysctl; + ++#ifdef IPSEC_FLOW_CACHE ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ rv = ipsec_flow_init(net); ++ if (rv < 0) ++ goto out_ipsec_flow; ++#endif ++#endif ++ + rv = xfrm_nat_keepalive_net_init(net); + if (rv < 0) + goto out_nat_keepalive; +@@ -4335,6 +4427,12 @@ static int __net_init xfrm_net_init(struct net *net) + return 0; + + out_nat_keepalive: ++#ifdef IPSEC_FLOW_CACHE ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ ipsec_flow_fini(net); ++out_ipsec_flow: ++#endif ++#endif + xfrm_sysctl_fini(net); + out_sysctl: + xfrm_policy_fini(net); +@@ -4349,6 +4447,11 @@ out_statistics: + static void __net_exit xfrm_net_exit(struct net *net) + { + xfrm_nat_keepalive_net_fini(net); ++#ifdef IPSEC_FLOW_CACHE ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ ipsec_flow_fini(net); ++#endif ++#endif + xfrm_sysctl_fini(net); + xfrm_policy_fini(net); + xfrm_state_fini(net); diff --git a/patches/ask/upstream/kernel/0137-net__xfrm__xfrm_state.c.patch b/patches/ask/upstream/kernel/0137-net__xfrm__xfrm_state.c.patch new file mode 100644 index 0000000..36db635 --- /dev/null +++ b/patches/ask/upstream/kernel/0137-net__xfrm__xfrm_state.c.patch @@ -0,0 +1,291 @@ +diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c +index 9e14e45..d685ed7 100644 +--- a/net/xfrm/xfrm_state.c ++++ b/net/xfrm/xfrm_state.c +@@ -58,6 +58,10 @@ static inline bool xfrm_state_hold_rcu(struct xfrm_state __rcu *x) + return refcount_inc_not_zero(&x->refcnt); + } + ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++static unsigned short xfrm_state_handle; ++#endif ++ + static inline unsigned int xfrm_dst_hash(struct net *net, + const xfrm_address_t *daddr, + const xfrm_address_t *saddr, +@@ -119,6 +123,9 @@ static void xfrm_hash_transfer(struct hlist_head *list, + struct hlist_head *nsrctable, + struct hlist_head *nspitable, + struct hlist_head *nseqtable, ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ struct hlist_head *nhtable, ++#endif + unsigned int nhashmask) + { + struct hlist_node *tmp; +@@ -150,6 +157,13 @@ static void xfrm_hash_transfer(struct hlist_head *list, + XFRM_STATE_INSERT(byseq, &x->byseq, nseqtable + h, + x->xso.type); + } ++ ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ if (x->handle && x->in_byh_hash) { ++ h = x->handle & nhashmask; ++ hlist_add_head_rcu(&x->byh, nhtable + h); ++ } ++#endif + } + } + +@@ -162,6 +176,9 @@ static void xfrm_hash_resize(struct work_struct *work) + { + struct net *net = container_of(work, struct net, xfrm.state_hash_work); + struct hlist_head *ndst, *nsrc, *nspi, *nseq, *odst, *osrc, *ospi, *oseq; ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ struct hlist_head *nh, *oh; ++#endif + unsigned long nsize, osize; + unsigned int nhashmask, ohashmask; + int i; +@@ -188,6 +205,16 @@ static void xfrm_hash_resize(struct work_struct *work) + xfrm_hash_free(nspi, nsize); + return; + } ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ nh = xfrm_hash_alloc(nsize); ++ if (!nh) { ++ xfrm_hash_free(ndst, nsize); ++ xfrm_hash_free(nsrc, nsize); ++ xfrm_hash_free(nspi, nsize); ++ xfrm_hash_free(nseq, nsize); ++ return; ++ } ++#endif + + spin_lock_bh(&net->xfrm.xfrm_state_lock); + write_seqcount_begin(&net->xfrm.xfrm_state_hash_generation); +@@ -195,17 +222,27 @@ static void xfrm_hash_resize(struct work_struct *work) + nhashmask = (nsize / sizeof(struct hlist_head)) - 1U; + odst = xfrm_state_deref_prot(net->xfrm.state_bydst, net); + for (i = net->xfrm.state_hmask; i >= 0; i--) +- xfrm_hash_transfer(odst + i, ndst, nsrc, nspi, nseq, nhashmask); ++ xfrm_hash_transfer(odst + i, ndst, nsrc, nspi, nseq, ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ nh, ++#endif ++ nhashmask); + + osrc = xfrm_state_deref_prot(net->xfrm.state_bysrc, net); + ospi = xfrm_state_deref_prot(net->xfrm.state_byspi, net); + oseq = xfrm_state_deref_prot(net->xfrm.state_byseq, net); ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ oh = xfrm_state_deref_prot(net->xfrm.state_byh, net); ++#endif + ohashmask = net->xfrm.state_hmask; + + rcu_assign_pointer(net->xfrm.state_bydst, ndst); + rcu_assign_pointer(net->xfrm.state_bysrc, nsrc); + rcu_assign_pointer(net->xfrm.state_byspi, nspi); + rcu_assign_pointer(net->xfrm.state_byseq, nseq); ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ rcu_assign_pointer(net->xfrm.state_byh, nh); ++#endif + net->xfrm.state_hmask = nhashmask; + + write_seqcount_end(&net->xfrm.xfrm_state_hash_generation); +@@ -219,6 +256,9 @@ static void xfrm_hash_resize(struct work_struct *work) + xfrm_hash_free(osrc, osize); + xfrm_hash_free(ospi, osize); + xfrm_hash_free(oseq, osize); ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ xfrm_hash_free(oh, osize); ++#endif + } + + static DEFINE_SPINLOCK(xfrm_state_afinfo_lock); +@@ -744,6 +784,9 @@ struct xfrm_state *xfrm_state_alloc(struct net *net) + INIT_HLIST_NODE(&x->bysrc); + INIT_HLIST_NODE(&x->byspi); + INIT_HLIST_NODE(&x->byseq); ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ INIT_HLIST_NODE(&x->byh); ++#endif + hrtimer_setup(&x->mtimer, xfrm_timer_handler, CLOCK_BOOTTIME, + HRTIMER_MODE_ABS_SOFT); + timer_setup(&x->rtimer, xfrm_replay_timer_handler, 0); +@@ -754,6 +797,12 @@ struct xfrm_state *xfrm_state_alloc(struct net *net) + x->lft.hard_packet_limit = XFRM_INF; + x->replay_maxage = 0; + x->replay_maxdiff = 0; ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ do { ++ x->handle = xfrm_state_handle++; ++ } while (x->handle == 0); ++ x->in_byh_hash = 0; ++#endif + x->pcpu_num = UINT_MAX; + spin_lock_init(&x->lock); + x->mode_data = NULL; +@@ -829,6 +878,12 @@ int __xfrm_state_delete(struct xfrm_state *x) + + if (x->id.spi) + hlist_del_rcu(&x->byspi); ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ if (x->handle && x->in_byh_hash) { ++ hlist_del_rcu(&x->byh); ++ x->in_byh_hash = 0; ++ } ++#endif + net->xfrm.state_num--; + xfrm_nat_keepalive_state_updated(x); + spin_unlock(&net->xfrm.xfrm_state_lock); +@@ -1582,6 +1637,13 @@ found: + net->xfrm.state_byseq + h, + x->xso.type); + } ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ if (x->handle && !x->in_byh_hash) { ++ h = x->handle & net->xfrm.state_hmask; ++ hlist_add_head_rcu(&x->byh, net->xfrm.state_byh + h); ++ x->in_byh_hash = 1; ++ } ++#endif + x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires; + hrtimer_start(&x->mtimer, + ktime_set(net->xfrm.sysctl_acq_expires, 0), +@@ -1752,6 +1814,14 @@ static void __xfrm_state_insert(struct xfrm_state *x) + x->xso.type); + } + ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ if (x->handle && !x->in_byh_hash) { ++ h = x->handle & net->xfrm.state_hmask; ++ hlist_add_head_rcu(&x->byh, net->xfrm.state_byh + h); ++ x->in_byh_hash = 1; ++ } ++#endif ++ + hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT); + if (x->replay_maxage) + mod_timer(&x->rtimer, jiffies + x->replay_maxage); +@@ -1773,6 +1843,9 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew) + u32 mark = xnew->mark.v & xnew->mark.m; + u32 if_id = xnew->if_id; + u32 cpu_id = xnew->pcpu_num; ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ u16 parent_sa_handle = 0; ++#endif + + h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family); + hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) { +@@ -1782,9 +1855,17 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew) + x->pcpu_num == cpu_id && + (mark & x->mark.m) == x->mark.v && + xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) && +- xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family)) ++ xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family)) { + x->genid++; ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ if (!parent_sa_handle) ++ parent_sa_handle = x->handle; ++#endif ++ } + } ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ xnew->parent_sa_handle = parent_sa_handle; ++#endif + } + + void xfrm_state_insert(struct xfrm_state *x) +@@ -2352,6 +2433,37 @@ xfrm_state_lookup_byaddr(struct net *net, u32 mark, + } + EXPORT_SYMBOL(xfrm_state_lookup_byaddr); + ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++struct xfrm_state *__xfrm_state_lookup_byhandle(struct net *net, u16 handle) ++{ ++ unsigned int h = handle & net->xfrm.state_hmask; ++ struct xfrm_state *x; ++ ++ hlist_for_each_entry(x, net->xfrm.state_byh + h, byh) { ++ if (x->handle != handle) ++ continue; ++ ++ xfrm_state_hold(x); ++ return x; ++ } ++ ++ return NULL; ++} ++ ++struct xfrm_state * ++xfrm_state_lookup_byhandle(struct net *net, u16 handle) ++{ ++ struct xfrm_state *x; ++ ++ spin_lock_bh(&net->xfrm.xfrm_state_lock); ++ x = __xfrm_state_lookup_byhandle(net, handle); ++ spin_unlock_bh(&net->xfrm.xfrm_state_lock); ++ ++ return x; ++} ++EXPORT_SYMBOL(xfrm_state_lookup_byhandle); ++#endif ++ + struct xfrm_state * + xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid, + u32 if_id, u32 pcpu_num, u8 proto, const xfrm_address_t *daddr, +@@ -2603,6 +2715,13 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high, + x->id.spi = newspi; + h = xfrm_spi_hash(net, &x->id.daddr, newspi, x->id.proto, x->props.family); + XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h, x->xso.type); ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ if (x->handle && !x->in_byh_hash) { ++ h = x->handle & net->xfrm.state_hmask; ++ hlist_add_head_rcu(&x->byh, net->xfrm.state_byh + h); ++ x->in_byh_hash = 1; ++ } ++#endif + spin_unlock_bh(&net->xfrm.xfrm_state_lock); + err = 0; + goto unlock; +@@ -3279,6 +3398,12 @@ int __net_init xfrm_state_init(struct net *net) + net->xfrm.state_byseq = xfrm_hash_alloc(sz); + if (!net->xfrm.state_byseq) + goto out_byseq; ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ net->xfrm.state_byh = xfrm_hash_alloc(sz); ++ if (!net->xfrm.state_byh) ++ goto out_byh; ++ get_random_bytes(&xfrm_state_handle, sizeof(xfrm_state_handle)); ++#endif + + net->xfrm.state_cache_input = alloc_percpu(struct hlist_head); + if (!net->xfrm.state_cache_input) +@@ -3294,6 +3419,10 @@ int __net_init xfrm_state_init(struct net *net) + return 0; + + out_state_cache_input: ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ xfrm_hash_free(net->xfrm.state_byh, sz); ++out_byh: ++#endif + xfrm_hash_free(net->xfrm.state_byseq, sz); + out_byseq: + xfrm_hash_free(net->xfrm.state_byspi, sz); +@@ -3321,9 +3450,15 @@ void xfrm_state_fini(struct net *net) + WARN_ON(!hlist_empty(net->xfrm.state_byspi + i)); + WARN_ON(!hlist_empty(net->xfrm.state_bysrc + i)); + WARN_ON(!hlist_empty(net->xfrm.state_bydst + i)); ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ WARN_ON(!hlist_empty(net->xfrm.state_byh + i)); ++#endif + } + + sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head); ++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) ++ xfrm_hash_free(net->xfrm.state_byh, sz); ++#endif + xfrm_hash_free(net->xfrm.state_byseq, sz); + xfrm_hash_free(net->xfrm.state_byspi, sz); + xfrm_hash_free(net->xfrm.state_bysrc, sz); diff --git a/patches/ask/upstream/libnetfilter-conntrack/02-libnetfilter-conntrack-do-not-abort-on-unusable-nxp-attrs-v3.patch b/patches/ask/upstream/libnetfilter-conntrack/02-libnetfilter-conntrack-do-not-abort-on-unusable-nxp-attrs-v3.patch new file mode 100644 index 0000000..80e842a --- /dev/null +++ b/patches/ask/upstream/libnetfilter-conntrack/02-libnetfilter-conntrack-do-not-abort-on-unusable-nxp-attrs-v3.patch @@ -0,0 +1,93 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mono +Date: Mon, 11 May 2026 00:00:00 +0900 +Subject: [PATCH] libnetfilter_conntrack: do not abort on unusable NXP attrs + +The NXP ASK extension patch teaches libnetfilter_conntrack about +Comcerto/Layerscape fast-path and QoS conntrack attributes, but it also +uses abi_breakage() when those attributes are present with a shape this +userspace does not expect. + +That is too fragile for CMM. CMM dumps the global conntrack table, which +can contain ordinary Kubernetes/Cilium conntrack entries alongside entries +that are relevant to the NXP fast path. A single unexpected or +unrepresentable vendor attribute must not abort the entire dump before CMM +has a chance to ignore the entry. + +Keep unsupported attribute IDs ignored as before. For NXP fast-path/QoS +attributes that fail validation or nested parsing, skip only that attribute +or fast-path block and continue parsing the rest of the conntrack object. + +Signed-off-by: Mono +--- + src/conntrack/parse_mnl.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +diff --git a/src/conntrack/parse_mnl.c b/src/conntrack/parse_mnl.c +index 33f7824..0000000 100644 +--- a/src/conntrack/parse_mnl.c ++++ b/src/conntrack/parse_mnl.c +@@ -873,16 +873,16 @@ nfct_parse_comcerto_fp_attr_cb(const struct nlattr *attr, void *data) + case CTA_COMCERTO_FP_IIF: + case CTA_COMCERTO_FP_UNDERLYING_IIF: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) +- abi_breakage(); ++ return MNL_CB_OK; + break; + case CTA_COMCERTO_FP_UNDERLYING_VID: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) +- abi_breakage(); ++ return MNL_CB_OK; + break; + case CTA_COMCERTO_FP_XFRM_HANDLE: + /* 4 x u32 = 16 bytes */ + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, 16) < 0) +- abi_breakage(); ++ return MNL_CB_OK; + break; + } + tb[type] = attr; +@@ -1024,11 +1024,11 @@ nfct_parse_conntrack_attr_cb(const struct nlattr *attr, void *data) + case CTA_LAYERSCAPE_FP_ORIG: + case CTA_LAYERSCAPE_FP_REPLY: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) +- abi_breakage(); ++ return MNL_CB_OK; + break; + case CTA_QOSCONNMARK: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) +- abi_breakage(); ++ return MNL_CB_OK; + break; + } + tb[type] = attr; +@@ -1164,18 +1164,21 @@ nfct_payload_parse(const void *payload, size_t payload_len, + + /* NXP ASK: Comcerto fast path and QoS */ + if (tb[CTA_LAYERSCAPE_FP_ORIG]) { +- if (nfct_parse_comcerto_fp(tb[CTA_LAYERSCAPE_FP_ORIG], ct, +- __DIR_ORIG) < 0) +- return -1; ++ /* ++ * Do not abort the entire conntrack dump if one fast-path ++ * extension block cannot be represented by this userspace. ++ */ ++ nfct_parse_comcerto_fp(tb[CTA_LAYERSCAPE_FP_ORIG], ct, ++ __DIR_ORIG); + } + + if (tb[CTA_LAYERSCAPE_FP_REPLY]) { +- if (nfct_parse_comcerto_fp(tb[CTA_LAYERSCAPE_FP_REPLY], ct, +- __DIR_REPL) < 0) +- return -1; ++ /* See CTA_LAYERSCAPE_FP_ORIG handling above. */ ++ nfct_parse_comcerto_fp(tb[CTA_LAYERSCAPE_FP_REPLY], ct, ++ __DIR_REPL); + } + + if (tb[CTA_QOSCONNMARK]) { + ct->qosconnmark = be64toh(mnl_attr_get_u64(tb[CTA_QOSCONNMARK])); + set_bit(ATTR_QOSCONNMARK, ct->head.set); + } +-- +2.47.3 diff --git a/scripts/fetch-artifact b/scripts/fetch-artifact new file mode 100755 index 0000000..a712355 --- /dev/null +++ b/scripts/fetch-artifact @@ -0,0 +1,40 @@ +#!/bin/sh +set -eu + +# Fetch helper contract: +# DEP_PKG_MIRROR=https://mirror.example.com/monok8s +# mirror URL = ${DEP_PKG_MIRROR}/${mirror_path} + +if [ "$#" -ne 3 ]; then + echo "usage: fetch-artifact " >&2 + exit 2 +fi + +mirror_path="$1" +out="$2" +upstream_url="$3" + +mkdir -p "$(dirname "$out")" +rm -f "$out" + +if [ -n "${DEP_PKG_MIRROR:-}" ]; then + mirror_url="${DEP_PKG_MIRROR%/}/${mirror_path}" + echo "fetch-artifact: trying mirror: ${mirror_url}" >&2 + if curl -fL --retry 3 -o "$out" "$mirror_url"; then + exit 0 + fi + rm -f "$out" + + if [ "${DEP_PKG_OFFLINE:-0}" = "1" ]; then + echo "fetch-artifact: mirror miss and DEP_PKG_OFFLINE=1: ${mirror_url}" >&2 + exit 1 + fi +fi + +if [ "${DEP_PKG_OFFLINE:-0}" = "1" ]; then + echo "fetch-artifact: DEP_PKG_OFFLINE=1 and no usable mirror for ${mirror_path}" >&2 + exit 1 +fi + +echo "fetch-artifact: fetching upstream: ${upstream_url}" >&2 +curl -fL --retry 3 -o "$out" "$upstream_url"