aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linuxkpi
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linuxkpi')
-rw-r--r--sys/compat/linuxkpi/common/include/linux/acpi.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bitops.h12
-rw-r--r--sys/compat/linuxkpi/common/include/linux/dma-mapping.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/eventfd.h54
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ieee80211.h40
-rw-r--r--sys/compat/linuxkpi/common/include/linux/minmax.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/netdevice.h24
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pci.h27
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/radix-tree.h29
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_buf.h73
-rw-r--r--sys/compat/linuxkpi/common/include/linux/skbuff.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h48
-rw-r--r--sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h14
-rw-r--r--sys/compat/linuxkpi/common/include/net/cfg80211.h8
-rw-r--r--sys/compat/linuxkpi/common/include/net/mac80211.h17
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c116
-rw-r--r--sys/compat/linuxkpi/common/src/linux_current.c10
-rw-r--r--sys/compat/linuxkpi/common/src/linux_eventfd.c63
-rw-r--r--sys/compat/linuxkpi/common/src/linux_firmware.c3
-rw-r--r--sys/compat/linuxkpi/common/src/linux_pci.c36
-rw-r--r--sys/compat/linuxkpi/common/src/linux_radix.c219
-rw-r--r--sys/compat/linuxkpi/common/src/linux_seq_buf.c64
-rw-r--r--sys/compat/linuxkpi/common/src/linux_shmemfs.c7
-rw-r--r--sys/compat/linuxkpi/common/src/linux_xarray.c4
-rw-r--r--sys/compat/linuxkpi/dummy/include/linux/eventfd.h0
26 files changed, 806 insertions, 86 deletions
diff --git a/sys/compat/linuxkpi/common/include/linux/acpi.h b/sys/compat/linuxkpi/common/include/linux/acpi.h
index 3e1ec1b20626..a764a975c983 100644
--- a/sys/compat/linuxkpi/common/include/linux/acpi.h
+++ b/sys/compat/linuxkpi/common/include/linux/acpi.h
@@ -32,7 +32,7 @@
#include <linux/device.h>
#include <linux/uuid.h>
-#if defined(__aarch64__) || defined(__amd64__) || defined(__i386__)
+#if defined(__aarch64__) || defined(__amd64__) || defined(__i386__) || defined(__riscv)
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
diff --git a/sys/compat/linuxkpi/common/include/linux/bitops.h b/sys/compat/linuxkpi/common/include/linux/bitops.h
index ebe9aa120094..125081ab5b74 100644
--- a/sys/compat/linuxkpi/common/include/linux/bitops.h
+++ b/sys/compat/linuxkpi/common/include/linux/bitops.h
@@ -51,12 +51,6 @@
#define BITS_PER_TYPE(t) (sizeof(t) * BITS_PER_BYTE)
#define BITS_TO_BYTES(n) howmany((n), BITS_PER_BYTE)
-#define hweight8(x) bitcount((uint8_t)(x))
-#define hweight16(x) bitcount16(x)
-#define hweight32(x) bitcount32(x)
-#define hweight64(x) bitcount64(x)
-#define hweight_long(x) bitcountl(x)
-
#if __has_builtin(__builtin_popcountg)
#define HWEIGHT8(x) (__builtin_popcountg((uint8_t)(x)))
#define HWEIGHT16(x) (__builtin_popcountg((uint16_t)(x)))
@@ -70,6 +64,12 @@
#define HWEIGHT64(x) (__const_bitcount64((uint64_t)(x)))
#endif
+#define hweight8(x) (__builtin_constant_p(x) ? HWEIGHT8(x) : bitcount((uint8_t)(x)))
+#define hweight16(x) (__builtin_constant_p(x) ? HWEIGHT16(x) : bitcount16(x))
+#define hweight32(x) (__builtin_constant_p(x) ? HWEIGHT32(x) : bitcount32(x))
+#define hweight64(x) (__builtin_constant_p(x) ? HWEIGHT64(x) : bitcount64(x))
+#define hweight_long(x) bitcountl(x)
+
static inline int
__ffs(int mask)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
index 2d8e1196d3d3..76efbfd51074 100644
--- a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
+++ b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
@@ -96,6 +96,8 @@ void *linux_dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag);
void *linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag);
+void linuxkpi_dmam_free_coherent(struct device *dev, size_t size,
+ void *addr, dma_addr_t dma_handle);
dma_addr_t linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len); /* backward compat */
dma_addr_t lkpi_dma_map_phys(struct device *, vm_paddr_t, size_t,
enum dma_data_direction, unsigned long);
@@ -181,6 +183,13 @@ dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
kmem_free(cpu_addr, size);
}
+static inline void
+dmam_free_coherent(struct device *dev, size_t size, void *addr,
+ dma_addr_t dma_handle)
+{
+ linuxkpi_dmam_free_coherent(dev, size, addr, dma_handle);
+}
+
static inline dma_addr_t
dma_map_page_attrs(struct device *dev, struct page *page, size_t offset,
size_t size, enum dma_data_direction direction, unsigned long attrs)
diff --git a/sys/compat/linuxkpi/common/include/linux/eventfd.h b/sys/compat/linuxkpi/common/include/linux/eventfd.h
new file mode 100644
index 000000000000..d167d4b7d189
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/eventfd.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2025 The FreeBSD Foundation
+ * Copyright (c) 2025 Jean-Sébastien Pédron
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LINUXKPI_LINUX_EVENTFD_H_
+#define _LINUXKPI_LINUX_EVENTFD_H_
+
+#include <sys/eventfd.h>
+
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/percpu-defs.h>
+#include <linux/percpu.h>
+#include <linux/sched.h>
+
+/*
+ * Linux uses `struct eventfd_ctx`, but FreeBSD defines `struct eventfd`. Here,
+ * we define a synonym to the FreeBSD structure. This allows to keep Linux code
+ * unmodified.
+ */
+#define eventfd_ctx eventfd
+
+#define eventfd_ctx_fdget lkpi_eventfd_ctx_fdget
+struct eventfd_ctx *lkpi_eventfd_ctx_fdget(int fd);
+
+#define eventfd_ctx_put lkpi_eventfd_ctx_put
+void lkpi_eventfd_ctx_put(struct eventfd_ctx *ctx);
+
+#endif /* _LINUXKPI_LINUX_EVENTFD_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
index 12160df43915..d1eba94a3ad8 100644
--- a/sys/compat/linuxkpi/common/include/linux/ieee80211.h
+++ b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -51,8 +51,16 @@ extern int linuxkpi_debug_80211;
#define IMPROVE(fmt, ...) if (linuxkpi_debug_80211 & D80211_IMPROVE) \
printf("%s:%d: XXX LKPI80211 IMPROVE " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
-
-/* 9.4.2.55 Management MIC element (CMAC-256, GMAC-128, and GMAC-256). */
+/* 802.11-2024, 9.4.2.53 MME. */
+/* BIP-CMAC-128 */
+struct ieee80211_mmie {
+ uint8_t element_id;
+ uint8_t length;
+ uint16_t key_id;
+ uint8_t ipn[6];
+ uint8_t mic[8];
+};
+/* BIP-CMAC-256, BIP-GMAC-128, BIP-GMAC-256 */
struct ieee80211_mmie_16 {
uint8_t element_id;
uint8_t length;
@@ -108,7 +116,18 @@ struct ieee80211_mmie_16 {
#define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100
enum ieee80211_rate_flags {
- IEEE80211_RATE_SHORT_PREAMBLE = BIT(0),
+ IEEE80211_RATE_SHORT_PREAMBLE = BIT(0), /* 2.4Ghz, CCK */
+ IEEE80211_RATE_SUPPORTS_5MHZ = BIT(1),
+ IEEE80211_RATE_SUPPORTS_10MHZ = BIT(2),
+ IEEE80211_RATE_ERP_G = BIT(3),
+
+ /*
+ * According to documentation these are flags initialized internally.
+ * See lkpi_wiphy_band_annotate().
+ */
+ IEEE80211_RATE_MANDATORY_A = BIT(4),
+ IEEE80211_RATE_MANDATORY_G = BIT(5),
+ IEEE80211_RATE_MANDATORY_B = BIT(6),
};
enum ieee80211_rate_control_changed_flags {
@@ -200,6 +219,7 @@ enum ieee80211_min_mpdu_start_spacing {
#define IEEE80211_FCTL_TODS (IEEE80211_FC1_DIR_TODS << 8)
#define IEEE80211_FCTL_MOREFRAGS (IEEE80211_FC1_MORE_FRAG << 8)
#define IEEE80211_FCTL_PM (IEEE80211_FC1_PWR_MGT << 8)
+#define IEEE80211_FCTL_MOREDATA (IEEE80211_FC1_MORE_DATA << 8)
#define IEEE80211_FTYPE_MGMT IEEE80211_FC0_TYPE_MGT
#define IEEE80211_FTYPE_CTL IEEE80211_FC0_TYPE_CTL
@@ -461,18 +481,6 @@ enum ieee80211_tx_control_flags {
IEEE80211_TX_CTRL_MLO_LINK = 0xF0000000, /* This is IEEE80211_LINK_UNSPECIFIED on the high bits. */
};
-enum ieee80211_tx_rate_flags {
- /* XXX TODO .. right shift numbers */
- IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(0),
- IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(1),
- IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(2),
- IEEE80211_TX_RC_GREEN_FIELD = BIT(3),
- IEEE80211_TX_RC_MCS = BIT(4),
- IEEE80211_TX_RC_SHORT_GI = BIT(5),
- IEEE80211_TX_RC_VHT_MCS = BIT(6),
- IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(7),
-};
-
#define IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED -128
#define IEEE80211_HT_CTL_LEN 4
diff --git a/sys/compat/linuxkpi/common/include/linux/minmax.h b/sys/compat/linuxkpi/common/include/linux/minmax.h
index d48958f0899f..fb8eb6f704b4 100644
--- a/sys/compat/linuxkpi/common/include/linux/minmax.h
+++ b/sys/compat/linuxkpi/common/include/linux/minmax.h
@@ -71,4 +71,7 @@
b = _swap_tmp; \
} while (0)
+/* XXX would have to make sure both are unsigned. */
+#define umin(x, y) MIN(x, y)
+
#endif /* _LINUXKPI_LINUX_MINMAX_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/netdevice.h b/sys/compat/linuxkpi/common/include/linux/netdevice.h
index cf27753bcb80..dfed5fbd61b4 100644
--- a/sys/compat/linuxkpi/common/include/linux/netdevice.h
+++ b/sys/compat/linuxkpi/common/include/linux/netdevice.h
@@ -160,6 +160,30 @@ struct net_device {
#define SET_NETDEV_DEV(_ndev, _dev) (_ndev)->dev.parent = _dev;
+enum net_device_path_type {
+ DEV_PATH_MTK_WDMA,
+};
+
+struct net_device_path {
+ enum net_device_path_type type;
+ const struct net_device *dev;
+ /* We assume there's a struct per type. */
+ union {
+ struct {
+ uint16_t wcid;
+ uint8_t wdma_idx;
+ uint8_t queue;
+ uint8_t bss;
+ uint8_t amsdu;
+ } mtk_wdma;
+ };
+};
+
+struct net_device_path_ctx {
+ const struct net_device *dev;
+};
+
+
/* -------------------------------------------------------------------------- */
/* According to linux::ipoib_main.c. */
struct netdev_notifier_info {
diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h
index 8fe09554aed2..c337be67f5a4 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -1136,19 +1136,28 @@ pci_num_vf(struct pci_dev *dev)
static inline enum pci_bus_speed
pcie_get_speed_cap(struct pci_dev *dev)
{
+ struct pci_dev *pbus;
device_t root;
uint32_t lnkcap, lnkcap2;
int error, pos;
- root = device_get_parent(dev->dev.bsddev);
- if (root == NULL)
- return (PCI_SPEED_UNKNOWN);
- root = device_get_parent(root);
- if (root == NULL)
- return (PCI_SPEED_UNKNOWN);
- root = device_get_parent(root);
- if (root == NULL)
- return (PCI_SPEED_UNKNOWN);
+ /*
+ * We should always be called on a PCI device.
+ * The only current consumer I could find was amdgpu which either
+ * calls us directly on a pdev(drmn?) or with the result of
+ * pci_upstream_bridge().
+ *
+ * Treat "drmn" as special again as it is not a PCI device.
+ */
+ if (dev->pdrv != NULL && dev->pdrv->isdrm) {
+ pbus = pci_upstream_bridge(dev);
+ if (pbus == NULL)
+ return (PCI_SPEED_UNKNOWN);
+ } else
+ pbus = dev;
+
+ /* "root" may be misleading as it may not be that. */
+ root = pbus->dev.bsddev;
if (pci_get_vendor(root) == PCI_VENDOR_ID_VIA ||
pci_get_vendor(root) == PCI_VENDOR_ID_SERVERWORKS)
diff --git a/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h b/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h
index aad46cc25b1b..6491cbeab7e2 100644
--- a/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h
+++ b/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h
@@ -49,6 +49,7 @@ struct ptp_clock_info {
int (*adjtime)(struct ptp_clock_info *, s64);
int (*getcrosststamp)(struct ptp_clock_info *, struct system_device_crosststamp *);
int (*gettime64)(struct ptp_clock_info *, struct timespec *);
+ int (*settime64)(struct ptp_clock_info *, const struct timespec *);
};
static inline struct ptp_clock *
diff --git a/sys/compat/linuxkpi/common/include/linux/radix-tree.h b/sys/compat/linuxkpi/common/include/linux/radix-tree.h
index 55f0fc949807..7182f4a9e407 100644
--- a/sys/compat/linuxkpi/common/include/linux/radix-tree.h
+++ b/sys/compat/linuxkpi/common/include/linux/radix-tree.h
@@ -38,12 +38,19 @@
#define RADIX_TREE_MAX_HEIGHT \
howmany(sizeof(long) * NBBY, RADIX_TREE_MAP_SHIFT)
+#define RADIX_TREE_MAX_TAGS 3
+#define RADIX_TREE_TAG_LONGS RADIX_TREE_MAP_SIZE
+
#define RADIX_TREE_ENTRY_MASK 3UL
#define RADIX_TREE_EXCEPTIONAL_ENTRY 2UL
#define RADIX_TREE_EXCEPTIONAL_SHIFT 2
+#define RADIX_TREE_ITER_TAG_MASK 0x0f
+#define RADIX_TREE_ITER_TAGGED 0x10
+
struct radix_tree_node {
void *slots[RADIX_TREE_MAP_SIZE];
+ unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
int count;
};
@@ -51,6 +58,8 @@ struct radix_tree_root {
struct radix_tree_node *rnode;
gfp_t gfp_mask;
int height;
+ /* Linux stores root tags inside `gfp_mask`. */
+ unsigned long tags[RADIX_TREE_MAX_TAGS];
};
struct radix_tree_iter {
@@ -64,9 +73,16 @@ struct radix_tree_iter {
#define RADIX_TREE(name, mask) \
struct radix_tree_root name = RADIX_TREE_INIT(mask)
-#define radix_tree_for_each_slot(slot, root, iter, start) \
- for ((iter)->index = (start); \
- radix_tree_iter_find(root, iter, &(slot)); (iter)->index++)
+#define radix_tree_for_each_slot(slot, root, iter, start) \
+ for ((iter)->index = (start); \
+ radix_tree_iter_find(root, iter, &(slot), 0); \
+ (iter)->index++)
+
+#define radix_tree_for_each_slot_tagged(slot, root, iter, start, tag) \
+ for ((iter)->index = (start); \
+ radix_tree_iter_find(root, iter, &(slot), \
+ RADIX_TREE_ITER_TAGGED | tag); \
+ (iter)->index++)
static inline void *
radix_tree_deref_slot(void **slot)
@@ -84,7 +100,12 @@ void *radix_tree_lookup(const struct radix_tree_root *, unsigned long);
void *radix_tree_delete(struct radix_tree_root *, unsigned long);
int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
int radix_tree_store(struct radix_tree_root *, unsigned long, void **);
-bool radix_tree_iter_find(const struct radix_tree_root *, struct radix_tree_iter *, void ***);
+bool radix_tree_iter_find(const struct radix_tree_root *, struct radix_tree_iter *, void ***, int);
void radix_tree_iter_delete(struct radix_tree_root *, struct radix_tree_iter *, void **);
+void *radix_tree_tag_set(struct radix_tree_root *root, unsigned long index, unsigned int tag);
+void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag);
+int radix_tree_tagged(const struct radix_tree_root *root, unsigned int tag);
+unsigned int radix_tree_gang_lookup(const struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items);
+unsigned int radix_tree_gang_lookup_tag(const struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag);
#endif /* _LINUXKPI_LINUX_RADIX_TREE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/seq_buf.h b/sys/compat/linuxkpi/common/include/linux/seq_buf.h
new file mode 100644
index 000000000000..d6246a40e6f7
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/seq_buf.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_SEQ_BUF_H_
+#define _LINUXKPI_LINUX_SEQ_BUF_H_
+
+#include <linux/bug.h>
+#include <linux/minmax.h>
+#include <linux/seq_file.h>
+#include <linux/types.h>
+
+struct seq_buf {
+ char *buffer;
+ size_t size;
+ size_t len;
+};
+
+#define DECLARE_SEQ_BUF(NAME, SIZE) \
+ struct seq_buf NAME = { \
+ .buffer = (char[SIZE]) { 0 }, \
+ .size = SIZE, \
+ }
+
+static inline void
+seq_buf_clear(struct seq_buf *s)
+{
+ s->len = 0;
+ if (s->size > 0)
+ s->buffer[0] = '\0';
+}
+
+static inline void
+seq_buf_set_overflow(struct seq_buf *s)
+{
+ s->len = s->size + 1;
+}
+
+static inline bool
+seq_buf_has_overflowed(struct seq_buf *s)
+{
+ return (s->len > s->size);
+}
+
+static inline bool
+seq_buf_buffer_left(struct seq_buf *s)
+{
+ if (seq_buf_has_overflowed(s))
+ return (0);
+
+ return (s->size - s->len);
+}
+
+#define seq_buf_init(s, buf, size) linuxkpi_seq_buf_init((s), (buf), (size))
+void linuxkpi_seq_buf_init(struct seq_buf *s, char *buf, unsigned int size);
+
+#define seq_buf_printf(s, f, ...) linuxkpi_seq_buf_printf((s), (f), __VA_ARGS__)
+int linuxkpi_seq_buf_printf(struct seq_buf *s, const char *fmt, ...) \
+ __printflike(2, 3);
+
+#define seq_buf_vprintf(s, f, a) linuxkpi_seq_buf_vprintf((s), (f), (a))
+int linuxkpi_seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args);
+
+#define seq_buf_str(s) linuxkpi_seq_buf_str((s))
+const char * linuxkpi_seq_buf_str(struct seq_buf *s);
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/skbuff.h b/sys/compat/linuxkpi/common/include/linux/skbuff.h
index 2e560a120e41..c43d6daff5ee 100644
--- a/sys/compat/linuxkpi/common/include/linux/skbuff.h
+++ b/sys/compat/linuxkpi/common/include/linux/skbuff.h
@@ -770,7 +770,7 @@ ___skb_queue_splice(const struct sk_buff_head *from,
}
static inline void
-skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
+skb_queue_splice(const struct sk_buff_head *from, struct sk_buff_head *to)
{
SKB_TRACE2(from, to);
@@ -780,6 +780,13 @@ skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
___skb_queue_splice(from, (struct sk_buff *)to, to->next);
to->qlen += from->qlen;
+}
+
+static inline void
+skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
+{
+
+ skb_queue_splice(from, to);
__skb_queue_head_init(from);
}
diff --git a/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h b/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h
new file mode 100644
index 000000000000..ade0b06d839f
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2026 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H
+#define _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H
+
+#include <linux/kernel.h> /* pr_debug */
+
+enum airoha_npu_wlan_get_cmd {
+ __dummy_airoha_npu_wlan_get_cmd,
+};
+enum airoha_npu_wlan_set_cmd {
+ __dummy_airoha_npu_wlan_set_cmd,
+};
+
+struct airoha_npu {
+};
+struct airoha_npu_rx_dma_desc {
+};
+struct airoha_npu_tx_dma_desc {
+};
+
+static __inline int
+airoha_npu_wlan_send_msg(void *npu, int ifindex,
+ enum airoha_npu_wlan_set_cmd cmd, void *val, size_t len, gfp_t gfp)
+{
+ pr_debug("%s: TODO\n", __func__);
+ return (-EOPNOTSUPP);
+}
+
+static __inline int
+airoha_npu_wlan_get_msg(void *npu, int ifindex,
+ enum airoha_npu_wlan_get_cmd cmd, void *val, size_t len, gfp_t gfp)
+{
+ pr_debug("%s: TODO\n", __func__);
+ return (-EOPNOTSUPP);
+}
+
+static __inline void
+airoha_npu_wlan_enable_irq(struct airoha_npu *npu, int q)
+{
+ pr_debug("%s: TODO\n", __func__);
+}
+
+#endif /* _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h b/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h
index 2b9c6ae4911e..64daa8c78c9d 100644
--- a/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h
+++ b/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2022-2025 Bjoern A. Zeeb
+ * Copyright (c) 2022-2026 Bjoern A. Zeeb
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -37,7 +37,13 @@ mtk_wed_device_active(struct mtk_wed_device *dev __unused)
static inline bool
mtk_wed_get_rx_capa(struct mtk_wed_device *dev __unused)
{
+ pr_debug("%s: TODO\n", __func__);
+ return (false);
+}
+static inline bool
+mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev __unused)
+{
pr_debug("%s: TODO\n", __func__);
return (false);
}
@@ -66,6 +72,12 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev __unused)
{
return (false);
}
+
+static inline bool
+mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev __unused)
+{
+ return (false);
+}
#endif /* CONFIG_NET_MEDIATEK_SOC_WED */
#endif /* _LINUXKPI_LINUX_SOC_MEDIATEK_MTK_WED_H */
diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h
index d7ed2bc97c98..94d34fb9dc0c 100644
--- a/sys/compat/linuxkpi/common/include/net/cfg80211.h
+++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h
@@ -124,6 +124,7 @@ enum ieee80211_channel_flags {
IEEE80211_CHAN_PSD = BIT(12),
IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP = BIT(13),
IEEE80211_CHAN_CAN_MONITOR = BIT(14),
+ IEEE80211_CHAN_NO_EHT = BIT(15),
};
#define IEEE80211_CHAN_NO_HT40 (IEEE80211_CHAN_NO_HT40MINUS|IEEE80211_CHAN_NO_HT40PLUS)
@@ -152,6 +153,8 @@ struct linuxkpi_ieee80211_channel {
int orig_mpwr;
};
+#define NL80211_EHT_NSS_MAX 16
+
struct cfg80211_bitrate_mask {
/* TODO FIXME */
struct {
@@ -159,6 +162,7 @@ struct cfg80211_bitrate_mask {
uint8_t ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
uint16_t vht_mcs[8];
uint16_t he_mcs[8];
+ uint16_t eht_mcs[NL80211_EHT_NSS_MAX];
enum nl80211_txrate_gi gi;
enum nl80211_he_gi he_gi;
uint8_t he_ltf; /* XXX enum? */
@@ -1230,6 +1234,7 @@ struct cfg80211_ops {
struct wiphy *linuxkpi_wiphy_new(const struct cfg80211_ops *, size_t);
void linuxkpi_wiphy_free(struct wiphy *wiphy);
+int linuxkpi_80211_wiphy_register(struct wiphy *);
void linuxkpi_wiphy_work_queue(struct wiphy *, struct wiphy_work *);
void linuxkpi_wiphy_work_cancel(struct wiphy *, struct wiphy_work *);
@@ -1749,8 +1754,7 @@ wiphy_net(struct wiphy *wiphy)
static __inline int
wiphy_register(struct wiphy *wiphy)
{
- TODO();
- return (0);
+ return (linuxkpi_80211_wiphy_register(wiphy));
}
static __inline void
diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index 6e2f3f2d8781..18891d035094 100644
--- a/sys/compat/linuxkpi/common/include/net/mac80211.h
+++ b/sys/compat/linuxkpi/common/include/net/mac80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
* Copyright (c) 2020-2025 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -789,10 +789,21 @@ struct ieee80211_tx_queue_params {
struct ieee80211_he_mu_edca_param_ac_rec mu_edca_param_rec;
};
+enum mac80211_rate_control_flags {
+ IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(0),
+ IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(1),
+ IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(2),
+ IEEE80211_TX_RC_GREEN_FIELD = BIT(3),
+ IEEE80211_TX_RC_MCS = BIT(4),
+ IEEE80211_TX_RC_SHORT_GI = BIT(5),
+ IEEE80211_TX_RC_VHT_MCS = BIT(6),
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(7),
+};
+
struct ieee80211_tx_rate {
uint8_t idx;
uint16_t count:5,
- flags:11;
+ flags:11; /* enum mac80211_rate_control_flags */
};
enum ieee80211_vif_driver_flags {
@@ -1092,6 +1103,8 @@ struct ieee80211_ops {
void (*rfkill_poll)(struct ieee80211_hw *);
+ int (*net_fill_forward_path)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct net_device_path_ctx *, struct net_device_path *);
+
/* #ifdef CONFIG_MAC80211_DEBUGFS */ /* Do not change depending on compile-time option. */
void (*sta_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct dentry *);
void (*vif_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *);
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 1ac28dfef448..4ce0a2094adb 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -896,41 +896,34 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
/* Deflink information. */
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *supband;
+ uint32_t rate_mandatory;;
supband = hw->wiphy->bands[band];
if (supband == NULL)
continue;
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ /* We have to assume 11g support here. */
+ rate_mandatory = IEEE80211_RATE_MANDATORY_G |
+ IEEE80211_RATE_MANDATORY_B;
+ break;
+ case NL80211_BAND_5GHZ:
+ rate_mandatory = IEEE80211_RATE_MANDATORY_A;
+ break;
+ default:
+ continue;
+ }
+
for (i = 0; i < supband->n_bitrates; i++) {
- switch (band) {
- case NL80211_BAND_2GHZ:
- switch (supband->bitrates[i].bitrate) {
- case 240: /* 11g only */
- case 120: /* 11g only */
- case 110:
- case 60: /* 11g only */
- case 55:
- case 20:
- case 10:
- sta->deflink.supp_rates[band] |= BIT(i);
- break;
- }
- break;
- case NL80211_BAND_5GHZ:
- switch (supband->bitrates[i].bitrate) {
- case 240:
- case 120:
- case 60:
- sta->deflink.supp_rates[band] |= BIT(i);
- break;
- }
- break;
- }
+ if ((supband->bitrates[i].flags & rate_mandatory) != 0)
+ sta->deflink.supp_rates[band] |= BIT(i);
}
}
sta->deflink.smps_mode = IEEE80211_SMPS_OFF;
sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20;
+ sta->deflink.agg.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
sta->deflink.rx_nss = 1;
sta->deflink.sta = sta;
@@ -6979,7 +6972,9 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
}
if (bootverbose) {
- ic_printf(ic, "netdev_features %b\n", hw->netdev_features, NETIF_F_BITS);
+ if (hw->netdev_features != 0)
+ ic_printf(ic, "netdev_features %b\n",
+ hw->netdev_features, NETIF_F_BITS);
ieee80211_announce(ic);
}
@@ -8028,6 +8023,77 @@ linuxkpi_wiphy_free(struct wiphy *wiphy)
kfree(lwiphy);
}
+static void
+lkpi_wiphy_band_annotate(struct wiphy *wiphy)
+{
+ int band;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *supband;
+ int i;
+
+ supband = wiphy->bands[band];
+ if (supband == NULL)
+ continue;
+
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ case NL80211_BAND_5GHZ:
+ break;
+ default:
+#ifdef LINUXKPI_DEBUG_80211
+ IMPROVE("band %d(%s) not yet supported",
+ band, lkpi_nl80211_band_name(band));
+ /* For bands added here, also check lkpi_lsta_alloc(). */
+#endif
+ continue;
+ }
+
+ for (i = 0; i < supband->n_bitrates; i++) {
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ switch (supband->bitrates[i].bitrate) {
+ case 110:
+ case 55:
+ case 20:
+ case 10:
+ supband->bitrates[i].flags |=
+ IEEE80211_RATE_MANDATORY_B;
+ /* FALLTHROUGH */
+ /* 11g only */
+ case 240:
+ case 120:
+ case 60:
+ supband->bitrates[i].flags |=
+ IEEE80211_RATE_MANDATORY_G;
+ break;
+ }
+ break;
+ case NL80211_BAND_5GHZ:
+ switch (supband->bitrates[i].bitrate) {
+ case 240:
+ case 120:
+ case 60:
+ supband->bitrates[i].flags |=
+ IEEE80211_RATE_MANDATORY_A;
+ break;
+ }
+ break;
+ }
+ }
+ }
+}
+
+int
+linuxkpi_80211_wiphy_register(struct wiphy *wiphy)
+{
+ TODO("Lots of checks and initialization");
+
+ lkpi_wiphy_band_annotate(wiphy);
+
+ return (0);
+}
+
static uint32_t
lkpi_cfg80211_calculate_bitrate_ht(struct rate_info *rate)
{
diff --git a/sys/compat/linuxkpi/common/src/linux_current.c b/sys/compat/linuxkpi/common/src/linux_current.c
index c342eb279caa..3bc5d31d211a 100644
--- a/sys/compat/linuxkpi/common/src/linux_current.c
+++ b/sys/compat/linuxkpi/common/src/linux_current.c
@@ -90,11 +90,8 @@ linux_alloc_current(struct thread *td, int flags)
}
ts = uma_zalloc(linux_current_zone, flags | M_ZERO);
- if (ts == NULL) {
- if ((flags & (M_WAITOK | M_NOWAIT)) == M_WAITOK)
- panic("linux_alloc_current: failed to allocate task");
+ if (ts == NULL)
return (ENOMEM);
- }
mm = NULL;
/* setup new task structure */
@@ -118,10 +115,7 @@ linux_alloc_current(struct thread *td, int flags)
PROC_UNLOCK(proc);
mm = uma_zalloc(linux_mm_zone, flags | M_ZERO);
if (mm == NULL) {
- if ((flags & (M_WAITOK | M_NOWAIT)) == M_WAITOK)
- panic(
- "linux_alloc_current: failed to allocate mm");
- uma_zfree(linux_current_zone, mm);
+ uma_zfree(linux_current_zone, ts);
return (ENOMEM);
}
diff --git a/sys/compat/linuxkpi/common/src/linux_eventfd.c b/sys/compat/linuxkpi/common/src/linux_eventfd.c
new file mode 100644
index 000000000000..126c6c54b9a5
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_eventfd.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2025 The FreeBSD Foundation
+ * Copyright (c) 2025 Jean-Sébastien Pédron
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+
+#include <linux/eventfd.h>
+
+struct eventfd_ctx *
+lkpi_eventfd_ctx_fdget(int fd)
+{
+ struct file *fp;
+ struct eventfd_ctx *ctx;
+
+ /* Lookup file pointer by file descriptor index. */
+ if (fget_unlocked(curthread, fd, &cap_no_rights, &fp) != 0)
+ return (ERR_PTR(-EBADF));
+
+ /*
+ * eventfd_get() bumps the refcount, so we can safely release the
+ * reference on the file itself afterwards.
+ */
+ ctx = eventfd_get(fp);
+ fdrop(fp, curthread);
+
+ if (ctx == NULL)
+ return (ERR_PTR(-EBADF));
+
+ return (ctx);
+}
+
+void
+lkpi_eventfd_ctx_put(struct eventfd_ctx *ctx)
+{
+ eventfd_put(ctx);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_firmware.c b/sys/compat/linuxkpi/common/src/linux_firmware.c
index 12658df5ce83..0c6d855501ef 100644
--- a/sys/compat/linuxkpi/common/src/linux_firmware.c
+++ b/sys/compat/linuxkpi/common/src/linux_firmware.c
@@ -66,7 +66,8 @@ _linuxkpi_request_firmware(const char *fw_name, const struct linuxkpi_firmware *
uint32_t flags;
if (fw_name == NULL || fw == NULL || dev == NULL) {
- *fw = NULL;
+ if (fw != NULL)
+ *fw = NULL;
return (-EINVAL);
}
diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c
index df5255d8a6ca..9e4fad45433a 100644
--- a/sys/compat/linuxkpi/common/src/linux_pci.c
+++ b/sys/compat/linuxkpi/common/src/linux_pci.c
@@ -1796,6 +1796,42 @@ lkpi_dmam_free_coherent(struct device *dev, void *p)
dma_free_coherent(dev, dr->size, dr->mem, *dr->handle);
}
+static int
+lkpi_dmam_coherent_match(struct device *dev, void *dr, void *mp)
+{
+ struct lkpi_devres_dmam_coherent *a, *b;
+
+ a = dr;
+ b = mp;
+
+ if (a->mem != b->mem)
+ return (0);
+ if (a->size != b->size || a->handle != b->handle)
+ dev_WARN(dev, "for mem %p: size %zu != %zu || handle %#jx != %#jx\n",
+ a->mem, a->size, b->size,
+ (uintmax_t)a->handle, (uintmax_t)b->handle);
+ return (1);
+}
+
+void
+linuxkpi_dmam_free_coherent(struct device *dev, size_t size,
+ void *addr, dma_addr_t dma_handle)
+{
+ struct lkpi_devres_dmam_coherent match = {
+ .size = size,
+ .handle = &dma_handle,
+ .mem = addr
+ };
+ int error;
+
+ error = devres_destroy(dev, lkpi_dmam_free_coherent,
+ lkpi_dmam_coherent_match, &match);
+ if (error != 0)
+ dev_WARN(dev, "devres_destroy returned %d, size %zu addr %p "
+ "dma_handle %#jx\n", error, size, addr, (uintmax_t)dma_handle);
+ dma_free_coherent(dev, size, addr, dma_handle);
+}
+
void *
linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t flag)
diff --git a/sys/compat/linuxkpi/common/src/linux_radix.c b/sys/compat/linuxkpi/common/src/linux_radix.c
index ee6b3a63c370..760f583f25c8 100644
--- a/sys/compat/linuxkpi/common/src/linux_radix.c
+++ b/sys/compat/linuxkpi/common/src/linux_radix.c
@@ -52,6 +52,81 @@ radix_pos(long id, int height)
return (id >> (RADIX_TREE_MAP_SHIFT * height)) & RADIX_TREE_MAP_MASK;
}
+static inline int
+root_tag_get(const struct radix_tree_root *root, unsigned tag)
+{
+ return (test_bit(tag, root->tags));
+}
+
+static inline void
+root_tag_set(struct radix_tree_root *root, unsigned tag)
+{
+ set_bit(tag, root->tags);
+}
+
+static inline void
+root_tag_clear(struct radix_tree_root *root, unsigned tag)
+{
+ clear_bit(tag, root->tags);
+}
+
+static inline int
+tag_get(const struct radix_tree_node *node, unsigned int tag, int pos)
+{
+ return (test_bit(pos, node->tags[tag]));
+}
+
+static inline void
+tag_set(struct radix_tree_node *node, unsigned int tag, int pos)
+{
+ set_bit(pos, node->tags[tag]);
+}
+
+static inline void
+tag_clear(struct radix_tree_node *node, unsigned int tag, int pos)
+{
+ clear_bit(pos, node->tags[tag]);
+}
+
+static inline bool
+any_tag_set(const struct radix_tree_node *node, unsigned int tag)
+{
+ unsigned int pos;
+
+ for (pos = 0; pos < RADIX_TREE_TAG_LONGS; pos++)
+ if (node->tags[tag][pos])
+ return (true);
+
+ return (false);
+}
+
+static void
+node_tag_clear(struct radix_tree_root *root, struct radix_tree_node *stack[],
+ unsigned long index, unsigned int tag)
+{
+ struct radix_tree_node *node;
+ int height, pos;
+
+ height = 1;
+ node = stack[height];
+
+ while (node && height <= root->height - 1) {
+ pos = radix_pos(index, height);
+ if (!tag_get(node, tag, pos))
+ return;
+
+ tag_clear(node, tag, pos);
+ if (any_tag_set(node, tag))
+ return;
+
+ height++;
+ node = stack[height];
+ }
+
+ if (root_tag_get(root, tag))
+ root_tag_clear(root, tag);
+}
+
static void
radix_tree_clean_root_node(struct radix_tree_root *root)
{
@@ -84,14 +159,70 @@ out:
return (item);
}
+unsigned int
+radix_tree_gang_lookup(const struct radix_tree_root *root, void **results,
+ unsigned long first_index, unsigned int max_items)
+{
+ struct radix_tree_iter iter;
+ void **slot;
+ int count;
+
+ count = 0;
+ if (max_items == 0)
+ return (count);
+
+ radix_tree_for_each_slot(slot, root, &iter, first_index) {
+ results[count] = *slot;
+ if (results[count] == NULL)
+ continue;
+
+ count++;
+ if (count == max_items)
+ break;
+ }
+
+ return (count);
+}
+
+unsigned int
+radix_tree_gang_lookup_tag(const struct radix_tree_root *root,
+ void **results, unsigned long first_index, unsigned int max_items,
+ unsigned int tag)
+{
+ struct radix_tree_iter iter;
+ void **slot;
+ int count;
+
+ count = 0;
+ if (max_items == 0)
+ return (count);
+
+ radix_tree_for_each_slot_tagged(slot, root, &iter, first_index, tag) {
+ results[count] = *slot;
+ if (results[count] == NULL)
+ continue;
+
+ count++;
+ if (count == max_items)
+ break;
+ }
+
+ return (count);
+}
+
bool
radix_tree_iter_find(const struct radix_tree_root *root,
- struct radix_tree_iter *iter, void ***pppslot)
+ struct radix_tree_iter *iter, void ***pppslot, int flags)
{
struct radix_tree_node *node;
unsigned long index = iter->index;
+ unsigned int tag;
int height;
+ tag = flags & RADIX_TREE_ITER_TAG_MASK;
+ if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag))
+ return (false);
+
restart:
node = root->rnode;
if (node == NULL)
@@ -109,7 +240,9 @@ restart:
*pppslot = node->slots + pos;
next = node->slots[pos];
- if (next == NULL) {
+ if (next == NULL ||
+ (flags & RADIX_TREE_ITER_TAGGED &&
+ !tag_get(next, tag, pos))) {
index += step;
index &= -step;
if ((index & mask) == 0)
@@ -131,6 +264,7 @@ radix_tree_delete(struct radix_tree_root *root, unsigned long index)
void *item;
int height;
int idx;
+ int tag;
item = NULL;
node = root->rnode;
@@ -144,9 +278,15 @@ radix_tree_delete(struct radix_tree_root *root, unsigned long index)
stack[height] = node;
node = node->slots[radix_pos(index, height--)];
}
- idx = radix_pos(index, 0);
- if (node)
+
+ if (node) {
+ idx = radix_pos(index, 0);
item = node->slots[idx];
+
+ for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++)
+ node_tag_clear(root, stack, index, tag);
+ }
+
/*
* If we removed something reduce the height of the tree.
*/
@@ -380,3 +520,74 @@ radix_tree_store(struct radix_tree_root *root, unsigned long index, void **ppite
node->count++;
return (0);
}
+
+void *
+radix_tree_tag_set(struct radix_tree_root *root, unsigned long index,
+ unsigned int tag)
+{
+ struct radix_tree_node *node;
+ void *item;
+ int height, pos;
+
+ item = NULL;
+ node = root->rnode;
+ height = root->height - 1;
+ if (index > radix_max(root))
+ goto out;
+
+ while (height) {
+ KASSERT(
+ node != NULL,
+ ("Node at index %lu does not exist in radix tree %p",
+ index, root));
+
+ pos = radix_pos(index, height--);
+ node = node->slots[pos];
+
+ if (!tag_get(node, tag, pos))
+ tag_set(node, tag, pos);
+ }
+
+ item = node->slots[radix_pos(index, 0)];
+
+ root_tag_set(root, tag);
+
+out:
+ return (item);
+}
+
+void *
+radix_tree_tag_clear(struct radix_tree_root *root,
+ unsigned long index, unsigned int tag)
+{
+ struct radix_tree_node *stack[RADIX_TREE_MAX_HEIGHT];
+ struct radix_tree_node *node;
+ void *item;
+ int height;
+
+ item = NULL;
+ node = root->rnode;
+ height = root->height - 1;
+ if (index > radix_max(root))
+ goto out;
+
+ while (height && node) {
+ stack[height] = node;
+ node = node->slots[radix_pos(index, height--)];
+ }
+
+ if (node) {
+ item = node->slots[radix_pos(index, 0)];
+
+ node_tag_clear(root, stack, index, tag);
+ }
+
+out:
+ return (item);
+}
+
+int
+radix_tree_tagged(const struct radix_tree_root *root, unsigned int tag)
+{
+ return (root_tag_get(root, tag));
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_seq_buf.c b/sys/compat/linuxkpi/common/src/linux_seq_buf.c
new file mode 100644
index 000000000000..112c53044c22
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_seq_buf.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <linux/seq_buf.h>
+
+void
+linuxkpi_seq_buf_init(struct seq_buf *s, char *buf, unsigned int size)
+{
+ s->buffer = buf;
+ s->size = size;
+
+ seq_buf_clear(s);
+}
+
+int
+linuxkpi_seq_buf_printf(struct seq_buf *s, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = seq_buf_vprintf(s, fmt, args);
+ va_end(args);
+
+ return (ret);
+}
+
+int
+linuxkpi_seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
+{
+ int ret;
+
+ if (!seq_buf_has_overflowed(s)) {
+ ret = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
+ if (s->len + ret < s->size) {
+ s->len += ret;
+ return (0);
+ }
+ }
+
+ seq_buf_set_overflow(s);
+ return (-1);
+}
+
+const char *
+linuxkpi_seq_buf_str(struct seq_buf *s)
+{
+ if (s->size == 0)
+ return ("");
+
+ if (seq_buf_buffer_left(s))
+ s->buffer[s->len] = '\0';
+ else
+ s->buffer[s->size - 1] = '\0';
+
+ return (s->buffer);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_shmemfs.c b/sys/compat/linuxkpi/common/src/linux_shmemfs.c
index 1fb17bc5c0cb..d5c118ba7624 100644
--- a/sys/compat/linuxkpi/common/src/linux_shmemfs.c
+++ b/sys/compat/linuxkpi/common/src/linux_shmemfs.c
@@ -62,11 +62,10 @@ linux_shmem_read_mapping_page_gfp(vm_object_t obj, int pindex, gfp_t gfp)
struct linux_file *
linux_shmem_file_setup(const char *name, loff_t size, unsigned long flags)
{
- struct fileobj {
+ struct {
struct linux_file file __aligned(sizeof(void *));
struct vnode vnode __aligned(sizeof(void *));
- };
- struct fileobj *fileobj;
+ } *fileobj;
struct linux_file *filp;
struct vnode *vp;
int error;
@@ -89,7 +88,7 @@ linux_shmem_file_setup(const char *name, loff_t size, unsigned long flags)
}
return (filp);
err_1:
- kfree(filp);
+ kfree(fileobj);
err_0:
return (ERR_PTR(error));
}
diff --git a/sys/compat/linuxkpi/common/src/linux_xarray.c b/sys/compat/linuxkpi/common/src/linux_xarray.c
index 3f07f6d7c59f..8caefbaf7e50 100644
--- a/sys/compat/linuxkpi/common/src/linux_xarray.c
+++ b/sys/compat/linuxkpi/common/src/linux_xarray.c
@@ -389,7 +389,7 @@ __xa_empty(struct xarray *xa)
XA_ASSERT_LOCKED(xa);
- return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp));
+ return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp, 0));
}
bool
@@ -426,7 +426,7 @@ __xa_next(struct xarray *xa, unsigned long *pindex, bool not_first)
return (NULL);
}
- found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot);
+ found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot, 0);
if (likely(found)) {
retval = *ppslot;
if (retval == NULL_VALUE)
diff --git a/sys/compat/linuxkpi/dummy/include/linux/eventfd.h b/sys/compat/linuxkpi/dummy/include/linux/eventfd.h
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/sys/compat/linuxkpi/dummy/include/linux/eventfd.h
+++ /dev/null