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/acpi/acpi.h78
-rw-r--r--sys/compat/linuxkpi/common/include/acpi/acpi_bus.h10
-rw-r--r--sys/compat/linuxkpi/common/include/asm/topology.h54
-rw-r--r--sys/compat/linuxkpi/common/include/kunit/static_stub.h15
-rw-r--r--sys/compat/linuxkpi/common/include/linux/acpi_amd_wbrf.h97
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bitops.h8
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cleanup.h49
-rw-r--r--sys/compat/linuxkpi/common/include/linux/compiler.h6
-rw-r--r--sys/compat/linuxkpi/common/include/linux/completion.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/container_of.h8
-rw-r--r--sys/compat/linuxkpi/common/include/linux/debugfs.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/devcoredump.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/device.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/dma-mapping.h64
-rw-r--r--sys/compat/linuxkpi/common/include/linux/etherdevice.h8
-rw-r--r--sys/compat/linuxkpi/common/include/linux/fs.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/gfp.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/highmem.h32
-rw-r--r--sys/compat/linuxkpi/common/include/linux/idr.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ieee80211.h72
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ioport.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/jiffies.h40
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kmod.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kobject.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ktime.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/leds.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/math.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/math64.h8
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mm.h82
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mm_types.h11
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mutex.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/netdev_features.h25
-rw-r--r--sys/compat/linuxkpi/common/include/linux/netdevice.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/nl80211.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/overflow.h180
-rw-r--r--sys/compat/linuxkpi/common/include/linux/page-flags.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pagemap.h11
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pagevec.h68
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pci.h111
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pci_ids.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pm_runtime.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/printk.h60
-rw-r--r--sys/compat/linuxkpi/common/include/linux/rcupdate.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ref_tracker.h93
-rw-r--r--sys/compat/linuxkpi/common/include/linux/refcount.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/scatterlist.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sched.h4
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_file.h29
-rw-r--r--sys/compat/linuxkpi/common/include/linux/shmem_fs.h10
-rw-r--r--sys/compat/linuxkpi/common/include/linux/skbuff.h71
-rw-r--r--sys/compat/linuxkpi/common/include/linux/slab.h163
-rw-r--r--sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/spinlock.h8
-rw-r--r--sys/compat/linuxkpi/common/include/linux/stdarg.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/stddef.h26
-rw-r--r--sys/compat/linuxkpi/common/include/linux/string_helpers.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/swap.h14
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sysfs.h191
-rw-r--r--sys/compat/linuxkpi/common/include/linux/time.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/timer.h27
-rw-r--r--sys/compat/linuxkpi/common/include/linux/topology.h35
-rw-r--r--sys/compat/linuxkpi/common/include/linux/types.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/wait.h26
-rw-r--r--sys/compat/linuxkpi/common/include/linux/workqueue.h10
-rw-r--r--sys/compat/linuxkpi/common/include/net/cfg80211.h380
-rw-r--r--sys/compat/linuxkpi/common/include/net/mac80211.h241
-rw-r--r--sys/compat/linuxkpi/common/include/stdarg.h2
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c2204
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.h65
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211_macops.c23
-rw-r--r--sys/compat/linuxkpi/common/src/linux_acpi.c39
-rw-r--r--sys/compat/linuxkpi/common/src/linux_compat.c139
-rw-r--r--sys/compat/linuxkpi/common/src/linux_devres.c26
-rw-r--r--sys/compat/linuxkpi/common/src/linux_folio.c58
-rw-r--r--sys/compat/linuxkpi/common/src/linux_i2c.c5
-rw-r--r--sys/compat/linuxkpi/common/src/linux_i2cbb.c5
-rw-r--r--sys/compat/linuxkpi/common/src/linux_idr.c3
-rw-r--r--sys/compat/linuxkpi/common/src/linux_kobject.c4
-rw-r--r--sys/compat/linuxkpi/common/src/linux_netdev.c16
-rw-r--r--sys/compat/linuxkpi/common/src/linux_page.c49
-rw-r--r--sys/compat/linuxkpi/common/src/linux_pci.c236
-rw-r--r--sys/compat/linuxkpi/common/src/linux_schedule.c126
-rw-r--r--sys/compat/linuxkpi/common/src/linux_seq_file.c9
-rw-r--r--sys/compat/linuxkpi/common/src/linux_skbuff.c100
-rw-r--r--sys/compat/linuxkpi/common/src/linux_slab.c83
-rw-r--r--sys/compat/linuxkpi/common/src/linux_work.c9
-rw-r--r--sys/compat/linuxkpi/dummy/include/kunit/skbuff.h0
-rw-r--r--sys/compat/linuxkpi/dummy/include/kunit/test-bug.h0
-rw-r--r--sys/compat/linuxkpi/dummy/include/kunit/test.h0
-rw-r--r--sys/compat/linuxkpi/dummy/include/linux/regmap.h0
-rw-r--r--sys/compat/linuxkpi/dummy/include/linux/unaligned.h0
-rw-r--r--sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h0
92 files changed, 4323 insertions, 1405 deletions
diff --git a/sys/compat/linuxkpi/common/include/acpi/acpi.h b/sys/compat/linuxkpi/common/include/acpi/acpi.h
index e0218bdde12e..9bb435591daa 100644
--- a/sys/compat/linuxkpi/common/include/acpi/acpi.h
+++ b/sys/compat/linuxkpi/common/include/acpi/acpi.h
@@ -3,6 +3,10 @@
*
* Copyright (c) 2017 Mark Johnston <markj@FreeBSD.org>
* Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Björn Zeeb
+ * 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
@@ -31,6 +35,13 @@
#define _LINUXKPI_ACPI_ACPI_H_
/*
+ * LINUXKPI_WANT_LINUX_ACPI is a temporary workaround to allow drm-kmod
+ * to update all needed branches without breaking builds.
+ * Once that happened and checks are implemented based on __FreeBSD_version
+ * we will remove these conditions again.
+ */
+
+/*
* FreeBSD import of ACPICA has a typedef for BOOLEAN which conflicts with
* amdgpu driver. Workaround it on preprocessor level.
*/
@@ -46,8 +57,8 @@ typedef int64_t INT64;
#include <contrib/dev/acpica/include/acpi.h>
#undef BOOLEAN
+typedef ACPI_IO_ADDRESS acpi_io_address;
typedef ACPI_HANDLE acpi_handle;
-typedef ACPI_OBJECT acpi_object;
typedef ACPI_OBJECT_HANDLER acpi_object_handler;
typedef ACPI_OBJECT_TYPE acpi_object_type;
typedef ACPI_STATUS acpi_status;
@@ -55,12 +66,62 @@ typedef ACPI_STRING acpi_string;
typedef ACPI_SIZE acpi_size;
typedef ACPI_WALK_CALLBACK acpi_walk_callback;
+union linuxkpi_acpi_object {
+ acpi_object_type type;
+ struct {
+ acpi_object_type type;
+ UINT64 value;
+ } integer;
+ struct {
+ acpi_object_type type;
+ UINT32 length;
+ char *pointer;
+ } string;
+ struct {
+ acpi_object_type type;
+ UINT32 length;
+ UINT8 *pointer;
+ } buffer;
+ struct {
+ acpi_object_type type;
+ UINT32 count;
+ union linuxkpi_acpi_object *elements;
+ } package;
+ struct {
+ acpi_object_type type;
+ acpi_object_type actual_type;
+ acpi_handle handle;
+ } reference;
+ struct {
+ acpi_object_type type;
+ UINT32 proc_id;
+ acpi_io_address pblk_address;
+ UINT32 pblk_length;
+ } processor;
+ struct {
+ acpi_object_type type;
+ UINT32 system_level;
+ UINT32 resource_order;
+ } power_resource;
+};
+
+#ifdef LINUXKPI_WANT_LINUX_ACPI
+struct linuxkpi_acpi_buffer {
+ acpi_size length; /* Length in bytes of the buffer */
+ void *pointer; /* pointer to buffer */
+};
+
+typedef struct linuxkpi_acpi_buffer lkpi_acpi_buffer_t;
+#else
+typedef ACPI_BUFFER lkpi_acpi_buffer_t;
+#endif
+
static inline ACPI_STATUS
acpi_evaluate_object(ACPI_HANDLE Object, ACPI_STRING Pathname,
- ACPI_OBJECT_LIST *ParameterObjects, ACPI_BUFFER *ReturnObjectBuffer)
+ ACPI_OBJECT_LIST *ParameterObjects, lkpi_acpi_buffer_t *ReturnObjectBuffer)
{
return (AcpiEvaluateObject(
- Object, Pathname, ParameterObjects, ReturnObjectBuffer));
+ Object, Pathname, ParameterObjects, (ACPI_BUFFER *)ReturnObjectBuffer));
}
static inline const char *
@@ -70,7 +131,7 @@ acpi_format_exception(ACPI_STATUS Exception)
}
static inline ACPI_STATUS
-acpi_get_handle(ACPI_HANDLE Parent, ACPI_STRING Pathname,
+acpi_get_handle(ACPI_HANDLE Parent, const char *Pathname,
ACPI_HANDLE *RetHandle)
{
return (AcpiGetHandle(Parent, Pathname, RetHandle));
@@ -83,9 +144,9 @@ acpi_get_data(ACPI_HANDLE ObjHandle, ACPI_OBJECT_HANDLER Handler, void **Data)
}
static inline ACPI_STATUS
-acpi_get_name(ACPI_HANDLE Object, UINT32 NameType, ACPI_BUFFER *RetPathPtr)
+acpi_get_name(ACPI_HANDLE Object, UINT32 NameType, lkpi_acpi_buffer_t *RetPathPtr)
{
- return (AcpiGetName(Object, NameType, RetPathPtr));
+ return (AcpiGetName(Object, NameType, (ACPI_BUFFER *)RetPathPtr));
}
static inline ACPI_STATUS
@@ -101,4 +162,9 @@ acpi_put_table(ACPI_TABLE_HEADER *Table)
AcpiPutTable(Table);
}
+#ifdef LINUXKPI_WANT_LINUX_ACPI
+#define acpi_object linuxkpi_acpi_object
+#define acpi_buffer linuxkpi_acpi_buffer
+#endif
+
#endif /* _LINUXKPI_ACPI_ACPI_H_ */
diff --git a/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h b/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h
index 65bcbe7f1bdd..da50d25a63bb 100644
--- a/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h
+++ b/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h
@@ -45,9 +45,9 @@ struct acpi_bus_event {
lkpi_acpi_dev_get_first_match_dev(__VA_ARGS__)
ACPI_HANDLE bsd_acpi_get_handle(device_t bsddev);
-bool acpi_check_dsm(ACPI_HANDLE handle, const char *uuid, int rev,
+bool acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev,
uint64_t funcs);
-ACPI_OBJECT * acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const char *uuid,
+ACPI_OBJECT * acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid,
int rev, int func, ACPI_OBJECT *argv4,
ACPI_OBJECT_TYPE type);
int register_acpi_notifier(struct notifier_block *nb);
@@ -58,4 +58,10 @@ bool lkpi_acpi_dev_present(const char *hid, const char *uid,
struct acpi_device *lkpi_acpi_dev_get_first_match_dev(const char *hid,
const char *uid, int64_t hrv);
+union linuxkpi_acpi_object;
+
+union linuxkpi_acpi_object *
+acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
+ UINT64 rev, UINT64 func, union linuxkpi_acpi_object *arg);
+
#endif /* _LINUXKPI_ACPI_ACPI_BUS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/asm/topology.h b/sys/compat/linuxkpi/common/include/asm/topology.h
new file mode 100644
index 000000000000..f334d3253cfb
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/asm/topology.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2025 The FreeBSD Foundation
+ * Copyright (c) 2025 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * 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_ASM_TOPOLOGY_H_
+#define _LINUXKPI_ASM_TOPOLOGY_H_
+
+#if defined(__i386__) || defined(__amd64__)
+#include <sys/smp.h>
+
+/*
+ * The following functions are defined in `arch/x86/include/asm/topology.h`
+ * and thus are specific to i386 and amd64.
+ */
+
+static inline unsigned int
+topology_num_cores_per_package(void)
+{
+ return (mp_ncores);
+}
+
+static inline unsigned int
+topology_num_threads_per_package(void)
+{
+ return (mp_ncpus);
+}
+#endif
+
+#endif /* _LINUXKPI_ASM_TOPOLOGY_H_ */
diff --git a/sys/compat/linuxkpi/common/include/kunit/static_stub.h b/sys/compat/linuxkpi/common/include/kunit/static_stub.h
new file mode 100644
index 000000000000..9d425d46dbb0
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/kunit/static_stub.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * This software was developed by Björn Zeeb under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_KUNIT_STATIC_STUB_H
+#define _LINUXKPI_KUNIT_STATIC_STUB_H
+
+#define KUNIT_STATIC_STUB_REDIRECT(_fn, ...) do { } while(0)
+
+#endif /* _LINUXKPI_KUNIT_STATIC_STUB_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/acpi_amd_wbrf.h b/sys/compat/linuxkpi/common/include/linux/acpi_amd_wbrf.h
new file mode 100644
index 000000000000..92c2ead41c45
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/acpi_amd_wbrf.h
@@ -0,0 +1,97 @@
+/*-
+ * 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_ACPI_AMD_WBRF_H_
+#define _LINUXKPI_LINUX_ACPI_AMD_WBRF_H_
+
+#include <linux/device.h>
+#include <linux/notifier.h>
+
+#define MAX_NUM_OF_WBRF_RANGES 11
+
+#define WBRF_RECORD_ADD 0x0
+#define WBRF_RECORD_REMOVE 0x1
+
+struct freq_band_range {
+ uint64_t start;
+ uint64_t end;
+};
+
+struct wbrf_ranges_in_out {
+ uint64_t num_of_ranges;
+ struct freq_band_range band_list[MAX_NUM_OF_WBRF_RANGES];
+};
+
+enum wbrf_notifier_actions {
+ WBRF_CHANGED,
+};
+
+/*
+ * The following functions currently have dummy implementations that, on Linux,
+ * are used when CONFIG_AMD_WBRF is not set at compile time.
+ */
+
+static inline bool
+acpi_amd_wbrf_supported_consumer(struct device *dev)
+{
+ return (false);
+}
+
+static inline int
+acpi_amd_wbrf_add_remove(struct device *dev, uint8_t action,
+ struct wbrf_ranges_in_out *in)
+{
+ return (-ENODEV);
+}
+
+static inline bool
+acpi_amd_wbrf_supported_producer(struct device *dev)
+{
+ return (false);
+}
+
+static inline int
+amd_wbrf_retrieve_freq_band(struct device *dev, struct wbrf_ranges_in_out *out)
+{
+ return (-ENODEV);
+}
+
+static inline int
+amd_wbrf_register_notifier(struct notifier_block *nb)
+{
+ return (-ENODEV);
+}
+
+static inline int
+amd_wbrf_unregister_notifier(struct notifier_block *nb)
+{
+ return (-ENODEV);
+}
+
+#endif /* _LINUXKPI_LINUX_ACPI_AMD_WBRF_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/bitops.h b/sys/compat/linuxkpi/common/include/linux/bitops.h
index 23aabb546cb2..00dd1f9a1ec0 100644
--- a/sys/compat/linuxkpi/common/include/linux/bitops.h
+++ b/sys/compat/linuxkpi/common/include/linux/bitops.h
@@ -62,10 +62,10 @@
#define hweight64(x) bitcount64(x)
#define hweight_long(x) bitcountl(x)
-#define HWEIGHT8(x) (bitcount8((uint8_t)(x)) + 1)
-#define HWEIGHT16(x) (bitcount16(x) + 1)
-#define HWEIGHT32(x) (bitcount32(x) + 1)
-#define HWEIGHT64(x) (bitcount64(x) + 1)
+#define HWEIGHT8(x) (__builtin_popcountg((uint8_t)(x)))
+#define HWEIGHT16(x) (__builtin_popcountg((uint16_t)(x)))
+#define HWEIGHT32(x) (__builtin_popcountg((uint32_t)(x)))
+#define HWEIGHT64(x) (__builtin_popcountg((uint64_t)(x)))
static inline int
__ffs(int mask)
diff --git a/sys/compat/linuxkpi/common/include/linux/cleanup.h b/sys/compat/linuxkpi/common/include/linux/cleanup.h
index 01f234f0cbe7..5bb146f082ed 100644
--- a/sys/compat/linuxkpi/common/include/linux/cleanup.h
+++ b/sys/compat/linuxkpi/common/include/linux/cleanup.h
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2024 The FreeBSD Foundation
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -43,4 +43,51 @@
guard_ ## _n ## _t guard_ ## _n ## _ ## __COUNTER__ \
__cleanup(guard_ ## _n ## _destroy) = guard_ ## _n ## _create
+#define DEFINE_FREE(_n, _t, _f) \
+ static inline void \
+ __free_ ## _n(void *p) \
+ { \
+ _t _T; \
+ \
+ _T = *(_t *)p; \
+ _f; \
+ }
+
+#define __free(_n) __cleanup(__free_##_n)
+
+/*
+ * Given this is a _0 version it should likely be broken up into parts.
+ * But we have no idead what a _1, _2, ... version would do different
+ * until we see a call.
+ * This is used for a not-real-type (rcu). We use a bool to "simulate"
+ * the lock held. Also _T still special, may not always be used, so tag
+ * with __unused (or better the LinuxKPI __maybe_unused).
+ */
+#define DEFINE_LOCK_GUARD_0(_n, _lock, _unlock, ...) \
+ \
+ typedef struct { \
+ bool lock; \
+ __VA_ARGS__; \
+ } guard_ ## _n ## _t; \
+ \
+ static inline void \
+ guard_ ## _n ## _destroy(guard_ ## _n ## _t *_T) \
+ { \
+ if (_T->lock) { \
+ _unlock; \
+ } \
+ } \
+ \
+ static inline guard_ ## _n ## _t \
+ guard_ ## _n ## _create(void) \
+ { \
+ guard_ ## _n ## _t _tmp; \
+ guard_ ## _n ## _t *_T __maybe_unused; \
+ \
+ _tmp.lock = true; \
+ _T = &_tmp; \
+ _lock; \
+ return (_tmp); \
+ }
+
#endif /* _LINUXKPI_LINUX_CLEANUP_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/compiler.h b/sys/compat/linuxkpi/common/include/linux/compiler.h
index fb5ad3bf4fe4..948396144ad6 100644
--- a/sys/compat/linuxkpi/common/include/linux/compiler.h
+++ b/sys/compat/linuxkpi/common/include/linux/compiler.h
@@ -130,4 +130,10 @@
#define is_signed_type(t) ((t)-1 < (t)1)
#define is_unsigned_type(t) ((t)-1 > (t)1)
+#if __has_builtin(__builtin_dynamic_object_size)
+#define __struct_size(_s) __builtin_dynamic_object_size(_s, 0)
+#else
+#define __struct_size(_s) __builtin_object_size(_s, 0)
+#endif
+
#endif /* _LINUXKPI_LINUX_COMPILER_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/completion.h b/sys/compat/linuxkpi/common/include/linux/completion.h
index 26e41a51c10b..9f8bebb4cf82 100644
--- a/sys/compat/linuxkpi/common/include/linux/completion.h
+++ b/sys/compat/linuxkpi/common/include/linux/completion.h
@@ -60,7 +60,8 @@ struct completion {
extern void linux_complete_common(struct completion *, int);
extern int linux_wait_for_common(struct completion *, int);
-extern int linux_wait_for_timeout_common(struct completion *, int, int);
+extern unsigned long linux_wait_for_timeout_common(struct completion *,
+ unsigned long, int);
extern int linux_try_wait_for_completion(struct completion *);
extern int linux_completion_done(struct completion *);
diff --git a/sys/compat/linuxkpi/common/include/linux/container_of.h b/sys/compat/linuxkpi/common/include/linux/container_of.h
index 449507fcf9c1..7210d531b055 100644
--- a/sys/compat/linuxkpi/common/include/linux/container_of.h
+++ b/sys/compat/linuxkpi/common/include/linux/container_of.h
@@ -41,6 +41,14 @@
(type *)((uintptr_t)__p - offsetof(type, member)); \
})
+#define container_of_const(ptr, type, member) \
+ _Generic(ptr, \
+ const typeof(*(ptr)) *: \
+ (const type *)container_of(ptr, type, member), \
+ default: \
+ container_of(ptr, type, member) \
+ )
+
#define typeof_member(type, member) __typeof(((type *)0)->member)
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/debugfs.h b/sys/compat/linuxkpi/common/include/linux/debugfs.h
index 54145b61503e..4d146e085a7b 100644
--- a/sys/compat/linuxkpi/common/include/linux/debugfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/debugfs.h
@@ -115,6 +115,8 @@ void debugfs_create_ulong(const char *name, umode_t mode, struct dentry *parent,
unsigned long *value);
void debugfs_create_atomic_t(const char *name, umode_t mode, struct dentry *parent,
atomic_t *value);
+void debugfs_create_str(const char *name, umode_t mode, struct dentry *parent,
+ char **value);
struct dentry *debugfs_create_blob(const char *name, umode_t mode,
struct dentry *parent, struct debugfs_blob_wrapper *value);
diff --git a/sys/compat/linuxkpi/common/include/linux/devcoredump.h b/sys/compat/linuxkpi/common/include/linux/devcoredump.h
index b58c490615ad..5fa06c6595a8 100644
--- a/sys/compat/linuxkpi/common/include/linux/devcoredump.h
+++ b/sys/compat/linuxkpi/common/include/linux/devcoredump.h
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2020 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -71,4 +71,11 @@ dev_coredumpsg(struct device *dev __unused, struct scatterlist *table,
_lkpi_dev_coredumpsg_free(table);
}
+static inline void
+_devcd_free_sgtable(struct scatterlist *table)
+{
+ /* UNIMPLEMENTED */
+ _lkpi_dev_coredumpsg_free(table);
+}
+
#endif /* _LINUXKPI_LINUX_DEVCOREDUMP_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/device.h b/sys/compat/linuxkpi/common/include/linux/device.h
index a5f6874a07f6..7dd6340746d2 100644
--- a/sys/compat/linuxkpi/common/include/linux/device.h
+++ b/sys/compat/linuxkpi/common/include/linux/device.h
@@ -4,7 +4,7 @@
* Copyright (c) 2010 Panasas, Inc.
* Copyright (c) 2013-2016 Mellanox Technologies, Ltd.
* All rights reserved.
- * Copyright (c) 2021-2022 The FreeBSD Foundation
+ * Copyright (c) 2021-2025 The FreeBSD Foundation
*
* Portions of this software were developed by Björn Zeeb
* under sponsorship from the FreeBSD Foundation.
@@ -90,6 +90,8 @@ struct dev_pm_ops {
struct device_driver {
const char *name;
const struct dev_pm_ops *pm;
+
+ void (*shutdown) (struct device *);
};
struct device_type {
@@ -282,7 +284,8 @@ int lkpi_devres_destroy(struct device *, void(*release)(struct device *, void *)
void lkpi_devres_release_free_list(struct device *);
void lkpi_devres_unlink(struct device *, void *);
void lkpi_devm_kmalloc_release(struct device *, void *);
-#define devm_kfree(_d, _p) lkpi_devm_kmalloc_release(_d, _p)
+void lkpi_devm_kfree(struct device *, const void *);
+#define devm_kfree(_d, _p) lkpi_devm_kfree(_d, _p)
static inline const char *
dev_driver_string(const struct device *dev)
diff --git a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
index 84f0361de765..2d8e1196d3d3 100644
--- a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
+++ b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
@@ -96,13 +96,17 @@ 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);
-dma_addr_t linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len);
-void linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t size);
+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);
+void linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t size); /* backward compat */
+void lkpi_dma_unmap(struct device *, dma_addr_t, size_t,
+ enum dma_data_direction, unsigned long);
int linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
- int nents, enum dma_data_direction dir __unused,
+ int nents, enum dma_data_direction direction,
unsigned long attrs __unused);
void linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
- int nents __unused, enum dma_data_direction dir __unused,
+ int nents __unused, enum dma_data_direction direction,
unsigned long attrs __unused);
void linuxkpi_dma_sync(struct device *, dma_addr_t, size_t, bus_dmasync_op_t);
@@ -173,16 +177,17 @@ dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_addr)
{
- linux_dma_unmap(dev, dma_addr, size);
+ lkpi_dma_unmap(dev, dma_addr, size, DMA_BIDIRECTIONAL, 0);
kmem_free(cpu_addr, size);
}
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 dir, unsigned long attrs)
+ size_t size, enum dma_data_direction direction, unsigned long attrs)
{
- return (linux_dma_map_phys(dev, page_to_phys(page) + offset, size));
+ return (lkpi_dma_map_phys(dev, page_to_phys(page) + offset, size,
+ direction, attrs));
}
/* linux_dma_(un)map_sg_attrs does not support attrs yet */
@@ -197,7 +202,8 @@ dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction direction)
{
- return (linux_dma_map_phys(dev, page_to_phys(page) + offset, size));
+ return (lkpi_dma_map_phys(dev, page_to_phys(page) + offset, size,
+ direction, 0));
}
static inline void
@@ -205,7 +211,21 @@ dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
enum dma_data_direction direction)
{
- linux_dma_unmap(dev, dma_address, size);
+ lkpi_dma_unmap(dev, dma_address, size, direction, 0);
+}
+
+static inline dma_addr_t
+dma_map_resource(struct device *dev, phys_addr_t paddr, size_t size,
+ enum dma_data_direction direction, unsigned long attrs)
+{
+ return (lkpi_dma_map_phys(dev, paddr, size, direction, attrs));
+}
+
+static inline void
+dma_unmap_resource(struct device *dev, dma_addr_t dma, size_t size,
+ enum dma_data_direction direction, unsigned long attrs)
+{
+ lkpi_dma_unmap(dev, dma, size, direction, attrs);
}
static inline void
@@ -263,28 +283,33 @@ dma_sync_single_for_device(struct device *dev, dma_addr_t dma,
linuxkpi_dma_sync(dev, dma, size, op);
}
+/* (20250329) These four seem to be unused code. */
static inline void
dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
enum dma_data_direction direction)
{
+ pr_debug("%s:%d: TODO dir %d\n", __func__, __LINE__, direction);
}
static inline void
dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems,
enum dma_data_direction direction)
{
+ pr_debug("%s:%d: TODO dir %d\n", __func__, __LINE__, direction);
}
static inline void
dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle,
- unsigned long offset, size_t size, int direction)
+ unsigned long offset, size_t size, enum dma_data_direction direction)
{
+ pr_debug("%s:%d: TODO dir %d\n", __func__, __LINE__, direction);
}
static inline void
dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle,
- unsigned long offset, size_t size, int direction)
+ unsigned long offset, size_t size, enum dma_data_direction direction)
{
+ pr_debug("%s:%d: TODO dir %d\n", __func__, __LINE__, direction);
}
#define DMA_MAPPING_ERROR (~(dma_addr_t)0)
@@ -306,24 +331,17 @@ static inline unsigned int dma_set_max_seg_size(struct device *dev,
static inline dma_addr_t
_dma_map_single_attrs(struct device *dev, void *ptr, size_t size,
- enum dma_data_direction direction, unsigned long attrs __unused)
+ enum dma_data_direction direction, unsigned long attrs)
{
- dma_addr_t dma;
-
- dma = linux_dma_map_phys(dev, vtophys(ptr), size);
- if (!dma_mapping_error(dev, dma))
- dma_sync_single_for_device(dev, dma, size, direction);
-
- return (dma);
+ return (lkpi_dma_map_phys(dev, vtophys(ptr), size,
+ direction, attrs));
}
static inline void
_dma_unmap_single_attrs(struct device *dev, dma_addr_t dma, size_t size,
- enum dma_data_direction direction, unsigned long attrs __unused)
+ enum dma_data_direction direction, unsigned long attrs)
{
-
- dma_sync_single_for_cpu(dev, dma, size, direction);
- linux_dma_unmap(dev, dma, size);
+ lkpi_dma_unmap(dev, dma, size, direction, attrs);
}
static inline size_t
diff --git a/sys/compat/linuxkpi/common/include/linux/etherdevice.h b/sys/compat/linuxkpi/common/include/linux/etherdevice.h
index 5d3df744ae0e..1f2d6cf22d7e 100644
--- a/sys/compat/linuxkpi/common/include/linux/etherdevice.h
+++ b/sys/compat/linuxkpi/common/include/linux/etherdevice.h
@@ -58,9 +58,15 @@ is_zero_ether_addr(const u8 * addr)
}
static inline bool
+is_unicast_ether_addr(const u8 * addr)
+{
+ return ((addr[0] & 0x01) == 0x00);
+}
+
+static inline bool
is_multicast_ether_addr(const u8 * addr)
{
- return (0x01 & addr[0]);
+ return ((addr[0] & 0x01) == 0x01);
}
static inline bool
diff --git a/sys/compat/linuxkpi/common/include/linux/fs.h b/sys/compat/linuxkpi/common/include/linux/fs.h
index 76a81e676744..f1568ad6282d 100644
--- a/sys/compat/linuxkpi/common/include/linux/fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/fs.h
@@ -150,6 +150,11 @@ struct file_operations {
* an illegal seek error
*/
off_t (*llseek)(struct linux_file *, off_t, int);
+/*
+ * Not supported in FreeBSD. That's ok, we never call it and it allows some
+ * drivers like DRM drivers to compile without changes.
+ */
+ void (*show_fdinfo)(struct seq_file *, struct file *);
#if 0
/* We do not support these methods. Don't permit them to compile. */
loff_t (*llseek)(struct file *, loff_t, int);
diff --git a/sys/compat/linuxkpi/common/include/linux/gfp.h b/sys/compat/linuxkpi/common/include/linux/gfp.h
index 35dbe3e2a436..7a32e7862338 100644
--- a/sys/compat/linuxkpi/common/include/linux/gfp.h
+++ b/sys/compat/linuxkpi/common/include/linux/gfp.h
@@ -34,6 +34,7 @@
#include <sys/malloc.h>
#include <linux/page.h>
+#include <linux/topology.h>
#include <vm/vm_param.h>
#include <vm/vm_object.h>
@@ -134,6 +135,8 @@ dev_alloc_pages(unsigned int order)
return (linux_alloc_pages(GFP_ATOMIC, order));
}
+struct folio *folio_alloc(gfp_t gfp, unsigned int order);
+
/*
* Page management for mapped pages:
*/
diff --git a/sys/compat/linuxkpi/common/include/linux/highmem.h b/sys/compat/linuxkpi/common/include/linux/highmem.h
index b8874481f9c6..58a9cdcdf60f 100644
--- a/sys/compat/linuxkpi/common/include/linux/highmem.h
+++ b/sys/compat/linuxkpi/common/include/linux/highmem.h
@@ -79,9 +79,7 @@ kmap_atomic_prot(struct page *page, pgprot_t prot)
vm_memattr_t attr = pgprot2cachemode(prot);
if (attr != VM_MEMATTR_DEFAULT) {
- vm_page_lock(page);
page->flags |= PG_FICTITIOUS;
- vm_page_unlock(page);
pmap_page_set_memattr(page, attr);
}
return (kmap(page));
@@ -139,4 +137,34 @@ kunmap_local(void *addr)
kunmap_atomic(addr);
}
+static inline void
+memcpy_from_page(char *to, struct page *page, size_t offset, size_t len)
+{
+ char *from;
+
+ KASSERT(offset + len <= PAGE_SIZE,
+ ("%s: memcpy from page %p to address %p: "
+ "offset+len (%zu+%zu) would go beyond page end",
+ __func__, page, to, offset, len));
+
+ from = kmap_local_page(page);
+ memcpy(to, from + offset, len);
+ kunmap_local(from);
+}
+
+static inline void
+memcpy_to_page(struct page *page, size_t offset, const char *from, size_t len)
+{
+ char *to;
+
+ KASSERT(offset + len <= PAGE_SIZE,
+ ("%s: memcpy from address %p to page %p: "
+ "offset+len (%zu+%zu) would go beyond page end",
+ __func__, from, page, offset, len));
+
+ to = kmap_local_page(page);
+ memcpy(to + offset, from, len);
+ kunmap_local(to);
+}
+
#endif /* _LINUXKPI_LINUX_HIGHMEM_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/idr.h b/sys/compat/linuxkpi/common/include/linux/idr.h
index 535d8ce07fb4..06850c94a5e9 100644
--- a/sys/compat/linuxkpi/common/include/linux/idr.h
+++ b/sys/compat/linuxkpi/common/include/linux/idr.h
@@ -147,6 +147,13 @@ ida_alloc_max(struct ida *ida, unsigned int max, gfp_t gfp)
return (ida_simple_get(ida, 0, max, gfp));
}
+static inline int
+ida_alloc_range(struct ida *ida, unsigned int min, unsigned int max, gfp_t gfp)
+{
+
+ return (ida_simple_get(ida, min, max, gfp));
+}
+
static inline int ida_alloc(struct ida *ida, gfp_t gfp)
{
return (ida_alloc_max(ida, ~0u, gfp));
diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
index efac2a26e27e..17041bb03ce8 100644
--- a/sys/compat/linuxkpi/common/include/linux/ieee80211.h
+++ b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
@@ -35,6 +35,7 @@
#include <asm/unaligned.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
+#include <linux/bitfield.h>
#include <linux/if_ether.h>
/* linux_80211.c */
@@ -121,7 +122,20 @@ enum ieee80211_rate_control_changed_flags {
/* 802.11-2016, 9.4.2.158.3 Supported VHT-MCS and NSS Set field. */
#define IEEE80211_VHT_EXT_NSS_BW_CAPABLE (1 << 13) /* part of tx_highest */
-#define IEEE80211_VHT_MAX_AMPDU_1024K 7 /* 9.4.2.56.3 A-MPDU Parameters field, Table 9-163 */
+/*
+ * 802.11-2020, 9.4.2.157.2 VHT Capabilities Information field,
+ * Table 9-271-Subfields of the VHT Capabilities Information field (continued).
+ */
+enum ieee80211_vht_max_ampdu_len_exp {
+ IEEE80211_VHT_MAX_AMPDU_8K = 0,
+ IEEE80211_VHT_MAX_AMPDU_16K = 1,
+ IEEE80211_VHT_MAX_AMPDU_32K = 2,
+ IEEE80211_VHT_MAX_AMPDU_64K = 3,
+ IEEE80211_VHT_MAX_AMPDU_128K = 4,
+ IEEE80211_VHT_MAX_AMPDU_256K = 5,
+ IEEE80211_VHT_MAX_AMPDU_512K = 6,
+ IEEE80211_VHT_MAX_AMPDU_1024K = 7,
+};
#define IEEE80211_WEP_IV_LEN 3 /* net80211: IEEE80211_WEP_IVLEN */
#define IEEE80211_WEP_ICV_LEN 4
@@ -133,9 +147,9 @@ enum ieee80211_rate_control_changed_flags {
enum wlan_ht_cap_sm_ps {
WLAN_HT_CAP_SM_PS_STATIC = 0,
- WLAN_HT_CAP_SM_PS_DYNAMIC,
- WLAN_HT_CAP_SM_PS_INVALID,
- WLAN_HT_CAP_SM_PS_DISABLED,
+ WLAN_HT_CAP_SM_PS_DYNAMIC = 1,
+ WLAN_HT_CAP_SM_PS_INVALID = 2,
+ WLAN_HT_CAP_SM_PS_DISABLED = 3
};
#define WLAN_MAX_KEY_LEN 32
@@ -295,6 +309,7 @@ enum ieee80211_ac_numbers {
#define IEEE80211_HT_MCS_MASK_LEN 10
#define IEEE80211_MLD_MAX_NUM_LINKS 15
+#define IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS 0xf
#define IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP 0x0060
#define IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME 1
@@ -303,7 +318,7 @@ struct ieee80211_mcs_info {
uint16_t rx_highest;
uint8_t tx_params;
uint8_t __reserved[3];
-};
+} __packed;
/* 802.11-2020, 9.4.2.55.1 HT Capabilities element structure */
struct ieee80211_ht_cap {
@@ -313,7 +328,7 @@ struct ieee80211_ht_cap {
uint16_t extended_ht_cap_info;
uint32_t tx_BF_cap_info;
uint8_t antenna_selection_info;
-};
+} __packed;
#define IEEE80211_HT_MAX_AMPDU_FACTOR 13
#define IEEE80211_HE_HT_MAX_AMPDU_FACTOR 16
@@ -349,6 +364,7 @@ enum ieee80211_chanctx_change_flags {
IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(3),
IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(4),
IEEE80211_CHANCTX_CHANGE_PUNCTURING = BIT(5),
+ IEEE80211_CHANCTX_CHANGE_MIN_DEF = BIT(6),
};
enum ieee80211_frame_release_type {
@@ -392,6 +408,14 @@ enum ieee80211_sta_state {
IEEE80211_STA_AUTHORIZED = 4, /* 802.1x */
};
+enum ieee80211_sta_rx_bandwidth {
+ IEEE80211_STA_RX_BW_20 = 0,
+ IEEE80211_STA_RX_BW_40,
+ IEEE80211_STA_RX_BW_80,
+ IEEE80211_STA_RX_BW_160,
+ IEEE80211_STA_RX_BW_320,
+};
+
enum ieee80211_tx_info_flags {
/* XXX TODO .. right shift numbers - not sure where that came from? */
IEEE80211_TX_CTL_AMPDU = BIT(0),
@@ -426,6 +450,7 @@ enum ieee80211_tx_control_flags {
IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1),
IEEE80211_TX_CTRL_RATE_INJECT = BIT(2),
+ IEEE80211_TX_CTRL_DONT_USE_RATE_MASK = BIT(3),
IEEE80211_TX_CTRL_MLO_LINK = 0xF0000000, /* This is IEEE80211_LINK_UNSPECIFIED on the high bits. */
};
@@ -507,24 +532,24 @@ struct ieee80211_mgmt {
uint16_t beacon_int;
uint16_t capab_info;
uint8_t variable[0];
- } beacon;
+ } __packed beacon;
/* 9.3.3.5 Association Request frame format */
struct {
uint16_t capab_info;
uint16_t listen_interval;
uint8_t variable[0];
- } assoc_req;
+ } __packed assoc_req;
/* 9.3.3.10 Probe Request frame format */
struct {
uint8_t variable[0];
- } probe_req;
+ } __packed probe_req;
/* 9.3.3.11 Probe Response frame format */
struct {
uint64_t timestamp;
uint16_t beacon_int;
uint16_t capab_info;
uint8_t variable[0];
- } probe_resp;
+ } __packed probe_resp;
/* 9.3.3.14 Action frame format */
struct {
/* 9.4.1.11 Action field */
@@ -540,7 +565,7 @@ struct ieee80211_mgmt {
uint8_t tpc_elem_length;
uint8_t tpc_elem_tx_power;
uint8_t tpc_elem_link_margin;
- } tpc_report;
+ } __packed tpc_report;
/* 9.6.8.33 Fine Timing Measurement frame format */
struct {
uint8_t dialog_token;
@@ -550,7 +575,7 @@ struct ieee80211_mgmt {
uint16_t tod_error;
uint16_t toa_error;
uint8_t variable[0];
- } ftm;
+ } __packed ftm;
/* 802.11-2016, 9.6.5.2 ADDBA Request frame format */
struct {
uint8_t action_code;
@@ -560,16 +585,16 @@ struct ieee80211_mgmt {
uint16_t start_seq_num;
/* Optional follows... */
uint8_t variable[0];
- } addba_req;
+ } __packed addba_req;
/* XXX */
struct {
uint8_t dialog_token;
- } wnm_timing_msr;
+ } __packed wnm_timing_msr;
} u;
- } action;
+ } __packed action;
DECLARE_FLEX_ARRAY(uint8_t, body);
} u;
-};
+} __packed __aligned(2);
struct ieee80211_cts { /* net80211::ieee80211_frame_cts */
__le16 frame_control;
@@ -783,6 +808,20 @@ struct ieee80211_bss_load_elem {
uint16_t avail_adm_capa;
};
+struct ieee80211_p2p_noa_desc {
+ uint32_t count; /* uint8_t ? */
+ uint32_t duration;
+ uint32_t interval;
+ uint32_t start_time;
+};
+
+struct ieee80211_p2p_noa_attr {
+ uint8_t index;
+ uint8_t oppps_ctwindow;
+ struct ieee80211_p2p_noa_desc desc[4];
+};
+
+
/* net80211: IEEE80211_IS_CTL() */
static __inline bool
ieee80211_is_ctl(__le16 fc)
@@ -1225,5 +1264,4 @@ ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr)
return (u8 *)hdr + 24;
}
-
#endif /* _LINUXKPI_LINUX_IEEE80211_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/ioport.h b/sys/compat/linuxkpi/common/include/linux/ioport.h
index 444f3ad94602..763af2de7c4f 100644
--- a/sys/compat/linuxkpi/common/include/linux/ioport.h
+++ b/sys/compat/linuxkpi/common/include/linux/ioport.h
@@ -40,6 +40,7 @@
struct resource {
resource_size_t start;
resource_size_t end;
+ const char *name;
};
static inline resource_size_t
diff --git a/sys/compat/linuxkpi/common/include/linux/jiffies.h b/sys/compat/linuxkpi/common/include/linux/jiffies.h
index f099caa1ce18..c2409726e874 100644
--- a/sys/compat/linuxkpi/common/include/linux/jiffies.h
+++ b/sys/compat/linuxkpi/common/include/linux/jiffies.h
@@ -32,21 +32,21 @@
#include <linux/types.h>
#include <linux/time.h>
-#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/limits.h>
+#include <sys/time.h>
-#define jiffies ticks
-#define jiffies_64 ticks
+extern unsigned long jiffies; /* defined in sys/kern/subr_ticks.S */
+#define jiffies_64 jiffies /* XXX-MJ wrong on 32-bit platforms */
#define jiffies_to_msecs(x) ((unsigned int)(((int64_t)(int)(x)) * 1000 / hz))
-#define MAX_JIFFY_OFFSET ((INT_MAX >> 1) - 1)
+#define MAX_JIFFY_OFFSET ((LONG_MAX >> 1) - 1)
-#define time_after(a, b) ((int)((b) - (a)) < 0)
+#define time_after(a, b) ((long)((b) - (a)) < 0)
#define time_after32(a, b) ((int32_t)((uint32_t)(b) - (uint32_t)(a)) < 0)
#define time_before(a, b) time_after(b,a)
#define time_before32(a, b) time_after32(b, a)
-#define time_after_eq(a, b) ((int)((a) - (b)) >= 0)
+#define time_after_eq(a, b) ((long)((a) - (b)) >= 0)
#define time_before_eq(a, b) time_after_eq(b, a)
#define time_in_range(a,b,c) \
(time_after_eq(a,b) && time_before_eq(a,c))
@@ -68,7 +68,7 @@ extern uint64_t lkpi_msec2hz_rem;
extern uint64_t lkpi_msec2hz_div;
extern uint64_t lkpi_msec2hz_max;
-static inline int
+static inline unsigned long
msecs_to_jiffies(uint64_t msec)
{
uint64_t result;
@@ -79,10 +79,10 @@ msecs_to_jiffies(uint64_t msec)
if (result > MAX_JIFFY_OFFSET)
result = MAX_JIFFY_OFFSET;
- return ((int)result);
+ return ((unsigned long)result);
}
-static inline int
+static inline unsigned long
usecs_to_jiffies(uint64_t usec)
{
uint64_t result;
@@ -93,7 +93,7 @@ usecs_to_jiffies(uint64_t usec)
if (result > MAX_JIFFY_OFFSET)
result = MAX_JIFFY_OFFSET;
- return ((int)result);
+ return ((unsigned long)result);
}
static inline uint64_t
@@ -120,34 +120,24 @@ nsecs_to_jiffies(uint64_t nsec)
}
static inline uint64_t
-jiffies_to_nsecs(int j)
+jiffies_to_nsecs(unsigned long j)
{
- return ((1000000000ULL / hz) * (uint64_t)(unsigned int)j);
+ return ((1000000000ULL / hz) * (uint64_t)j);
}
static inline uint64_t
-jiffies_to_usecs(int j)
+jiffies_to_usecs(unsigned long j)
{
- return ((1000000ULL / hz) * (uint64_t)(unsigned int)j);
+ return ((1000000ULL / hz) * (uint64_t)j);
}
static inline uint64_t
get_jiffies_64(void)
{
- return ((uint64_t)(unsigned int)ticks);
-}
-
-static inline int
-linux_timer_jiffies_until(int expires)
-{
- int delta = expires - jiffies;
- /* guard against already expired values */
- if (delta < 1)
- delta = 1;
- return (delta);
+ return ((uint64_t)jiffies);
}
#endif /* _LINUXKPI_LINUX_JIFFIES_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/kmod.h b/sys/compat/linuxkpi/common/include/linux/kmod.h
index b3cbe2ed2e02..8f9f034aabd8 100644
--- a/sys/compat/linuxkpi/common/include/linux/kmod.h
+++ b/sys/compat/linuxkpi/common/include/linux/kmod.h
@@ -33,7 +33,7 @@
#include <sys/syscallsubr.h>
#include <sys/refcount.h>
#include <sys/sbuf.h>
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#include <sys/proc.h>
#define request_module(...) \
diff --git a/sys/compat/linuxkpi/common/include/linux/kobject.h b/sys/compat/linuxkpi/common/include/linux/kobject.h
index 512f47f9e4b4..98f55d1234c4 100644
--- a/sys/compat/linuxkpi/common/include/linux/kobject.h
+++ b/sys/compat/linuxkpi/common/include/linux/kobject.h
@@ -29,7 +29,7 @@
#ifndef _LINUXKPI_LINUX_KOBJECT_H_
#define _LINUXKPI_LINUX_KOBJECT_H_
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#include <linux/kernel.h>
#include <linux/kref.h>
diff --git a/sys/compat/linuxkpi/common/include/linux/ktime.h b/sys/compat/linuxkpi/common/include/linux/ktime.h
index 53c2abd64fc6..6a2f04f3d789 100644
--- a/sys/compat/linuxkpi/common/include/linux/ktime.h
+++ b/sys/compat/linuxkpi/common/include/linux/ktime.h
@@ -232,6 +232,13 @@ ktime_get_boottime_ns(void)
return (ktime_to_ns(ktime_get_boottime()));
}
+static inline uint64_t
+ktime_get_boottime_seconds(void)
+{
+
+ return (ktime_divns(ktime_get_boottime(), NSEC_PER_SEC));
+}
+
static inline ktime_t
ktime_get_real(void)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/leds.h b/sys/compat/linuxkpi/common/include/linux/leds.h
index f7ee7a68dcf5..89f7286f6800 100644
--- a/sys/compat/linuxkpi/common/include/linux/leds.h
+++ b/sys/compat/linuxkpi/common/include/linux/leds.h
@@ -27,7 +27,7 @@
#define _LINUXKPI_LINUX_LEDS_H
enum led_brightness {
- __DUMMY,
+ LED_OFF,
};
struct led_classdev {
@@ -35,6 +35,7 @@ struct led_classdev {
const char *default_trigger;
int (*blink_set)(struct led_classdev *, unsigned long *, unsigned long *);
void (*brightness_set)(struct led_classdev *, enum led_brightness);
+ void (*led_set)(struct led_classdev *, enum led_brightness);
};
#endif /* _LINUXKPI_LINUX_LEDS_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/math.h b/sys/compat/linuxkpi/common/include/linux/math.h
index 5a348a57747b..1d50e011f66d 100644
--- a/sys/compat/linuxkpi/common/include/linux/math.h
+++ b/sys/compat/linuxkpi/common/include/linux/math.h
@@ -56,7 +56,7 @@
__ret; \
})
-#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60600
+#if !defined(LINUXKPI_VERSION) || (LINUXKPI_VERSION >= 60600)
#define abs_diff(x, y) ({ \
__typeof(x) _x = (x); \
__typeof(y) _y = (y); \
diff --git a/sys/compat/linuxkpi/common/include/linux/math64.h b/sys/compat/linuxkpi/common/include/linux/math64.h
index cae5e30b08df..25ca9da1b622 100644
--- a/sys/compat/linuxkpi/common/include/linux/math64.h
+++ b/sys/compat/linuxkpi/common/include/linux/math64.h
@@ -61,6 +61,8 @@ div64_u64(uint64_t dividend, uint64_t divisor)
return (dividend / divisor);
}
+#define div64_ul(x, y) div64_u64((x), (y))
+
static inline uint64_t
div_u64_rem(uint64_t dividend, uint32_t divisor, uint32_t *remainder)
{
@@ -96,6 +98,12 @@ div64_u64_round_up(uint64_t dividend, uint64_t divisor)
return ((dividend + divisor - 1) / divisor);
}
+static inline uint64_t
+roundup_u64(uint64_t x1, uint32_t x2)
+{
+ return (div_u64(x1 + x2 - 1, x2) * x2);
+}
+
#define DIV64_U64_ROUND_UP(...) \
div64_u64_round_up(__VA_ARGS__)
diff --git a/sys/compat/linuxkpi/common/include/linux/mm.h b/sys/compat/linuxkpi/common/include/linux/mm.h
index b4c8bf3c1c30..156b00a0c0f0 100644
--- a/sys/compat/linuxkpi/common/include/linux/mm.h
+++ b/sys/compat/linuxkpi/common/include/linux/mm.h
@@ -161,6 +161,14 @@ virt_to_head_page(const void *p)
return (virt_to_page(p));
}
+static inline struct folio *
+virt_to_folio(const void *p)
+{
+ struct page *page = virt_to_page(p);
+
+ return (page_folio(page));
+}
+
/*
* Compute log2 of the power of two rounded up count of pages
* needed for size bytes.
@@ -184,7 +192,7 @@ get_order(unsigned long size)
*
* NOTE: This function only works for pages allocated by the kernel.
*/
-void *linux_page_address(struct page *);
+void *linux_page_address(const struct page *);
#define page_address(page) linux_page_address(page)
static inline void *
@@ -275,6 +283,38 @@ get_page(struct page *page)
vm_page_wire(page);
}
+static inline void
+put_page(struct page *page)
+{
+ /* `__free_page()` takes care of the refcounting (unwire). */
+ __free_page(page);
+}
+
+static inline void
+folio_get(struct folio *folio)
+{
+ get_page(&folio->page);
+}
+
+static inline void
+folio_put(struct folio *folio)
+{
+ put_page(&folio->page);
+}
+
+/*
+ * Linux uses the following "transparent" union so that `release_pages()`
+ * accepts both a list of `struct page` or a list of `struct folio`. This
+ * relies on the fact that a `struct folio` can be cast to a `struct page`.
+ */
+typedef union {
+ struct page **pages;
+ struct folio **folios;
+} release_pages_arg __attribute__ ((__transparent_union__));
+
+void linux_release_pages(release_pages_arg arg, int nr);
+#define release_pages(arg, nr) linux_release_pages((arg), (nr))
+
extern long
lkpi_get_user_pages(unsigned long start, unsigned long nr_pages,
unsigned int gup_flags, struct page **);
@@ -331,12 +371,6 @@ pin_user_pages_remote(struct task_struct *task, struct mm_struct *mm,
task, mm, start, nr_pages, gup_flags, pages, vmas);
}
-static inline void
-put_page(struct page *page)
-{
- vm_page_unwire(page, PQ_ACTIVE);
-}
-
#define unpin_user_page(page) put_page(page)
#define unpin_user_pages(pages, npages) release_pages(pages, npages)
@@ -372,14 +406,14 @@ vmalloc_to_page(const void *addr)
static inline int
trylock_page(struct page *page)
{
- return (vm_page_trylock(page));
+ return (vm_page_tryxbusy(page));
}
static inline void
unlock_page(struct page *page)
{
- vm_page_unlock(page);
+ vm_page_xunbusy(page);
}
extern int is_vmalloc_addr(const void *addr);
@@ -412,4 +446,34 @@ want_init_on_free(void)
return (false);
}
+static inline unsigned long
+folio_pfn(struct folio *folio)
+{
+ return (page_to_pfn(&folio->page));
+}
+
+static inline long
+folio_nr_pages(struct folio *folio)
+{
+ return (1);
+}
+
+static inline size_t
+folio_size(struct folio *folio)
+{
+ return (PAGE_SIZE);
+}
+
+static inline void
+folio_mark_dirty(struct folio *folio)
+{
+ set_page_dirty(&folio->page);
+}
+
+static inline void *
+folio_address(const struct folio *folio)
+{
+ return (page_address(&folio->page));
+}
+
#endif /* _LINUXKPI_LINUX_MM_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/mm_types.h b/sys/compat/linuxkpi/common/include/linux/mm_types.h
index c08e2511725b..3ea68e97004c 100644
--- a/sys/compat/linuxkpi/common/include/linux/mm_types.h
+++ b/sys/compat/linuxkpi/common/include/linux/mm_types.h
@@ -79,4 +79,15 @@ mmgrab(struct mm_struct *mm)
extern struct mm_struct *linux_get_task_mm(struct task_struct *);
#define get_task_mm(task) linux_get_task_mm(task)
+struct folio {
+ /*
+ * The page member must be at the beginning because `page_folio(p)`
+ * casts from a `struct page` to a `struct folio`.
+ *
+ * `release_pages()` also relies on this to be able to accept either a
+ * list of `struct page` or a list of `struct folio`.
+ */
+ struct page page;
+};
+
#endif /* _LINUXKPI_LINUX_MM_TYPES_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/mutex.h b/sys/compat/linuxkpi/common/include/linux/mutex.h
index 1d85ba20baca..6fb6a7744a89 100644
--- a/sys/compat/linuxkpi/common/include/linux/mutex.h
+++ b/sys/compat/linuxkpi/common/include/linux/mutex.h
@@ -35,6 +35,7 @@
#include <sys/sx.h>
#include <linux/kernel.h>
+#include <linux/cleanup.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <asm/atomic.h>
diff --git a/sys/compat/linuxkpi/common/include/linux/netdev_features.h b/sys/compat/linuxkpi/common/include/linux/netdev_features.h
index 06e88d107708..fae82776b071 100644
--- a/sys/compat/linuxkpi/common/include/linux/netdev_features.h
+++ b/sys/compat/linuxkpi/common/include/linux/netdev_features.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2021 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
*
* Portions of this software were developed by Björn Zeeb
* under sponsorship from the FreeBSD Foundation.
@@ -33,15 +33,20 @@
typedef uint32_t netdev_features_t;
-#define NETIF_F_HIGHDMA BIT(0)
-#define NETIF_F_SG BIT(1)
-#define NETIF_F_IP_CSUM BIT(2)
-#define NETIF_F_IPV6_CSUM BIT(3)
-#define NETIF_F_TSO BIT(4)
-#define NETIF_F_TSO6 BIT(5)
-#define NETIF_F_RXCSUM BIT(6)
-#define NETIF_F_HW_CSUM BIT(7)
+#define NETIF_F_HIGHDMA BIT(0) /* Can DMA to high memory. */
+#define NETIF_F_SG BIT(1) /* Can do scatter/gather I/O. */
+#define NETIF_F_IP_CSUM BIT(2) /* Can csum TCP/UDP on IPv4. */
+#define NETIF_F_IPV6_CSUM BIT(3) /* Can csum TCP/UDP on IPv6. */
+#define NETIF_F_TSO BIT(4) /* Can do TCP over IPv4 segmentation. */
+#define NETIF_F_TSO6 BIT(5) /* Can do TCP over IPv6 segmentation. */
+#define NETIF_F_RXCSUM BIT(6) /* Can do receive csum offload. */
+#define NETIF_F_HW_CSUM BIT(7) /* Can csum packets (which?). */
+#define NETIF_F_HW_TC BIT(8) /* Can offload TC. */
-#define NETIF_F_CSUM_MASK (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)
+#define NETIF_F_CSUM_MASK (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)
+
+#define NETIF_F_BITS \
+ "\20\1HIGHDMA\2SG\3IP_CSUM\4IPV6_CSUM\5TSO\6TSO6\7RXCSUM" \
+ "\10HW_CSUM\11HW_TC"
#endif /* _LINUXKPI_LINUX_NETDEV_FEATURES_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/netdevice.h b/sys/compat/linuxkpi/common/include/linux/netdevice.h
index cd7d23077a62..3b808a4a1749 100644
--- a/sys/compat/linuxkpi/common/include/linux/netdevice.h
+++ b/sys/compat/linuxkpi/common/include/linux/netdevice.h
@@ -4,7 +4,7 @@
* Copyright (c) 2010 Panasas, Inc.
* Copyright (c) 2013-2019 Mellanox Technologies, Ltd.
* All rights reserved.
- * Copyright (c) 2020-2021 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
* Copyright (c) 2020-2022 Bjoern A. Zeeb
*
* Portions of this software were developed by Björn Zeeb
@@ -302,6 +302,13 @@ netdev_rss_key_fill(uint32_t *buf, size_t len)
get_random_bytes(buf, len);
}
+static inline void
+__hw_addr_init(struct netdev_hw_addr_list *list)
+{
+ list->count = 0;
+ INIT_LIST_HEAD(&list->addr_list);
+}
+
static inline int
netdev_hw_addr_list_count(struct netdev_hw_addr_list *list)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/nl80211.h b/sys/compat/linuxkpi/common/include/linux/nl80211.h
index b2a33a28b3a7..f3979d3a2abc 100644
--- a/sys/compat/linuxkpi/common/include/linux/nl80211.h
+++ b/sys/compat/linuxkpi/common/include/linux/nl80211.h
@@ -190,8 +190,6 @@ enum nl80211_tdls_operation {
NL80211_TDLS_ENABLE_LINK,
NL80211_TDLS_DISABLE_LINK,
NL80211_TDLS_DISCOVERY_REQ,
- NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
- NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
};
enum nl80211_cqm_rssi_threshold_event {
@@ -436,6 +434,9 @@ enum nl80211_hidden_ssid {
NL80211_HIDDEN_SSID_NOT_IN_USE,
};
+#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16
+#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24
+
#define NL80211_KCK_LEN 16
#define NL80211_KCK_EXT_LEN 24
#define NL80211_KEK_LEN 16
diff --git a/sys/compat/linuxkpi/common/include/linux/overflow.h b/sys/compat/linuxkpi/common/include/linux/overflow.h
index 9ba9b9500f11..e811037b8ecc 100644
--- a/sys/compat/linuxkpi/common/include/linux/overflow.h
+++ b/sys/compat/linuxkpi/common/include/linux/overflow.h
@@ -33,8 +33,10 @@
* credit to Christian Biere.
*/
#define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type)))
-#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
-#define type_min(T) ((T)((T)-type_max(T)-(T)1))
+#define __type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
+#define type_max(t) __type_max(typeof(t))
+#define __type_min(T) ((T)((T)-type_max(T)-(T)1))
+#define type_min(t) __type_min(typeof(t))
/*
* Avoids triggering -Wtype-limits compilation warning,
@@ -59,46 +61,123 @@ static inline bool __must_check __must_check_overflow(bool overflow)
* @b: second addend
* @d: pointer to store sum
*
- * Returns 0 on success.
+ * Returns true on wrap-around, false otherwise.
*
- * *@d holds the results of the attempted addition, but is not considered
- * "safe for use" on a non-zero return value, which indicates that the
- * sum has overflowed or been truncated.
+ * *@d holds the results of the attempted addition, regardless of whether
+ * wrap-around occurred.
*/
#define check_add_overflow(a, b, d) \
__must_check_overflow(__builtin_add_overflow(a, b, d))
/**
+ * wrapping_add() - Intentionally perform a wrapping addition
+ * @type: type for result of calculation
+ * @a: first addend
+ * @b: second addend
+ *
+ * Return the potentially wrapped-around addition without
+ * tripping any wrap-around sanitizers that may be enabled.
+ */
+#define wrapping_add(type, a, b) \
+ ({ \
+ type __val; \
+ __builtin_add_overflow(a, b, &__val); \
+ __val; \
+ })
+
+/**
+ * wrapping_assign_add() - Intentionally perform a wrapping increment assignment
+ * @var: variable to be incremented
+ * @offset: amount to add
+ *
+ * Increments @var by @offset with wrap-around. Returns the resulting
+ * value of @var. Will not trip any wrap-around sanitizers.
+ *
+ * Returns the new value of @var.
+ */
+#define wrapping_assign_add(var, offset) \
+ ({ \
+ typeof(var) *__ptr = &(var); \
+ *__ptr = wrapping_add(typeof(var), *__ptr, offset); \
+ })
+
+/**
* check_sub_overflow() - Calculate subtraction with overflow checking
* @a: minuend; value to subtract from
* @b: subtrahend; value to subtract from @a
* @d: pointer to store difference
*
- * Returns 0 on success.
+ * Returns true on wrap-around, false otherwise.
*
- * *@d holds the results of the attempted subtraction, but is not considered
- * "safe for use" on a non-zero return value, which indicates that the
- * difference has underflowed or been truncated.
+ * *@d holds the results of the attempted subtraction, regardless of whether
+ * wrap-around occurred.
*/
#define check_sub_overflow(a, b, d) \
__must_check_overflow(__builtin_sub_overflow(a, b, d))
/**
+ * wrapping_sub() - Intentionally perform a wrapping subtraction
+ * @type: type for result of calculation
+ * @a: minuend; value to subtract from
+ * @b: subtrahend; value to subtract from @a
+ *
+ * Return the potentially wrapped-around subtraction without
+ * tripping any wrap-around sanitizers that may be enabled.
+ */
+#define wrapping_sub(type, a, b) \
+ ({ \
+ type __val; \
+ __builtin_sub_overflow(a, b, &__val); \
+ __val; \
+ })
+
+/**
+ * wrapping_assign_sub() - Intentionally perform a wrapping decrement assign
+ * @var: variable to be decremented
+ * @offset: amount to subtract
+ *
+ * Decrements @var by @offset with wrap-around. Returns the resulting
+ * value of @var. Will not trip any wrap-around sanitizers.
+ *
+ * Returns the new value of @var.
+ */
+#define wrapping_assign_sub(var, offset) \
+ ({ \
+ typeof(var) *__ptr = &(var); \
+ *__ptr = wrapping_sub(typeof(var), *__ptr, offset); \
+ })
+
+/**
* check_mul_overflow() - Calculate multiplication with overflow checking
* @a: first factor
* @b: second factor
* @d: pointer to store product
*
- * Returns 0 on success.
+ * Returns true on wrap-around, false otherwise.
*
- * *@d holds the results of the attempted multiplication, but is not
- * considered "safe for use" on a non-zero return value, which indicates
- * that the product has overflowed or been truncated.
+ * *@d holds the results of the attempted multiplication, regardless of whether
+ * wrap-around occurred.
*/
#define check_mul_overflow(a, b, d) \
__must_check_overflow(__builtin_mul_overflow(a, b, d))
/**
+ * wrapping_mul() - Intentionally perform a wrapping multiplication
+ * @type: type for result of calculation
+ * @a: first factor
+ * @b: second factor
+ *
+ * Return the potentially wrapped-around multiplication without
+ * tripping any wrap-around sanitizers that may be enabled.
+ */
+#define wrapping_mul(type, a, b) \
+ ({ \
+ type __val; \
+ __builtin_mul_overflow(a, b, &__val); \
+ __val; \
+ })
+
+/**
* check_shl_overflow() - Calculate a left-shifted value and check overflow
* @a: Value to be shifted
* @s: How many bits left to shift
@@ -122,7 +201,7 @@ static inline bool __must_check __must_check_overflow(bool overflow)
typeof(a) _a = a; \
typeof(s) _s = s; \
typeof(d) _d = d; \
- u64 _a_full = _a; \
+ unsigned long long _a_full = _a; \
unsigned int _to_shift = \
is_non_negative(_s) && _s < 8 * sizeof(*d) ? _s : 0; \
*_d = (_a_full << _to_shift); \
@@ -132,10 +211,10 @@ static inline bool __must_check __must_check_overflow(bool overflow)
#define __overflows_type_constexpr(x, T) ( \
is_unsigned_type(typeof(x)) ? \
- (x) > type_max(typeof(T)) : \
+ (x) > type_max(T) : \
is_unsigned_type(typeof(T)) ? \
- (x) < 0 || (x) > type_max(typeof(T)) : \
- (x) < type_min(typeof(T)) || (x) > type_max(typeof(T)))
+ (x) < 0 || (x) > type_max(T) : \
+ (x) < type_min(T) || (x) > type_max(T))
#define __overflows_type(x, T) ({ \
typeof(T) v = 0; \
@@ -312,27 +391,40 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
struct_size((type *)NULL, member, count)
/**
- * _DEFINE_FLEX() - helper macro for DEFINE_FLEX() family.
- * Enables caller macro to pass (different) initializer.
+ * __DEFINE_FLEX() - helper macro for DEFINE_FLEX() family.
+ * Enables caller macro to pass arbitrary trailing expressions
*
* @type: structure type name, including "struct" keyword.
* @name: Name for a variable to define.
* @member: Name of the array member.
* @count: Number of elements in the array; must be compile-time const.
- * @initializer: initializer expression (could be empty for no init).
+ * @trailer: Trailing expressions for attributes and/or initializers.
*/
-#define _DEFINE_FLEX(type, name, member, count, initializer) \
+#define __DEFINE_FLEX(type, name, member, count, trailer...) \
_Static_assert(__builtin_constant_p(count), \
"onstack flex array members require compile-time const count"); \
union { \
u8 bytes[struct_size_t(type, member, count)]; \
type obj; \
- } name##_u initializer; \
+ } name##_u trailer; \
type *name = (type *)&name##_u
/**
- * DEFINE_FLEX() - Define an on-stack instance of structure with a trailing
- * flexible array member.
+ * _DEFINE_FLEX() - helper macro for DEFINE_FLEX() family.
+ * Enables caller macro to pass (different) initializer.
+ *
+ * @type: structure type name, including "struct" keyword.
+ * @name: Name for a variable to define.
+ * @member: Name of the array member.
+ * @count: Number of elements in the array; must be compile-time const.
+ * @initializer: Initializer expression (e.g., pass `= { }` at minimum).
+ */
+#define _DEFINE_FLEX(type, name, member, count, initializer...) \
+ __DEFINE_FLEX(type, name, member, count, = { .obj initializer })
+
+/**
+ * DEFINE_RAW_FLEX() - Define an on-stack instance of structure with a trailing
+ * flexible array member, when it does not have a __counted_by annotation.
*
* @type: structure type name, including "struct" keyword.
* @name: Name for a variable to define.
@@ -342,8 +434,42 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
* Define a zeroed, on-stack, instance of @type structure with a trailing
* flexible array member.
* Use __struct_size(@name) to get compile-time size of it afterwards.
+ * Use __member_size(@name->member) to get compile-time size of @name members.
+ * Use STACK_FLEX_ARRAY_SIZE(@name, @member) to get compile-time number of
+ * elements in array @member.
+ */
+#define DEFINE_RAW_FLEX(type, name, member, count) \
+ __DEFINE_FLEX(type, name, member, count, = { })
+
+/**
+ * DEFINE_FLEX() - Define an on-stack instance of structure with a trailing
+ * flexible array member.
+ *
+ * @TYPE: structure type name, including "struct" keyword.
+ * @NAME: Name for a variable to define.
+ * @MEMBER: Name of the array member.
+ * @COUNTER: Name of the __counted_by member.
+ * @COUNT: Number of elements in the array; must be compile-time const.
+ *
+ * Define a zeroed, on-stack, instance of @TYPE structure with a trailing
+ * flexible array member.
+ * Use __struct_size(@NAME) to get compile-time size of it afterwards.
+ * Use __member_size(@NAME->member) to get compile-time size of @NAME members.
+ * Use STACK_FLEX_ARRAY_SIZE(@name, @member) to get compile-time number of
+ * elements in array @member.
+ */
+#define DEFINE_FLEX(TYPE, NAME, MEMBER, COUNTER, COUNT) \
+ _DEFINE_FLEX(TYPE, NAME, MEMBER, COUNT, = { .COUNTER = COUNT, })
+
+/**
+ * STACK_FLEX_ARRAY_SIZE() - helper macro for DEFINE_FLEX() family.
+ * Returns the number of elements in @array.
+ *
+ * @name: Name for a variable defined in DEFINE_RAW_FLEX()/DEFINE_FLEX().
+ * @array: Name of the array member.
*/
-#define DEFINE_FLEX(type, name, member, count) \
- _DEFINE_FLEX(type, name, member, count, = {})
+#define STACK_FLEX_ARRAY_SIZE(name, array) \
+ (__member_size((name)->array) / sizeof(*(name)->array) + \
+ __must_be_array((name)->array))
#endif /* _LINUXKPI_LINUX_OVERFLOW_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/page-flags.h b/sys/compat/linuxkpi/common/include/linux/page-flags.h
index 9dd49c8492a5..a22b3a24c330 100644
--- a/sys/compat/linuxkpi/common/include/linux/page-flags.h
+++ b/sys/compat/linuxkpi/common/include/linux/page-flags.h
@@ -29,6 +29,13 @@
#ifndef _LINUXKPI_LINUX_PAGEFLAGS_H_
#define _LINUXKPI_LINUX_PAGEFLAGS_H_
+#include <linux/mm_types.h>
+
#define PageHighMem(p) (0)
+#define page_folio(p) \
+ (_Generic((p), \
+ const struct page *: (const struct folio *)(p), \
+ struct page *: (struct folio *)(p)))
+
#endif /* _LINUXKPI_LINUX_PAGEFLAGS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/pagemap.h b/sys/compat/linuxkpi/common/include/linux/pagemap.h
index 7244b61257dc..cb6a1820ea8b 100644
--- a/sys/compat/linuxkpi/common/include/linux/pagemap.h
+++ b/sys/compat/linuxkpi/common/include/linux/pagemap.h
@@ -33,6 +33,8 @@
#include <linux/highmem.h>
#include <linux/vmalloc.h>
+struct folio_batch;
+
#define invalidate_mapping_pages(...) \
linux_invalidate_mapping_pages(__VA_ARGS__)
@@ -40,15 +42,6 @@ unsigned long linux_invalidate_mapping_pages(vm_object_t obj, pgoff_t start,
pgoff_t end);
static inline void
-release_pages(struct page **pages, int nr)
-{
- int i;
-
- for (i = 0; i < nr; i++)
- put_page(pages[i]);
-}
-
-static inline void
mapping_clear_unevictable(vm_object_t mapping)
{
}
diff --git a/sys/compat/linuxkpi/common/include/linux/pagevec.h b/sys/compat/linuxkpi/common/include/linux/pagevec.h
index 9ba8ff8effa0..0a952e965b5a 100644
--- a/sys/compat/linuxkpi/common/include/linux/pagevec.h
+++ b/sys/compat/linuxkpi/common/include/linux/pagevec.h
@@ -66,4 +66,72 @@ check_move_unevictable_pages(struct pagevec *pvec)
{
}
+/*
+ * struct folio
+ *
+ * On Linux, `struct folio` replaces `struct page`. To manage a list of folios,
+ * there is `struct folio_batch` on top of this, which replaces `struct
+ * pagevec` above.
+ *
+ * Here is the original description when `struct folio` was added to the Linux
+ * kernel:
+ * "A struct folio is a new abstraction to replace the venerable struct page.
+ * A function which takes a struct folio argument declares that it will
+ * operate on the entire (possibly compound) page, not just PAGE_SIZE bytes.
+ * In return, the caller guarantees that the pointer it is passing does not
+ * point to a tail page. No change to generated code."
+ */
+
+struct folio;
+
+struct folio_batch {
+ uint8_t nr;
+ struct folio *folios[PAGEVEC_SIZE];
+};
+
+static inline void
+folio_batch_init(struct folio_batch *fbatch)
+{
+ fbatch->nr = 0;
+}
+
+static inline void
+folio_batch_reinit(struct folio_batch *fbatch)
+{
+ fbatch->nr = 0;
+}
+
+static inline unsigned int
+folio_batch_count(struct folio_batch *fbatch)
+{
+ return (fbatch->nr);
+}
+
+static inline unsigned int
+folio_batch_space(struct folio_batch *fbatch)
+{
+ return (PAGEVEC_SIZE - fbatch->nr);
+}
+
+static inline unsigned int
+folio_batch_add(struct folio_batch *fbatch, struct folio *folio)
+{
+ KASSERT(
+ fbatch->nr < PAGEVEC_SIZE,
+ ("struct folio_batch %p is full", fbatch));
+
+ fbatch->folios[fbatch->nr++] = folio;
+
+ return (folio_batch_space(fbatch));
+}
+
+void __folio_batch_release(struct folio_batch *fbatch);
+
+static inline void
+folio_batch_release(struct folio_batch *fbatch)
+{
+ if (folio_batch_count(fbatch))
+ __folio_batch_release(fbatch);
+}
+
#endif /* _LINUXKPI_LINUX_PAGEVEC_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h
index afd6c827b3b4..d891d0df3546 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -4,7 +4,7 @@
* Copyright (c) 2010 Panasas, Inc.
* Copyright (c) 2013-2016 Mellanox Technologies, Ltd.
* All rights reserved.
- * Copyright (c) 2020-2022 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
*
* Portions of this software were developed by Björn Zeeb
* under sponsorship from the FreeBSD Foundation.
@@ -72,6 +72,10 @@ struct pci_device_id {
uintptr_t driver_data;
};
+#define MODULE_DEVICE_TABLE_BUS_pci(_bus, _table) \
+MODULE_PNP_INFO("U32:vendor;U32:device;V32:subvendor;V32:subdevice", \
+ _bus, lkpi_ ## _table, _table, nitems(_table) - 1)
+
/* Linux has an empty element at the end of the ID table -> nitems() - 1. */
#define MODULE_DEVICE_TABLE(_bus, _table) \
\
@@ -85,11 +89,10 @@ static driver_t _ ## _bus ## _ ## _table ## _driver = { \
0 \
}; \
\
-DRIVER_MODULE(lkpi_ ## _table, pci, _ ## _bus ## _ ## _table ## _driver,\
+DRIVER_MODULE(lkpi_ ## _table, _bus, _ ## _bus ## _ ## _table ## _driver,\
0, 0); \
\
-MODULE_PNP_INFO("U32:vendor;U32:device;V32:subvendor;V32:subdevice", \
- _bus, lkpi_ ## _table, _table, nitems(_table) - 1)
+MODULE_DEVICE_TABLE_BUS_ ## _bus(_bus, _table)
#define PCI_ANY_ID -1U
@@ -220,11 +223,11 @@ enum pcie_link_width {
typedef int pci_power_t;
-#define PCI_D0 PCI_POWERSTATE_D0
-#define PCI_D1 PCI_POWERSTATE_D1
-#define PCI_D2 PCI_POWERSTATE_D2
-#define PCI_D3hot PCI_POWERSTATE_D3
-#define PCI_D3cold 4
+#define PCI_D0 PCI_POWERSTATE_D0
+#define PCI_D1 PCI_POWERSTATE_D1
+#define PCI_D2 PCI_POWERSTATE_D2
+#define PCI_D3hot PCI_POWERSTATE_D3_HOT
+#define PCI_D3cold PCI_POWERSTATE_D3_COLD
#define PCI_POWER_ERROR PCI_POWERSTATE_UNKNOWN
@@ -352,20 +355,22 @@ struct pci_dev {
TAILQ_HEAD(, pci_mmio_region) mmio;
};
-int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name);
int pci_alloc_irq_vectors(struct pci_dev *pdev, int minv, int maxv,
unsigned int flags);
bool pci_device_is_present(struct pci_dev *pdev);
int linuxkpi_pcim_enable_device(struct pci_dev *pdev);
void __iomem **linuxkpi_pcim_iomap_table(struct pci_dev *pdev);
-void *linuxkpi_pci_iomap_range(struct pci_dev *pdev, int mmio_bar,
- unsigned long mmio_off, unsigned long mmio_size);
-void *linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size);
+void *linuxkpi_pci_iomap_range(struct pci_dev *, int,
+ unsigned long, unsigned long);
+void *linuxkpi_pci_iomap(struct pci_dev *, int, unsigned long);
+void *linuxkpi_pcim_iomap(struct pci_dev *, int, unsigned long);
void linuxkpi_pci_iounmap(struct pci_dev *pdev, void *res);
int linuxkpi_pcim_iomap_regions(struct pci_dev *pdev, uint32_t mask,
const char *name);
+int linuxkpi_pci_request_region(struct pci_dev *, int, const char *);
int linuxkpi_pci_request_regions(struct pci_dev *pdev, const char *res_name);
+int linuxkpi_pcim_request_all_regions(struct pci_dev *, const char *);
void linuxkpi_pci_release_region(struct pci_dev *pdev, int bar);
void linuxkpi_pci_release_regions(struct pci_dev *pdev);
int linuxkpi_pci_enable_msix(struct pci_dev *pdev, struct msix_entry *entries,
@@ -374,13 +379,15 @@ int linuxkpi_pci_enable_msix(struct pci_dev *pdev, struct msix_entry *entries,
/* Internal helper function(s). */
struct pci_dev *lkpinew_pci_dev(device_t);
void lkpi_pci_devres_release(struct device *, void *);
-struct pci_dev *lkpi_pci_get_device(uint16_t, uint16_t, struct pci_dev *);
+struct pci_dev *lkpi_pci_get_device(uint32_t, uint32_t, struct pci_dev *);
struct msi_desc *lkpi_pci_msi_desc_alloc(int);
struct device *lkpi_pci_find_irq_dev(unsigned int irq);
int _lkpi_pci_enable_msi_range(struct pci_dev *pdev, int minvec, int maxvec);
#define pci_err(pdev, fmt, ...) \
- dev_err(&(pdev)->dev, fmt, __VA_ARGS__)
+ dev_err(&(pdev)->dev, fmt, ##__VA_ARGS__)
+#define pci_info(pdev, fmt, ...) \
+ dev_info(&(pdev)->dev, fmt, ##__VA_ARGS__)
static inline bool
dev_is_pci(struct device *dev)
@@ -523,7 +530,20 @@ pci_upstream_bridge(struct pci_dev *pdev)
if (pdev == pdev->bus->self) {
device_t bridge;
- bridge = device_get_parent(pdev->dev.bsddev);
+ /*
+ * In the case of DRM drivers, the passed device is a child of
+ * `vgapci`. We want to start the lookup from `vgapci`, so the
+ * parent of the passed `drmn`.
+ *
+ * We can use the `isdrm` flag to determine this.
+ */
+ bridge = pdev->dev.bsddev;
+ if (pdev->pdrv != NULL && pdev->pdrv->isdrm)
+ bridge = device_get_parent(bridge);
+ if (bridge == NULL)
+ goto done;
+
+ bridge = device_get_parent(bridge);
if (bridge == NULL)
goto done;
bridge = device_get_parent(bridge);
@@ -543,10 +563,16 @@ done:
return (pdev->bus->self);
}
-#define pci_release_region(pdev, bar) linuxkpi_pci_release_region(pdev, bar)
-#define pci_release_regions(pdev) linuxkpi_pci_release_regions(pdev)
-#define pci_request_regions(pdev, res_name) \
- linuxkpi_pci_request_regions(pdev, res_name)
+#define pci_request_region(pdev, bar, res_name) \
+ linuxkpi_pci_request_region(pdev, bar, res_name)
+#define pci_release_region(pdev, bar) \
+ linuxkpi_pci_release_region(pdev, bar)
+#define pci_request_regions(pdev, res_name) \
+ linuxkpi_pci_request_regions(pdev, res_name)
+#define pci_release_regions(pdev) \
+ linuxkpi_pci_release_regions(pdev)
+#define pcim_request_all_regions(pdev, name) \
+ linuxkpi_pcim_request_all_regions(pdev, name)
static inline void
lkpi_pci_disable_msix(struct pci_dev *pdev)
@@ -712,8 +738,10 @@ int linux_pci_register_drm_driver(struct pci_driver *pdrv);
void linux_pci_unregister_driver(struct pci_driver *pdrv);
void linux_pci_unregister_drm_driver(struct pci_driver *pdrv);
-#define pci_register_driver(pdrv) linux_pci_register_driver(pdrv)
-#define pci_unregister_driver(pdrv) linux_pci_unregister_driver(pdrv)
+#define pci_register_driver(pdrv) \
+ linux_pci_register_driver(pdrv)
+#define pci_unregister_driver(pdrv) \
+ linux_pci_unregister_driver(pdrv)
/*
* Enable msix, positive errors indicate actual number of available
@@ -722,10 +750,11 @@ void linux_pci_unregister_drm_driver(struct pci_driver *pdrv);
* NB: define added to prevent this definition of pci_enable_msix from
* clashing with the native FreeBSD version.
*/
-#define pci_enable_msix(...) linuxkpi_pci_enable_msix(__VA_ARGS__)
+#define pci_enable_msix(...) \
+ linuxkpi_pci_enable_msix(__VA_ARGS__)
-#define pci_enable_msix_range(...) \
- linux_pci_enable_msix_range(__VA_ARGS__)
+#define pci_enable_msix_range(...) \
+ linux_pci_enable_msix_range(__VA_ARGS__)
static inline int
pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
@@ -750,8 +779,8 @@ pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
return (nvec);
}
-#define pci_enable_msi(pdev) \
- linux_pci_enable_msi(pdev)
+#define pci_enable_msi(pdev) \
+ linux_pci_enable_msi(pdev)
static inline int
pci_enable_msi(struct pci_dev *pdev)
@@ -776,11 +805,14 @@ static inline void pci_disable_sriov(struct pci_dev *dev)
{
}
-#define pci_iomap_range(pdev, mmio_bar, mmio_off, mmio_size) \
- linuxkpi_pci_iomap_range(pdev, mmio_bar, mmio_off, mmio_size)
-#define pci_iomap(pdev, mmio_bar, mmio_size) \
- linuxkpi_pci_iomap(pdev, mmio_bar, mmio_size)
-#define pci_iounmap(pdev, res) linuxkpi_pci_iounmap(pdev, res)
+#define pci_iomap_range(pdev, mmio_bar, mmio_off, mmio_size) \
+ linuxkpi_pci_iomap_range(pdev, mmio_bar, mmio_off, mmio_size)
+#define pci_iomap(pdev, mmio_bar, mmio_size) \
+ linuxkpi_pci_iomap(pdev, mmio_bar, mmio_size)
+#define pcim_iomap(pdev, bar, maxlen) \
+ linuxkpi_pcim_iomap(pdev, bar, maxlen)
+#define pci_iounmap(pdev, res) \
+ linuxkpi_pci_iounmap(pdev, res)
static inline void
lkpi_pci_save_state(struct pci_dev *pdev)
@@ -1369,10 +1401,12 @@ struct pci_dev *lkpi_pci_get_base_class(unsigned int class,
/* -------------------------------------------------------------------------- */
-#define pcim_enable_device(pdev) linuxkpi_pcim_enable_device(pdev)
-#define pcim_iomap_table(pdev) linuxkpi_pcim_iomap_table(pdev)
-#define pcim_iomap_regions(pdev, mask, name) \
- linuxkpi_pcim_iomap_regions(pdev, mask, name)
+#define pcim_enable_device(pdev) \
+ linuxkpi_pcim_enable_device(pdev)
+#define pcim_iomap_table(pdev) \
+ linuxkpi_pcim_iomap_table(pdev)
+#define pcim_iomap_regions(pdev, mask, name) \
+ linuxkpi_pcim_iomap_regions(pdev, mask, name)
static inline int
pcim_iomap_regions_request_all(struct pci_dev *pdev, uint32_t mask, char *name)
@@ -1413,12 +1447,15 @@ err:
* using pci_get_device() need to be changed to call linuxkpi_pci_get_device().
*/
static inline struct pci_dev *
-linuxkpi_pci_get_device(uint16_t vendor, uint16_t device, struct pci_dev *odev)
+linuxkpi_pci_get_device(uint32_t vendor, uint32_t device, struct pci_dev *odev)
{
return (lkpi_pci_get_device(vendor, device, odev));
}
+#define for_each_pci_dev(_pdev) \
+ while ((_pdev = linuxkpi_pci_get_device(PCI_ANY_ID, PCI_ANY_ID, _pdev)) != NULL)
+
/* This is a FreeBSD extension so we can use bus_*(). */
static inline void
linuxkpi_pcim_want_to_use_bus_functions(struct pci_dev *pdev)
diff --git a/sys/compat/linuxkpi/common/include/linux/pci_ids.h b/sys/compat/linuxkpi/common/include/linux/pci_ids.h
index 2f02d6ad1c14..e318f6f75ce7 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci_ids.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci_ids.h
@@ -46,6 +46,7 @@
#define PCI_VENDOR_ID_APPLE 0x106b
#define PCI_VENDOR_ID_ASUSTEK 0x1043
+#define PCI_VENDOR_ID_ASMEDIA 0x1b21
#define PCI_VENDOR_ID_ATHEROS 0x168c
#define PCI_VENDOR_ID_ATI 0x1002
#define PCI_VENDOR_ID_BROADCOM 0x14e4
diff --git a/sys/compat/linuxkpi/common/include/linux/pm_runtime.h b/sys/compat/linuxkpi/common/include/linux/pm_runtime.h
index 616dd508e562..6114b7b159d7 100644
--- a/sys/compat/linuxkpi/common/include/linux/pm_runtime.h
+++ b/sys/compat/linuxkpi/common/include/linux/pm_runtime.h
@@ -34,8 +34,13 @@ pm_runtime_get_if_in_use(struct device *dev)
return 1;
}
+#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION < 60900
static inline int
pm_runtime_get_if_active(struct device *dev, bool x)
+#else
+static inline int
+pm_runtime_get_if_active(struct device *dev)
+#endif
{
return 1;
}
diff --git a/sys/compat/linuxkpi/common/include/linux/printk.h b/sys/compat/linuxkpi/common/include/linux/printk.h
index 3840a6e5fb8a..d2d197682782 100644
--- a/sys/compat/linuxkpi/common/include/linux/printk.h
+++ b/sys/compat/linuxkpi/common/include/linux/printk.h
@@ -44,57 +44,19 @@ enum {
DUMP_PREFIX_OFFSET
};
+int __lkpi_hexdump_printf(void *, const char *, ...) __printflike(2, 3);
+
+void lkpi_hex_dump(int(*)(void *, const char *, ...), void *arg1,
+ const char *, const char *, const int, const int, const int,
+ const void *, size_t, const bool);
+
static inline void
print_hex_dump(const char *level, const char *prefix_str,
const int prefix_type, const int rowsize, const int groupsize,
const void *buf, size_t len, const bool ascii)
{
- typedef const struct { long long value; } __packed *print_64p_t;
- typedef const struct { uint32_t value; } __packed *print_32p_t;
- typedef const struct { uint16_t value; } __packed *print_16p_t;
- const void *buf_old = buf;
- int row;
-
- while (len > 0) {
- if (level != NULL)
- printf("%s", level);
- if (prefix_str != NULL)
- printf("%s ", prefix_str);
-
- switch (prefix_type) {
- case DUMP_PREFIX_ADDRESS:
- printf("[%p] ", buf);
- break;
- case DUMP_PREFIX_OFFSET:
- printf("[%#tx] ", ((const char *)buf -
- (const char *)buf_old));
- break;
- default:
- break;
- }
- for (row = 0; row != rowsize; row++) {
- if (groupsize == 8 && len > 7) {
- printf("%016llx ", ((print_64p_t)buf)->value);
- buf = (const uint8_t *)buf + 8;
- len -= 8;
- } else if (groupsize == 4 && len > 3) {
- printf("%08x ", ((print_32p_t)buf)->value);
- buf = (const uint8_t *)buf + 4;
- len -= 4;
- } else if (groupsize == 2 && len > 1) {
- printf("%04x ", ((print_16p_t)buf)->value);
- buf = (const uint8_t *)buf + 2;
- len -= 2;
- } else if (len > 0) {
- printf("%02x ", *(const uint8_t *)buf);
- buf = (const uint8_t *)buf + 1;
- len--;
- } else {
- break;
- }
- }
- printf("\n");
- }
+ lkpi_hex_dump(__lkpi_hexdump_printf, NULL, level, prefix_str, prefix_type,
+ rowsize, groupsize, buf, len, ascii);
}
static inline void
@@ -132,4 +94,10 @@ print_hex_dump_bytes(const char *prefix_str, const int prefix_type,
0; \
})
+#define FW_BUG "[Firmware Bug]: "
+#define FW_WARN "[Firmware Warn]: "
+#define FW_INFO "[Firmware Info]: "
+#define HW_ERR "[Hardware Error]: "
+#define DEPRECATED "[Deprecated]: "
+
#endif /* _LINUXKPI_LINUX_PRINTK_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/rcupdate.h b/sys/compat/linuxkpi/common/include/linux/rcupdate.h
index 85d766c8dbc9..4aceb7296cd6 100644
--- a/sys/compat/linuxkpi/common/include/linux/rcupdate.h
+++ b/sys/compat/linuxkpi/common/include/linux/rcupdate.h
@@ -1,7 +1,7 @@
/*-
* Copyright (c) 2016-2017 Mellanox Technologies, Ltd.
* All rights reserved.
- * Copyright (c) 2024 The FreeBSD Foundation
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
*
* Portions of this software were developed by Björn Zeeb
* under sponsorship from the FreeBSD Foundation.
@@ -35,6 +35,7 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/kernel.h>
+#include <linux/cleanup.h>
#include <machine/atomic.h>
@@ -162,4 +163,6 @@ void linux_synchronize_rcu(unsigned type);
#define init_rcu_head_on_stack(...)
#define destroy_rcu_head_on_stack(...)
+DEFINE_LOCK_GUARD_0(rcu, rcu_read_lock(), rcu_read_unlock())
+
#endif /* _LINUXKPI_LINUX_RCUPDATE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/ref_tracker.h b/sys/compat/linuxkpi/common/include/linux/ref_tracker.h
new file mode 100644
index 000000000000..fa510b2498e1
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/ref_tracker.h
@@ -0,0 +1,93 @@
+/*-
+ * 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_REF_TRACKER_H_
+#define _LINUXKPI_LINUX_REF_TRACKER_H_
+
+#include <linux/refcount.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/stackdepot.h>
+
+struct ref_tracker;
+
+struct ref_tracker_dir {
+};
+
+/*
+ * The following functions currently have dummy implementations that, on Linux,
+ * are used when CONFIG_REF_TRACKER is not set at compile time.
+ *
+ * The ref tracker is a tool to associate a refcount increase to a refcount
+ * decrease. This helps developers track, document and debug refcounts. We
+ * don't need this feature for now in linuxkpi.
+ */
+
+static inline void
+ref_tracker_dir_init(struct ref_tracker_dir *dir,
+ unsigned int quarantine_count, const char *name)
+{
+}
+
+static inline void
+ref_tracker_dir_exit(struct ref_tracker_dir *dir)
+{
+}
+
+static inline void
+ref_tracker_dir_print_locked(struct ref_tracker_dir *dir,
+ unsigned int display_limit)
+{
+}
+
+static inline void
+ref_tracker_dir_print(struct ref_tracker_dir *dir, unsigned int display_limit)
+{
+}
+
+static inline int
+ref_tracker_dir_snprint(struct ref_tracker_dir *dir, char *buf, size_t size)
+{
+ return (0);
+}
+
+static inline int
+ref_tracker_alloc(struct ref_tracker_dir *dir, struct ref_tracker **trackerp,
+ gfp_t gfp)
+{
+ return (0);
+}
+
+static inline int
+ref_tracker_free(struct ref_tracker_dir *dir, struct ref_tracker **trackerp)
+{
+ return (0);
+}
+
+#endif /* !defined(_LINUXKPI_LINUX_REF_TRACKER_H_) */
diff --git a/sys/compat/linuxkpi/common/include/linux/refcount.h b/sys/compat/linuxkpi/common/include/linux/refcount.h
index 02a7eda3f4a9..46e501a65396 100644
--- a/sys/compat/linuxkpi/common/include/linux/refcount.h
+++ b/sys/compat/linuxkpi/common/include/linux/refcount.h
@@ -30,6 +30,7 @@
#define _LINUXKPI_LINUX_REFCOUNT_H
#include <linux/atomic.h>
+#include <linux/spinlock.h>
typedef atomic_t refcount_t;
diff --git a/sys/compat/linuxkpi/common/include/linux/scatterlist.h b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
index 51ced19e6b5b..537f5bebc5aa 100644
--- a/sys/compat/linuxkpi/common/include/linux/scatterlist.h
+++ b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
@@ -674,4 +674,11 @@ sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
return (total);
}
+static inline void
+sg_set_folio(struct scatterlist *sg, struct folio *folio, size_t len,
+ size_t offset)
+{
+ sg_set_page(sg, &folio->page, len, offset);
+}
+
#endif /* _LINUXKPI_LINUX_SCATTERLIST_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/sched.h b/sys/compat/linuxkpi/common/include/linux/sched.h
index 80354493f955..3ad2f8e4ce8b 100644
--- a/sys/compat/linuxkpi/common/include/linux/sched.h
+++ b/sys/compat/linuxkpi/common/include/linux/sched.h
@@ -53,7 +53,7 @@
#include <asm/atomic.h>
-#define MAX_SCHEDULE_TIMEOUT INT_MAX
+#define MAX_SCHEDULE_TIMEOUT LONG_MAX
#define TASK_RUNNING 0x0000
#define TASK_INTERRUPTIBLE 0x0001
@@ -160,7 +160,7 @@ void linux_send_sig(int signo, struct task_struct *task);
linux_send_sig(signo, task); \
} while (0)
-int linux_schedule_timeout(int timeout);
+long linux_schedule_timeout(long timeout);
static inline void
linux_schedule_save_interrupt_value(struct task_struct *task, int value)
diff --git a/sys/compat/linuxkpi/common/include/linux/seq_file.h b/sys/compat/linuxkpi/common/include/linux/seq_file.h
index d8b327f59538..47da16ab8688 100644
--- a/sys/compat/linuxkpi/common/include/linux/seq_file.h
+++ b/sys/compat/linuxkpi/common/include/linux/seq_file.h
@@ -28,9 +28,13 @@
#ifndef _LINUXKPI_LINUX_SEQ_FILE_H_
#define _LINUXKPI_LINUX_SEQ_FILE_H_
+#include <sys/types.h>
+#include <sys/sbuf.h>
+
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/string_helpers.h>
+#include <linux/printk.h>
#undef file
#define inode vnode
@@ -51,6 +55,21 @@ static const struct file_operations __name ## _fops = { \
.release = single_release, \
}
+#define DEFINE_SHOW_STORE_ATTRIBUTE(__name) \
+static int __name ## _open(struct inode *inode, struct linux_file *file) \
+{ \
+ return single_open(file, __name ## _show, inode->i_private); \
+} \
+ \
+static const struct file_operations __name ## _fops = { \
+ .owner = THIS_MODULE, \
+ .open = __name ## _open, \
+ .read = seq_read, \
+ .write = __name ## _write, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+}
+
struct seq_file {
struct sbuf *buf;
size_t size;
@@ -89,6 +108,16 @@ void lkpi_seq_printf(struct seq_file *m, const char *fmt, ...);
#define seq_vprintf(...) lkpi_seq_vprintf(__VA_ARGS__)
#define seq_printf(...) lkpi_seq_printf(__VA_ARGS__)
+int __lkpi_hexdump_sbuf_printf(void *, const char *, ...) __printflike(2, 3);
+
+static inline void
+seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type,
+ int rowsize, int groupsize, const void *buf, size_t len, bool ascii)
+{
+ lkpi_hex_dump(__lkpi_hexdump_sbuf_printf, m->buf, NULL, prefix_str, prefix_type,
+ rowsize, groupsize, buf, len, ascii);
+}
+
#define file linux_file
#endif /* _LINUXKPI_LINUX_SEQ_FILE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/shmem_fs.h b/sys/compat/linuxkpi/common/include/linux/shmem_fs.h
index efa2c855fe7d..5e91725d4a1c 100644
--- a/sys/compat/linuxkpi/common/include/linux/shmem_fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/shmem_fs.h
@@ -54,4 +54,14 @@ void linux_shmem_truncate_range(vm_object_t obj, loff_t lstart,
#define shmem_truncate_range(...) \
linux_shmem_truncate_range(__VA_ARGS__)
+static inline struct folio *
+shmem_read_folio_gfp(vm_object_t obj, int pindex, gfp_t gfp)
+{
+ struct page *page;
+
+ page = shmem_read_mapping_page_gfp(obj, pindex, gfp);
+
+ return (page_folio(page));
+}
+
#endif /* _LINUXKPI_LINUX_SHMEM_FS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/skbuff.h b/sys/compat/linuxkpi/common/include/linux/skbuff.h
index 9db29c72e20c..6e41c368a8b8 100644
--- a/sys/compat/linuxkpi/common/include/linux/skbuff.h
+++ b/sys/compat/linuxkpi/common/include/linux/skbuff.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2020-2025 The FreeBSD Foundation
- * Copyright (c) 2021-2023 Bjoern A. Zeeb
+ * Copyright (c) 2021-2025 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -47,13 +47,11 @@
#include <linux/ktime.h>
#include <linux/compiler.h>
-#include "opt_wlan.h"
-
-/* Currently this is only used for wlan so we can depend on that. */
-#if defined(IEEE80211_DEBUG) && !defined(SKB_DEBUG)
-#define SKB_DEBUG
-#endif
-
+/*
+ * At least the net/intel-irdma-kmod port pulls this header in; likely through
+ * if_ether.h (see PR289268). This means we no longer can rely on
+ * IEEE80211_DEBUG (opt_wlan.h) to automatically set SKB_DEBUG.
+ */
/* #define SKB_DEBUG */
#ifdef SKB_DEBUG
@@ -120,7 +118,7 @@ enum sk_checksum_flags {
CHECKSUM_NONE = 0x00,
CHECKSUM_UNNECESSARY = 0x01,
CHECKSUM_PARTIAL = 0x02,
- CHECKSUM_COMPLETE = 0x04,
+ CHECKSUM_COMPLETE = 0x03,
};
struct skb_frag {
@@ -154,38 +152,46 @@ struct sk_buff {
};
struct list_head list;
};
- uint32_t _alloc_len; /* Length of alloc data-buf. XXX-BZ give up for truesize? */
+
+ uint8_t *head; /* Head of buffer. */
+ uint8_t *data; /* Head of data. */
+ uint8_t *tail; /* End of data. */
+ uint8_t *end; /* End of buffer. */
+
uint32_t len; /* ? */
uint32_t data_len; /* ? If we have frags? */
+ union {
+ __wsum csum;
+ struct {
+ uint16_t csum_offset;
+ uint16_t csum_start;
+ };
+ };
+ uint16_t protocol;
+ uint8_t ip_summed; /* 2 bit only. */
+ /* uint8_t */
+
+ /* "Scratch" area for layers to store metadata. */
+ /* ??? I see sizeof() operations so probably an array. */
+ uint8_t cb[64] __aligned(CACHE_LINE_SIZE);
+
+ struct skb_shared_info *shinfo __aligned(CACHE_LINE_SIZE);
+
uint32_t truesize; /* The total size of all buffers, incl. frags. */
- uint16_t mac_len; /* Link-layer header length. */
- __sum16 csum;
- uint16_t l3hdroff; /* network header offset from *head */
- uint16_t l4hdroff; /* transport header offset from *head */
uint32_t priority;
uint16_t qmap; /* queue mapping */
uint16_t _flags; /* Internal flags. */
#define _SKB_FLAGS_SKBEXTFRAG 0x0001
- enum sk_buff_pkt_type pkt_type;
+ uint16_t l3hdroff; /* network header offset from *head */
+ uint16_t l4hdroff; /* transport header offset from *head */
uint16_t mac_header; /* offset of mac_header */
+ uint16_t mac_len; /* Link-layer header length. */
+ enum sk_buff_pkt_type pkt_type;
refcount_t refcnt;
- /* "Scratch" area for layers to store metadata. */
- /* ??? I see sizeof() operations so probably an array. */
- uint8_t cb[64] __aligned(CACHE_LINE_SIZE);
-
struct net_device *dev;
void *sk; /* XXX net/sock.h? */
- int csum_offset, csum_start, ip_summed, protocol;
-
- uint8_t *head; /* Head of buffer. */
- uint8_t *data; /* Head of data. */
- uint8_t *tail; /* End of data. */
- uint8_t *end; /* End of buffer. */
-
- struct skb_shared_info *shinfo;
-
/* FreeBSD specific bandaid (see linuxkpi_kfree_skb). */
void *m;
void(*m_free_func)(void *);
@@ -993,6 +999,13 @@ skb_get_queue_mapping(struct sk_buff *skb)
return (skb->qmap);
}
+static inline void
+skb_copy_header(struct sk_buff *to, const struct sk_buff *from)
+{
+ SKB_TRACE2(to, from);
+ SKB_TODO();
+}
+
static inline bool
skb_header_cloned(struct sk_buff *skb)
{
@@ -1038,7 +1051,7 @@ skb_orphan(struct sk_buff *skb)
SKB_TODO();
}
-static inline __sum16
+static inline __wsum
csum_unfold(__sum16 sum)
{
return (sum);
diff --git a/sys/compat/linuxkpi/common/include/linux/slab.h b/sys/compat/linuxkpi/common/include/linux/slab.h
index 07c16884b00e..0e649e1e3c4a 100644
--- a/sys/compat/linuxkpi/common/include/linux/slab.h
+++ b/sys/compat/linuxkpi/common/include/linux/slab.h
@@ -4,6 +4,10 @@
* Copyright (c) 2010 Panasas, Inc.
* Copyright (c) 2013-2021 Mellanox Technologies, Ltd.
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Björn Zeeb
+ * 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
@@ -36,18 +40,19 @@
#include <linux/compat.h>
#include <linux/types.h>
#include <linux/gfp.h>
+#include <linux/err.h>
#include <linux/llist.h>
#include <linux/overflow.h>
+#include <linux/cleanup.h>
MALLOC_DECLARE(M_KMALLOC);
-#define kmalloc(size, flags) lkpi_kmalloc(size, flags)
-#define kvmalloc(size, flags) kmalloc(size, flags)
-#define kvzalloc(size, flags) kmalloc(size, (flags) | __GFP_ZERO)
+#define kvzalloc(size, flags) kvmalloc(size, (flags) | __GFP_ZERO)
#define kvcalloc(n, size, flags) kvmalloc_array(n, size, (flags) | __GFP_ZERO)
#define kzalloc(size, flags) kmalloc(size, (flags) | __GFP_ZERO)
#define kzalloc_node(size, flags, node) kmalloc_node(size, (flags) | __GFP_ZERO, node)
#define kfree_const(ptr) kfree(ptr)
+#define kfree_async(ptr) kfree(ptr) /* drm-kmod 5.4 compat */
#define vzalloc(size) __vmalloc(size, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO, 0)
#define vfree(arg) kfree(arg)
#define kvfree(arg) kfree(arg)
@@ -85,15 +90,22 @@ struct linux_kmem_cache;
#define ARCH_KMALLOC_MINALIGN \
__alignof(unsigned long long)
-/* drm-kmod 5.4 compat */
-#define kfree_async(ptr) kfree(ptr);
-
#define ZERO_SIZE_PTR ((void *)16)
#define ZERO_OR_NULL_PTR(x) ((x) == NULL || (x) == ZERO_SIZE_PTR)
-extern void *lkpi_kmalloc(size_t size, gfp_t flags);
-void *lkpi___kmalloc(size_t size, gfp_t flags);
-#define __kmalloc(_s, _f) lkpi___kmalloc(_s, _f)
+struct linux_kmem_cache *linux_kmem_cache_create(const char *name,
+ size_t size, size_t align, unsigned flags, linux_kmem_ctor_t *ctor);
+void *lkpi_kmem_cache_alloc(struct linux_kmem_cache *, gfp_t);
+void *lkpi_kmem_cache_zalloc(struct linux_kmem_cache *, gfp_t);
+void lkpi_kmem_cache_free(struct linux_kmem_cache *, void *);
+void linux_kmem_cache_destroy(struct linux_kmem_cache *);
+
+void *lkpi_kmalloc(size_t, gfp_t);
+void *lkpi_kvmalloc(size_t, gfp_t);
+void *lkpi___kmalloc(size_t, gfp_t);
+void *lkpi___kmalloc_node(size_t, gfp_t, int);
+void *lkpi_krealloc(void *, size_t, gfp_t);
+void lkpi_kfree(const void *);
static inline gfp_t
linux_check_m_flags(gfp_t flags)
@@ -110,103 +122,124 @@ linux_check_m_flags(gfp_t flags)
return (flags & GFP_NATIVE_MASK);
}
+/*
+ * Base functions with a native implementation.
+ */
static inline void *
-kmalloc_node(size_t size, gfp_t flags, int node)
+kmalloc(size_t size, gfp_t flags)
{
- return (malloc_domainset(size, M_KMALLOC,
- linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+ return (lkpi_kmalloc(size, flags));
}
static inline void *
-kcalloc(size_t n, size_t size, gfp_t flags)
+__kmalloc(size_t size, gfp_t flags)
{
- flags |= __GFP_ZERO;
- return (mallocarray(n, size, M_KMALLOC, linux_check_m_flags(flags)));
+ return (lkpi___kmalloc(size, flags));
}
static inline void *
-kcalloc_node(size_t n, size_t size, gfp_t flags, int node)
+kmalloc_node(size_t size, gfp_t flags, int node)
{
- flags |= __GFP_ZERO;
- return (mallocarray_domainset(n, size, M_KMALLOC,
- linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+ return (lkpi___kmalloc_node(size, flags, node));
}
static inline void *
-__vmalloc(size_t size, gfp_t flags, int other)
+krealloc(void *ptr, size_t size, gfp_t flags)
{
- return (malloc(size, M_KMALLOC, linux_check_m_flags(flags)));
+ return (lkpi_krealloc(ptr, size, flags));
}
-static inline void *
-__vmalloc_node(size_t size, gfp_t flags, int node)
+static inline void
+kfree(const void *ptr)
{
- return (malloc_domainset(size, M_KMALLOC,
- linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+ lkpi_kfree(ptr);
}
-static inline void *
-vmalloc_32(size_t size)
-{
- return (contigmalloc(size, M_KMALLOC, M_WAITOK, 0, UINT_MAX, 1, 1));
-}
+DEFINE_FREE(kfree, void *, if (!IS_ERR_OR_NULL(_T)) kfree(_T))
+/*
+ * Other k*alloc() funtions using the above as underlying allocator.
+ */
+/* kmalloc */
static inline void *
kmalloc_array(size_t n, size_t size, gfp_t flags)
{
- return (mallocarray(n, size, M_KMALLOC, linux_check_m_flags(flags)));
+ if (WOULD_OVERFLOW(n, size))
+ panic("%s: %zu * %zu overflowed", __func__, n, size);
+
+ return (kmalloc(size * n, flags));
}
static inline void *
-kmalloc_array_node(size_t n, size_t size, gfp_t flags, int node)
+kcalloc(size_t n, size_t size, gfp_t flags)
{
- return (mallocarray_domainset(n, size, M_KMALLOC,
- linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+ flags |= __GFP_ZERO;
+ return (kmalloc_array(n, size, flags));
}
+/* kmalloc_node */
static inline void *
-kvmalloc_array(size_t n, size_t size, gfp_t flags)
+kmalloc_array_node(size_t n, size_t size, gfp_t flags, int node)
{
- return (mallocarray(n, size, M_KMALLOC, linux_check_m_flags(flags)));
+ if (WOULD_OVERFLOW(n, size))
+ panic("%s: %zu * %zu overflowed", __func__, n, size);
+
+ return (kmalloc_node(size * n, flags, node));
}
static inline void *
-krealloc(void *ptr, size_t size, gfp_t flags)
+kcalloc_node(size_t n, size_t size, gfp_t flags, int node)
{
- return (realloc(ptr, size, M_KMALLOC, linux_check_m_flags(flags)));
+ flags |= __GFP_ZERO;
+ return (kmalloc_array_node(n, size, flags, node));
}
+/* krealloc */
static inline void *
krealloc_array(void *ptr, size_t n, size_t size, gfp_t flags)
{
- if (WOULD_OVERFLOW(n, size)) {
+ if (WOULD_OVERFLOW(n, size))
return NULL;
- }
- return (realloc(ptr, n * size, M_KMALLOC, linux_check_m_flags(flags)));
+ return (krealloc(ptr, n * size, flags));
}
-extern void linux_kfree_async(void *);
+/*
+ * vmalloc/kvalloc functions.
+ */
+static inline void *
+__vmalloc(size_t size, gfp_t flags, int other)
+{
+ return (malloc(size, M_KMALLOC, linux_check_m_flags(flags)));
+}
-static inline void
-kfree(const void *ptr)
+static inline void *
+__vmalloc_node(size_t size, gfp_t flags, int node)
{
- if (ZERO_OR_NULL_PTR(ptr))
- return;
+ return (malloc_domainset(size, M_KMALLOC,
+ linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+}
- if (curthread->td_critnest != 0)
- linux_kfree_async(__DECONST(void *, ptr));
- else
- free(__DECONST(void *, ptr), M_KMALLOC);
+static inline void *
+vmalloc_32(size_t size)
+{
+ return (contigmalloc(size, M_KMALLOC, M_WAITOK, 0, UINT_MAX, 1, 1));
}
-static __inline void
-kfree_sensitive(const void *ptr)
+/* May return non-contiguous memory. */
+static inline void *
+kvmalloc(size_t size, gfp_t flags)
{
- if (ZERO_OR_NULL_PTR(ptr))
- return;
+ return (lkpi_kvmalloc(size, flags));
+}
- zfree(__DECONST(void *, ptr), M_KMALLOC);
+static inline void *
+kvmalloc_array(size_t n, size_t size, gfp_t flags)
+{
+ if (WOULD_OVERFLOW(n, size))
+ panic("%s: %zu * %zu overflowed", __func__, n, size);
+
+ return (kvmalloc(size * n, flags));
}
static inline void *
@@ -226,6 +259,19 @@ kvrealloc(const void *ptr, size_t oldsize, size_t newsize, gfp_t flags)
return (newptr);
}
+/*
+ * Misc.
+ */
+
+static __inline void
+kfree_sensitive(const void *ptr)
+{
+ if (ZERO_OR_NULL_PTR(ptr))
+ return;
+
+ zfree(__DECONST(void *, ptr), M_KMALLOC);
+}
+
static inline size_t
ksize(const void *ptr)
{
@@ -240,11 +286,4 @@ kmalloc_size_roundup(size_t size)
return (malloc_size(size));
}
-extern struct linux_kmem_cache *linux_kmem_cache_create(const char *name,
- size_t size, size_t align, unsigned flags, linux_kmem_ctor_t *ctor);
-extern void *lkpi_kmem_cache_alloc(struct linux_kmem_cache *, gfp_t);
-extern void *lkpi_kmem_cache_zalloc(struct linux_kmem_cache *, gfp_t);
-extern void lkpi_kmem_cache_free(struct linux_kmem_cache *, void *);
-extern void linux_kmem_cache_destroy(struct linux_kmem_cache *);
-
#endif /* _LINUXKPI_LINUX_SLAB_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 74038f0e7520..903053e7f6e8 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
@@ -43,6 +43,7 @@ struct mtk_wed_device {
do {} while (0)
#define mtk_wed_device_stop(_dev) do { } while(0)
#define mtk_wed_device_start_hw_rro(_dev, _mask, _b) do { } while(0)
+#define mtk_wed_device_setup_tc(_dev, _ndev, _type, _tdata) (-EOPNOTSUPP)
static inline bool
mtk_wed_device_active(struct mtk_wed_device *dev __unused)
diff --git a/sys/compat/linuxkpi/common/include/linux/spinlock.h b/sys/compat/linuxkpi/common/include/linux/spinlock.h
index 2992e41c9c02..dc10b0457153 100644
--- a/sys/compat/linuxkpi/common/include/linux/spinlock.h
+++ b/sys/compat/linuxkpi/common/include/linux/spinlock.h
@@ -154,6 +154,14 @@ typedef struct mtx spinlock_t;
mtx_assert(_l, MA_OWNED); \
} while (0)
+#define local_irq_save(flags) do { \
+ (flags) = 0; \
+} while (0)
+
+#define local_irq_restore(flags) do { \
+ (void)(flags); \
+} while (0)
+
#define atomic_dec_and_lock_irqsave(cnt, lock, flags) \
_atomic_dec_and_lock_irqsave(cnt, lock, &(flags))
static inline int
diff --git a/sys/compat/linuxkpi/common/include/linux/stdarg.h b/sys/compat/linuxkpi/common/include/linux/stdarg.h
index ab2fdf7534e5..698ac45e9198 100644
--- a/sys/compat/linuxkpi/common/include/linux/stdarg.h
+++ b/sys/compat/linuxkpi/common/include/linux/stdarg.h
@@ -28,6 +28,6 @@
#ifndef _LINUXKPI_STDARG_H_
#define _LINUXKPI_STDARG_H_
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#endif /* _LINUXKPI_STDARG_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/stddef.h b/sys/compat/linuxkpi/common/include/linux/stddef.h
index a3bc6b13765e..d04a5a4bf516 100644
--- a/sys/compat/linuxkpi/common/include/linux/stddef.h
+++ b/sys/compat/linuxkpi/common/include/linux/stddef.h
@@ -5,11 +5,27 @@
#include <sys/stddef.h>
-#define struct_group(NAME, ...) \
+/*
+ * FreeBSD has multiple (vendor) drivers containing copies of this
+ * and including LinuxKPI headers. Put the #defines behind guards.
+ */
+
+#ifndef __struct_group
+#define __struct_group(_tag, _name, _attrs, _members...) \
union { \
- struct { __VA_ARGS__ }; \
- struct { __VA_ARGS__ } NAME; \
- }
+ struct { _members } _attrs; \
+ struct _tag { _members } _attrs _name; \
+ } _attrs
+#endif
-#endif /* _LINUXKPI_LINUX_STDDEF_H_ */
+#ifndef struct_group
+#define struct_group(_name, _members...) \
+ __struct_group(/* no tag */, _name, /* no attrs */, _members)
+#endif
+#ifndef struct_group_tagged
+#define struct_group_tagged(_tag, _name, _members...) \
+ __struct_group(_tag, _name, /* no attrs */, _members)
+#endif
+
+#endif /* _LINUXKPI_LINUX_STDDEF_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/string_helpers.h b/sys/compat/linuxkpi/common/include/linux/string_helpers.h
index 1bdff2730361..2c6fe0b1708d 100644
--- a/sys/compat/linuxkpi/common/include/linux/string_helpers.h
+++ b/sys/compat/linuxkpi/common/include/linux/string_helpers.h
@@ -66,4 +66,6 @@ str_enable_disable(bool value)
return "disable";
}
+#define str_disable_enable(_v) str_enable_disable(!(_v))
+
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/swap.h b/sys/compat/linuxkpi/common/include/linux/swap.h
index a353d353cd33..5828db7ae392 100644
--- a/sys/compat/linuxkpi/common/include/linux/swap.h
+++ b/sys/compat/linuxkpi/common/include/linux/swap.h
@@ -37,6 +37,9 @@
#include <vm/swap_pager.h>
#include <vm/vm_pageout.h>
+#include <linux/pagemap.h>
+#include <linux/page-flags.h>
+
static inline long
get_nr_swap_pages(void)
{
@@ -54,4 +57,15 @@ current_is_kswapd(void)
return (curproc == pageproc);
}
+static inline void
+folio_mark_accessed(struct folio *folio)
+{
+ mark_page_accessed(&folio->page);
+}
+
+static inline void
+check_move_unevictable_folios(struct folio_batch *fbatch)
+{
+}
+
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/sysfs.h b/sys/compat/linuxkpi/common/include/linux/sysfs.h
index b5ad73e4460b..470c224a9778 100644
--- a/sys/compat/linuxkpi/common/include/linux/sysfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/sysfs.h
@@ -50,6 +50,15 @@ struct attribute_group {
struct attribute **attrs;
};
+struct bin_attribute {
+ struct attribute attr;
+ size_t size;
+ ssize_t (*read)(struct linux_file *, struct kobject *,
+ struct bin_attribute *, char *, loff_t, size_t);
+ ssize_t (*write)(struct linux_file *, struct kobject *,
+ struct bin_attribute *, char *, loff_t, size_t);
+};
+
#define __ATTR(_name, _mode, _show, _store) { \
.attr = { .name = __stringify(_name), .mode = _mode }, \
.show = _show, .store = _store, \
@@ -72,6 +81,39 @@ struct attribute_group {
NULL, \
}
+#define __BIN_ATTR(_name, _mode, _read, _write, _size) { \
+ .attr = { .name = __stringify(_name), .mode = _mode }, \
+ .read = _read, .write = _write, .size = _size, \
+}
+#define __BIN_ATTR_RO(_name, _size) { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .read = _name##_read, .size = _size, \
+}
+#define __BIN_ATTR_WO(_name, _size) { \
+ .attr = { .name = __stringify(_name), .mode = 0200 }, \
+ .write = _name##_write, .size = _size, \
+}
+#define __BIN_ATTR_WR(_name, _size) { \
+ .attr = { .name = __stringify(_name), .mode = 0644 }, \
+ .read = _name##_read, .write = _name##_write, .size = _size, \
+}
+
+#define BIN_ATTR(_name, _mode, _read, _write, _size) \
+struct bin_attribute bin_attr_##_name = \
+ __BIN_ATTR(_name, _mode, _read, _write, _size);
+
+#define BIN_ATTR_RO(_name, _size) \
+struct bin_attribute bin_attr_##_name = \
+ __BIN_ATTR_RO(_name, _size);
+
+#define BIN_ATTR_WO(_name, _size) \
+struct bin_attribute bin_attr_##_name = \
+ __BIN_ATTR_WO(_name, _size);
+
+#define BIN_ATTR_WR(_name, _size) \
+struct bin_attribute bin_attr_##_name = \
+ __BIN_ATTR_WR(_name, _size);
+
/*
* Handle our generic '\0' terminated 'C' string.
* Two cases:
@@ -147,6 +189,50 @@ sysfs_create_file(struct kobject *kobj, const struct attribute *attr)
return (0);
}
+static inline struct kobject *
+__sysfs_lookup_group(struct kobject *kobj, const char *group)
+{
+ int found;
+ struct sysctl_oid *group_oidp;
+ struct kobject *group_kobj;
+
+ found = 0;
+ if (group != NULL) {
+ SYSCTL_FOREACH(group_oidp, SYSCTL_CHILDREN(kobj->oidp)) {
+ if (strcmp(group_oidp->oid_name, group) != 0)
+ continue;
+ found = 1;
+ break;
+ }
+ } else {
+ found = 1;
+ group_oidp = kobj->oidp;
+ }
+
+ if (!found)
+ return (NULL);
+
+ group_kobj = group_oidp->oid_arg1;
+
+ return (group_kobj);
+}
+
+static inline int
+sysfs_add_file_to_group(struct kobject *kobj,
+ const struct attribute *attr, const char *group)
+{
+ int ret;
+ struct kobject *group_kobj;
+
+ group_kobj = __sysfs_lookup_group(kobj, group);
+ if (group_kobj == NULL)
+ return (-ENOENT);
+
+ ret = sysfs_create_file(group_kobj, attr);
+
+ return (ret);
+}
+
static inline void
sysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
{
@@ -155,6 +241,93 @@ sysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
sysctl_remove_name(kobj->oidp, attr->name, 1, 1);
}
+static inline void
+sysfs_remove_file_from_group(struct kobject *kobj,
+ const struct attribute *attr, const char *group)
+{
+ struct kobject *group_kobj;
+
+ group_kobj = __sysfs_lookup_group(kobj, group);
+ if (group_kobj == NULL)
+ return;
+
+ sysfs_remove_file(group_kobj, attr);
+}
+
+static inline int
+sysctl_handle_bin_attr(SYSCTL_HANDLER_ARGS)
+{
+ struct kobject *kobj;
+ struct bin_attribute *attr;
+ char *buf;
+ int error;
+ ssize_t len;
+
+ kobj = arg1;
+ attr = (struct bin_attribute *)(intptr_t)arg2;
+ if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL)
+ return (ENODEV);
+ buf = (char *)get_zeroed_page(GFP_KERNEL);
+ if (buf == NULL)
+ return (ENOMEM);
+
+ if (attr->read) {
+ len = attr->read(
+ NULL, /* <-- struct file, unimplemented */
+ kobj, attr, buf, req->oldidx, PAGE_SIZE);
+ if (len < 0) {
+ error = -len;
+ if (error != EIO)
+ goto out;
+ }
+ }
+
+ error = sysctl_handle_opaque(oidp, buf, PAGE_SIZE, req);
+ if (error != 0 || req->newptr == NULL || attr->write == NULL)
+ goto out;
+
+ len = attr->write(
+ NULL, /* <-- struct file, unimplemented */
+ kobj, attr, buf, req->newidx, req->newlen);
+ if (len < 0)
+ error = -len;
+out:
+ free_page((unsigned long)buf);
+
+ return (error);
+}
+
+static inline int
+sysfs_create_bin_file(struct kobject *kobj, const struct bin_attribute *attr)
+{
+ struct sysctl_oid *oid;
+ int ctlflags;
+
+ ctlflags = CTLTYPE_OPAQUE | CTLFLAG_MPSAFE;
+ if (attr->attr.mode & (S_IRUSR | S_IWUSR))
+ ctlflags |= CTLFLAG_RW;
+ else if (attr->attr.mode & S_IRUSR)
+ ctlflags |= CTLFLAG_RD;
+ else if (attr->attr.mode & S_IWUSR)
+ ctlflags |= CTLFLAG_WR;
+
+ oid = SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO,
+ attr->attr.name, ctlflags, kobj,
+ (uintptr_t)attr, sysctl_handle_bin_attr, "", "");
+ if (oid == NULL)
+ return (-ENOMEM);
+
+ return (0);
+}
+
+static inline void
+sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr)
+{
+
+ if (kobj->oidp)
+ sysctl_remove_name(kobj->oidp, attr->attr.name, 1, 1);
+}
+
static inline int
sysfs_create_link(struct kobject *kobj __unused,
struct kobject *target __unused, const char *name __unused)
@@ -348,6 +521,24 @@ sysfs_emit_at(char *buf, int at, const char *fmt, ...)
return (i);
}
+static inline int
+_sysfs_match_string(const char * const *a, size_t l, const char *s)
+{
+ const char *p;
+ int i;
+
+ for (i = 0; i < l; i++) {
+ p = a[i];
+ if (p == NULL)
+ break;
+ if (sysfs_streq(p, s))
+ return (i);
+ }
+
+ return (-ENOENT);
+}
+#define sysfs_match_string(a, s) _sysfs_match_string(a, ARRAY_SIZE(a), s)
+
#define sysfs_attr_init(attr) do {} while(0)
#endif /* _LINUXKPI_LINUX_SYSFS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/time.h b/sys/compat/linuxkpi/common/include/linux/time.h
index 6c9a781d5e0e..ca77a20516ff 100644
--- a/sys/compat/linuxkpi/common/include/linux/time.h
+++ b/sys/compat/linuxkpi/common/include/linux/time.h
@@ -42,6 +42,8 @@
#include <linux/math64.h>
+typedef int64_t time64_t;
+
static inline struct timeval
ns_to_timeval(const int64_t nsec)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/timer.h b/sys/compat/linuxkpi/common/include/linux/timer.h
index 8bea082c3e6c..d48939e28a02 100644
--- a/sys/compat/linuxkpi/common/include/linux/timer.h
+++ b/sys/compat/linuxkpi/common/include/linux/timer.h
@@ -42,15 +42,20 @@ struct timer_list {
void (*function_415) (struct timer_list *);
};
unsigned long data;
- int expires;
+ unsigned long expires;
};
extern unsigned long linux_timer_hz_mask;
#define TIMER_IRQSAFE 0x0001
+#if defined(LINUXKPI_VERSION) && (LINUXKPI_VERSION < 61600)
#define from_timer(var, arg, field) \
container_of(arg, typeof(*(var)), field)
+#else
+#define timer_container_of(var, arg, field) \
+ container_of(arg, typeof(*(var)), field)
+#endif
#define timer_setup(timer, func, flags) do { \
CTASSERT(((flags) & ~TIMER_IRQSAFE) == 0); \
@@ -76,17 +81,29 @@ extern unsigned long linux_timer_hz_mask;
callout_init(&(timer)->callout, 1); \
} while (0)
-extern int mod_timer(struct timer_list *, int);
+extern int mod_timer(struct timer_list *, unsigned long);
extern void add_timer(struct timer_list *);
extern void add_timer_on(struct timer_list *, int cpu);
-extern int del_timer(struct timer_list *);
-extern int del_timer_sync(struct timer_list *);
+
+extern int timer_delete(struct timer_list *);
extern int timer_delete_sync(struct timer_list *);
extern int timer_shutdown_sync(struct timer_list *);
+static inline int
+del_timer(struct timer_list *tl)
+{
+ return (timer_delete(tl));
+}
+
+static inline int
+del_timer_sync(struct timer_list *tl)
+{
+ return (timer_delete_sync(tl));
+}
+
#define timer_pending(timer) callout_pending(&(timer)->callout)
#define round_jiffies(j) \
- ((int)(((j) + linux_timer_hz_mask) & ~linux_timer_hz_mask))
+ ((unsigned long)(((j) + linux_timer_hz_mask) & ~linux_timer_hz_mask))
#define round_jiffies_relative(j) round_jiffies(j)
#define round_jiffies_up(j) round_jiffies(j)
#define round_jiffies_up_relative(j) round_jiffies_up(j)
diff --git a/sys/compat/linuxkpi/common/include/linux/topology.h b/sys/compat/linuxkpi/common/include/linux/topology.h
new file mode 100644
index 000000000000..16baffc024d1
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/topology.h
@@ -0,0 +1,35 @@
+/*-
+ * 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_TOPOLOGY_H_
+#define _LINUXKPI_LINUX_TOPOLOGY_H_
+
+#include <asm/topology.h>
+
+#endif /* _LINUXKPI_LINUX_TOPOLOGY_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/types.h b/sys/compat/linuxkpi/common/include/linux/types.h
index b11ce1cdd805..fcc455e5f731 100644
--- a/sys/compat/linuxkpi/common/include/linux/types.h
+++ b/sys/compat/linuxkpi/common/include/linux/types.h
@@ -63,6 +63,7 @@ typedef unsigned gfp_t;
typedef off_t loff_t;
typedef vm_paddr_t resource_size_t;
typedef uint16_t __bitwise__ __sum16;
+typedef uint32_t __bitwise__ __wsum;
typedef unsigned long pgoff_t;
typedef unsigned __poll_t;
diff --git a/sys/compat/linuxkpi/common/include/linux/wait.h b/sys/compat/linuxkpi/common/include/linux/wait.h
index 309c7816aa7b..03ddce2c06f5 100644
--- a/sys/compat/linuxkpi/common/include/linux/wait.h
+++ b/sys/compat/linuxkpi/common/include/linux/wait.h
@@ -61,12 +61,14 @@ typedef struct wait_queue_head wait_queue_head_t;
typedef int wait_queue_func_t(wait_queue_t *, unsigned int, int, void *);
+#define WQ_FLAG_WOKEN 0x02
+
/*
* Many API consumers directly reference these fields and those of
* wait_queue_head.
*/
struct wait_queue {
- unsigned int flags; /* always 0 */
+ unsigned int flags;
void *private;
wait_queue_func_t *func;
union {
@@ -87,8 +89,14 @@ struct wait_queue_head {
* This function is referenced by at least one DRM driver, so it may not be
* renamed and furthermore must be the default wait queue callback.
*/
-extern wait_queue_func_t autoremove_wake_function;
-extern wait_queue_func_t default_wake_function;
+wait_queue_func_t autoremove_wake_function;
+wait_queue_func_t default_wake_function;
+wait_queue_func_t woken_wake_function;
+
+long linux_wait_woken(wait_queue_t *wq, unsigned state, long timeout);
+
+#define wait_woken(wq, state, timeout) \
+ linux_wait_woken((wq), (state), (timeout))
#define DEFINE_WAIT_FUNC(name, function) \
wait_queue_t name = { \
@@ -138,7 +146,7 @@ void linux_wake_up(wait_queue_head_t *, unsigned int, int, bool);
#define wake_up_interruptible_all(wqh) \
linux_wake_up(wqh, TASK_INTERRUPTIBLE, 0, false)
-int linux_wait_event_common(wait_queue_head_t *, wait_queue_t *, int,
+int linux_wait_event_common(wait_queue_head_t *, wait_queue_t *, long,
unsigned int, spinlock_t *);
/*
@@ -148,9 +156,9 @@ int linux_wait_event_common(wait_queue_head_t *, wait_queue_t *, int,
*/
#define __wait_event_common(wqh, cond, timeout, state, lock) ({ \
DEFINE_WAIT(__wq); \
- const int __timeout = ((int)(timeout)) < 1 ? 1 : (timeout); \
- int __start = ticks; \
- int __ret = 0; \
+ const long __timeout = ((long)(timeout)) < 1 ? 1 : (timeout); \
+ long __start = jiffies; \
+ long __ret = 0; \
\
for (;;) { \
linux_prepare_to_wait(&(wqh), &__wq, state); \
@@ -166,7 +174,7 @@ int linux_wait_event_common(wait_queue_head_t *, wait_queue_t *, int,
if (__ret == -EWOULDBLOCK) \
__ret = !!(cond); \
else if (__ret != -ERESTARTSYS) { \
- __ret = __timeout + __start - ticks; \
+ __ret = __timeout + __start - jiffies; \
/* range check return value */ \
if (__ret < 1) \
__ret = 1; \
@@ -284,7 +292,7 @@ void linux_finish_wait(wait_queue_head_t *, wait_queue_t *);
#define finish_wait(wqh, wq) linux_finish_wait(wqh, wq)
void linux_wake_up_bit(void *, int);
-int linux_wait_on_bit_timeout(unsigned long *, int, unsigned int, int);
+int linux_wait_on_bit_timeout(unsigned long *, int, unsigned int, long);
void linux_wake_up_atomic_t(atomic_t *);
int linux_wait_on_atomic_t(atomic_t *, unsigned int);
diff --git a/sys/compat/linuxkpi/common/include/linux/workqueue.h b/sys/compat/linuxkpi/common/include/linux/workqueue.h
index 1c9df9fcb74d..66d3981d4229 100644
--- a/sys/compat/linuxkpi/common/include/linux/workqueue.h
+++ b/sys/compat/linuxkpi/common/include/linux/workqueue.h
@@ -90,7 +90,7 @@ struct delayed_work {
struct {
struct callout callout;
struct mtx mtx;
- int expires;
+ unsigned long expires;
} timer;
};
@@ -245,7 +245,7 @@ extern struct workqueue_struct *linux_create_workqueue_common(const char *, int)
extern void linux_destroy_workqueue(struct workqueue_struct *);
extern bool linux_queue_work_on(int cpu, struct workqueue_struct *, struct work_struct *);
extern bool linux_queue_delayed_work_on(int cpu, struct workqueue_struct *,
- struct delayed_work *, unsigned delay);
+ struct delayed_work *, unsigned long delay);
extern bool linux_cancel_work(struct work_struct *);
extern bool linux_cancel_delayed_work(struct delayed_work *);
extern bool linux_cancel_work_sync(struct work_struct *);
@@ -258,4 +258,10 @@ extern struct work_struct *linux_current_work(void);
extern bool linux_queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rwork);
extern bool linux_flush_rcu_work(struct rcu_work *rwork);
+static inline bool
+queue_work_node(int node __unused, struct workqueue_struct *wq, struct work_struct *work)
+{
+ return (queue_work(wq, work));
+}
+
#endif /* _LINUXKPI_LINUX_WORKQUEUE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h
index 2cdb96ffccdf..239b4a5ae7b8 100644
--- a/sys/compat/linuxkpi/common/include/net/cfg80211.h
+++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h
@@ -36,6 +36,7 @@
#include <linux/mutex.h>
#include <linux/if_ether.h>
#include <linux/ethtool.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/random.h>
@@ -44,6 +45,8 @@
#include <linux/workqueue.h>
#include <net/regulatory.h>
+#include <net80211/ieee80211.h>
+
/* linux_80211.c */
extern int linuxkpi_debug_80211;
#ifndef D80211_TODO
@@ -54,8 +57,8 @@ extern int linuxkpi_debug_80211;
#endif
#define TODO(fmt, ...) if (linuxkpi_debug_80211 & D80211_TODO) \
printf("%s:%d: XXX LKPI80211 TODO " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
-#define IMPROVE(...) if (linuxkpi_debug_80211 & D80211_IMPROVE) \
- printf("%s:%d: XXX LKPI80211 IMPROVE\n", __func__, __LINE__)
+#define IMPROVE(fmt, ...) if (linuxkpi_debug_80211 & D80211_IMPROVE) \
+ printf("%s:%d: XXX LKPI80211 IMPROVE " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
enum rfkill_hard_block_reasons {
RFKILL_HARD_BLOCK_NOT_OWNER = BIT(0),
@@ -125,19 +128,24 @@ struct ieee80211_txrx_stypes {
uint16_t rx;
};
-/* XXX net80211 has an ieee80211_channel as well. */
+/*
+ * net80211 has an ieee80211_channel as well; we use the linuxkpi_ version
+ * interally in LinuxKPI and re-define ieee80211_channel for the drivers
+ * at the end of the file.
+ */
struct linuxkpi_ieee80211_channel {
- /* TODO FIXME */
- uint32_t hw_value; /* ic_ieee */
- uint32_t center_freq; /* ic_freq */
- enum ieee80211_channel_flags flags; /* ic_flags */
+ uint32_t center_freq;
+ uint16_t hw_value;
+ enum ieee80211_channel_flags flags;
enum nl80211_band band;
- int8_t max_power; /* ic_maxpower */
bool beacon_found;
- int max_antenna_gain, max_reg_power;
- int orig_flags;
- int dfs_cac_ms, dfs_state;
- int orig_mpwr;
+ enum nl80211_dfs_state dfs_state;
+ unsigned int dfs_cac_ms;
+ int max_antenna_gain;
+ int max_power;
+ int max_reg_power;
+ uint32_t orig_flags;
+ int orig_mpwr;
};
struct cfg80211_bitrate_mask {
@@ -640,174 +648,6 @@ struct linuxkpi_ieee80211_regdomain {
struct ieee80211_reg_rule reg_rules[];
};
-/* XXX-BZ this are insensible values probably ... */
-#define IEEE80211_HE_MAC_CAP0_HTC_HE 0x1
-#define IEEE80211_HE_MAC_CAP0_TWT_REQ 0x2
-#define IEEE80211_HE_MAC_CAP0_TWT_RES 0x4
-
-#define IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION 0x1
-#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8 0x2
-#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US 0x4
-#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_MASK 0x8
-
-#define IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP 0x1
-#define IEEE80211_HE_MAC_CAP2_ACK_EN 0x2
-#define IEEE80211_HE_MAC_CAP2_BSR 0x4
-#define IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION 0x8
-#define IEEE80211_HE_MAC_CAP2_BCAST_TWT 0x10
-#define IEEE80211_HE_MAC_CAP2_ALL_ACK 0x20
-#define IEEE80211_HE_MAC_CAP2_MU_CASCADING 0x40
-#define IEEE80211_HE_MAC_CAP2_TRS 0x80
-
-#define IEEE80211_HE_MAC_CAP3_OMI_CONTROL 0x02
-#define IEEE80211_HE_MAC_CAP3_OFDMA_RA 0x04
-#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1 0x08
-#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2 0x10
-#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3 0x18
-#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK 0x18
-#define IEEE80211_HE_MAC_CAP3_FLEX_TWT_SCHED 0x40
-#define IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS 0x80
-
-#define IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU 0x1
-#define IEEE80211_HE_MAC_CAP4_BQR 0x2
-#define IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39 0x4
-#define IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU 0x8
-#define IEEE80211_HE_MAC_CAP4_OPS 0x10
-#define IEEE80211_HE_MAC_CAP4_BSRP_BQRP_A_MPDU_AGG 0x20
-
-#define IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS 0x1
-#define IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX 0x2
-#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40 0x4
-#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41 0x8
-#define IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU 0x10
-#define IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX 0x20
-#define IEEE80211_HE_MAC_CAP5_PUNCTURED_SOUNDING 0x40
-#define IEEE80211_HE_MAC_CAP5_SUBCHAN_SELECTIVE_TRANSMISSION 0x80
-
-#define IEEE80211_HE_MCS_NOT_SUPPORTED 0x0
-#define IEEE80211_HE_MCS_SUPPORT_0_7 0x1
-#define IEEE80211_HE_MCS_SUPPORT_0_9 0x2
-#define IEEE80211_HE_MCS_SUPPORT_0_11 0x4
-
-#define IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS 0x01
-#define IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS 0x02
-#define IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START 0x04
-#define IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN 0x08
-#define IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP 0x10
-#define IEEE80211_HE_6GHZ_CAP_SM_PS 0x20
-
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G 0x1
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G 0x2
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G 0x4
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G 0x8
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G 0x10
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G 0x20
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK 0x40
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL 0xff
-
-#define IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A 0x1
-#define IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD 0x2
-#define IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS 0x4
-#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK 0x8
-#define IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US 0x10
-
-#define IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS 0x1
-#define IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US 0x2
-#define IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ 0x4
-#define IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ 0x8
-#define IEEE80211_HE_PHY_CAP2_DOPPLER_TX 0x10
-#define IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO 0x20
-#define IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO 0x40
-
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK 0x1
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_NO_DCM 0x2
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM 0x4
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 0x8
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 0x10
-#define IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER 0x20
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM 0x40
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_16_QAM 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2 0x10
-#define IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU 0x20
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK 0x40
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_2 0x80
-
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8 0x1
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 0x2
-#define IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE 0x4
-#define IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER 0x8
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 0x10
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4 0x20
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK 0x40
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK 0x80
-
-#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2 0x1
-#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 0x2
-#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK 0x4
-#define IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK 0x8
-#define IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK 0x10
-#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK 0x20
-
-#define IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT 0x1
-#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB 0x2
-#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB 0x4
-#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB 0x8
-#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB 0x20
-#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU 0x40
-#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU 0x80
-#define IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE 0x80
-#define IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB 0x80
-#define IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO 0x80
-
-#define IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI 0x1
-#define IEEE80211_HE_PHY_CAP7_MAX_NC_1 0x2
-#define IEEE80211_HE_PHY_CAP7_MAX_NC_2 0x4
-#define IEEE80211_HE_PHY_CAP7_MAX_NC_MASK 0x6
-#define IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR 0x8
-#define IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP 0x10
-#define IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ 0x20
-#define IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ 0x40
-#define IEEE80211_HE_PHY_CAP7_PSR_BASED_SR 0x80
-
-#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU 0x1
-#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G 0x2
-#define IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU 0x4
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242 0x8
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484 0x10
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996 0x18
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996 0x20
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK 0x28
-#define IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI 0x40
-#define IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI 0x80
-
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US 0x1
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US 0x2
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US 0x4
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK 0x8
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED 0x10
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_POS 0x0
-#define IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK 0x20
-#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB 0x4
-#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB 0x8
-#define IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU 0x10
-#define IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU 0x20
-#define IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM 0x40
-
-#define IEEE80211_HE_PHY_CAP10_HE_MU_M1RU_MAX_LTF 0x1
-
-#define IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED 0x1
-#define IEEE80211_HE_OPERATION_BSS_COLOR_OFFSET 0x2
-#define IEEE80211_HE_OPERATION_ER_SU_DISABLE 0x4
-
-#define IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED 0x01
-#define IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED 0x02
-#define IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT 0x04
-#define IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT 0x08
-
#define IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS 0x01
#define IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454 0x02
#define IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK 0x03
@@ -888,33 +728,19 @@ struct linuxkpi_ieee80211_regdomain {
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU 0x04
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY 0x08
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US 0x10
+#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US 0x10
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY 0x20
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US 0x40
+#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US 0x40
#define VENDOR_CMD_RAW_DATA (void *)(uintptr_t)(-ENOENT)
-struct ieee80211_he_cap_elem {
- u8 mac_cap_info[6];
- u8 phy_cap_info[11];
-} __packed;
-
-struct ieee80211_he_mcs_nss_supp {
- /* TODO FIXME */
- uint32_t rx_mcs_80;
- uint32_t tx_mcs_80;
- uint32_t rx_mcs_160;
- uint32_t tx_mcs_160;
- uint32_t rx_mcs_80p80;
- uint32_t tx_mcs_80p80;
-};
-
-#define IEEE80211_STA_HE_CAP_PPE_THRES_MAX 32
+/* net80211::net80211_he_cap */
struct ieee80211_sta_he_cap {
- /* TODO FIXME */
- int has_he;
+ bool has_he;
struct ieee80211_he_cap_elem he_cap_elem;
struct ieee80211_he_mcs_nss_supp he_mcs_nss_supp;
- uint8_t ppe_thres[IEEE80211_STA_HE_CAP_PPE_THRES_MAX];
+ uint8_t ppe_thres[IEEE80211_HE_CAP_PPE_THRES_MAX];
};
struct cfg80211_he_bss_color {
@@ -931,11 +757,6 @@ struct ieee80211_he_obss_pd {
uint8_t partial_bssid_bitmap[8];
};
-struct ieee80211_sta_he_6ghz_capa {
- /* TODO FIXME */
- int capa;
-};
-
struct ieee80211_eht_mcs_nss_supp_20mhz_only {
union {
struct {
@@ -989,7 +810,7 @@ struct ieee80211_sband_iftype_data {
/* TODO FIXME */
enum nl80211_iftype types_mask;
struct ieee80211_sta_he_cap he_cap;
- struct ieee80211_sta_he_6ghz_capa he_6ghz_capa;
+ struct ieee80211_he_6ghz_capa he_6ghz_capa;
struct ieee80211_sta_eht_cap eht_cap;
struct {
const uint8_t *data;
@@ -1182,6 +1003,18 @@ enum cfg80211_regulatory {
REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(6),
};
+struct wiphy_radio_freq_range {
+ uint32_t start_freq;
+ uint32_t end_freq;
+};
+
+struct wiphy_radio {
+ int n_freq_range;
+ int n_iface_combinations;
+ const struct wiphy_radio_freq_range *freq_range;
+ const struct ieee80211_iface_combination *iface_combinations;
+};
+
enum wiphy_flags {
WIPHY_FLAG_AP_UAPSD = BIT(0),
WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(1),
@@ -1246,6 +1079,9 @@ struct wiphy {
uint8_t available_antennas_rx;
uint8_t available_antennas_tx;
+ int n_radio;
+ const struct wiphy_radio *radio;
+
int features, hw_version;
int interface_modes, max_match_sets, max_remain_on_channel_duration, max_scan_ssids, max_sched_scan_ie_len, max_sched_scan_plan_interval, max_sched_scan_plan_iterations, max_sched_scan_plans, max_sched_scan_reqs, max_sched_scan_ssids;
int num_iftype_ext_capab;
@@ -1259,6 +1095,7 @@ struct wiphy {
unsigned long ext_features[BITS_TO_LONGS(NUM_NL80211_EXT_FEATURES)];
struct dentry *debugfsdir;
+
const struct wiphy_wowlan_support *wowlan;
struct cfg80211_wowlan *wowlan_config;
/* Lower layer (driver/mac80211) specific data. */
@@ -1271,8 +1108,9 @@ struct wiphy {
struct wireless_dev {
/* XXX TODO, like ic? */
- int iftype;
- int address;
+ enum nl80211_iftype iftype;
+ uint32_t radio_mask;
+ uint8_t address[ETH_ALEN];
struct net_device *netdev;
struct wiphy *wiphy;
};
@@ -1394,20 +1232,11 @@ wiphy_dev(struct wiphy *wiphy)
return (wiphy->dev);
}
-#define wiphy_dereference(wiphy, p) \
- rcu_dereference_check(p, lockdep_is_held(&(wiphy)->mtx))
+#define wiphy_dereference(_w, p) \
+ rcu_dereference_check(p, lockdep_is_held(&(_w)->mtx))
-static __inline void
-wiphy_lock(struct wiphy *wiphy)
-{
- mutex_lock(&wiphy->mtx);
-}
-
-static __inline void
-wiphy_unlock(struct wiphy *wiphy)
-{
- mutex_unlock(&wiphy->mtx);
-}
+#define wiphy_lock(_w) mutex_lock(&(_w)->mtx)
+#define wiphy_unlock(_w) mutex_unlock(&(_w)->mtx)
static __inline void
wiphy_rfkill_set_hw_state_reason(struct wiphy *wiphy, bool blocked,
@@ -1458,15 +1287,26 @@ rfkill_soft_blocked(int rfkill)
return (false);
}
+static __inline void
+wiphy_rfkill_start_polling(struct wiphy *wiphy)
+{
+ TODO();
+}
+
+static __inline void
+wiphy_rfkill_stop_polling(struct wiphy *wiphy)
+{
+ TODO();
+}
+
static __inline int
reg_query_regdb_wmm(uint8_t *alpha2, uint32_t center_freq,
struct ieee80211_reg_rule *rule)
{
- /* ETSI has special rules. FreeBSD regdb needs to learn about them. */
- TODO();
+ IMPROVE("regdomain.xml needs to grow wmm information for at least ETSI");
- return (-ENXIO);
+ return (-ENODATA);
}
static __inline const u8 *
@@ -1530,6 +1370,43 @@ cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
};
}
+static __inline bool
+cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+cfg80211_chandef_dfs_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef)
+{
+ TODO();
+ return (false);
+}
+
+static __inline unsigned int
+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef)
+{
+ TODO();
+ return (0);
+}
+
+static __inline bool
+cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef_1,
+ const struct cfg80211_chan_def *chandef_2)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+cfg80211_chandef_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef, uint32_t flags)
+{
+ TODO();
+ return (false);
+}
+
static __inline void
cfg80211_bss_iter(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
void (*iterfunc)(struct wiphy *, struct cfg80211_bss *, void *), void *data)
@@ -1994,7 +1871,7 @@ cfg80211_channel_is_psc(struct linuxkpi_ieee80211_channel *channel)
static inline int
cfg80211_get_ies_channel_number(const uint8_t *ie, size_t len,
- enum nl80211_band band, enum cfg80211_bss_frame_type ftype)
+ enum nl80211_band band)
{
const struct element *elem;
@@ -2065,27 +1942,6 @@ cfg80211_find_ext_ie(uint8_t eid, const uint8_t *p, size_t len)
return (NULL);
}
-static __inline bool
-cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
-{
- TODO();
- return (false);
-}
-
-static __inline bool
-cfg80211_chandef_dfs_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef)
-{
- TODO();
- return (false);
-}
-
-static __inline unsigned int
-cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef)
-{
- TODO();
- return (0);
-}
-
static inline void
_ieee80211_set_sband_iftype_data(struct ieee80211_supported_band *band,
struct ieee80211_sband_iftype_data *iftype_data, size_t nitems)
@@ -2177,6 +2033,14 @@ cfg80211_get_iftype_ext_capa(struct wiphy *wiphy, enum nl80211_iftype iftype)
return (NULL);
}
+static inline uint16_t
+ieee80211_get_he_6ghz_capa(const struct ieee80211_supported_band *sband,
+ enum nl80211_iftype iftype)
+{
+ TODO();
+ return (0);
+}
+
static inline int
nl80211_chan_width_to_mhz(enum nl80211_chan_width width)
{
@@ -2207,6 +2071,28 @@ nl80211_chan_width_to_mhz(enum nl80211_chan_width width)
}
}
+static __inline ssize_t
+wiphy_locked_debugfs_read(struct wiphy *wiphy, struct file *file,
+ char *buf, size_t bufsize, const char __user *userbuf, size_t count,
+ loff_t *ppos,
+ ssize_t (*handler)(struct wiphy *, struct file *, char *, size_t, void *),
+ void *data)
+{
+ TODO();
+ return (-ENXIO);
+}
+
+
+static __inline ssize_t
+wiphy_locked_debugfs_write(struct wiphy *wiphy, struct file *file,
+ char *buf, size_t bufsize, const char __user *userbuf, size_t count,
+ ssize_t (*handler)(struct wiphy *, struct file *, char *, size_t, void *),
+ void *data)
+{
+ TODO();
+ return (-ENXIO);
+}
+
/* -------------------------------------------------------------------------- */
static inline void
@@ -2259,9 +2145,9 @@ wiphy_delayed_work_cancel(struct wiphy *wiphy, struct wiphy_delayed_work *wdwk)
#define wiphy_err(_wiphy, _fmt, ...) \
dev_err((_wiphy)->dev, _fmt, __VA_ARGS__)
#define wiphy_info(wiphy, fmt, ...) \
- dev_info(&(wiphy)->dev, fmt, ##__VA_ARGS__)
+ dev_info((wiphy)->dev, fmt, ##__VA_ARGS__)
#define wiphy_info_once(wiphy, fmt, ...) \
- dev_info_once(&(wiphy)->dev, fmt, ##__VA_ARGS__)
+ dev_info_once((wiphy)->dev, fmt, ##__VA_ARGS__)
#ifndef LINUXKPI_NET80211
#define ieee80211_channel linuxkpi_ieee80211_channel
diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index 9830d8f16d05..8de03410c6b6 100644
--- a/sys/compat/linuxkpi/common/include/net/mac80211.h
+++ b/sys/compat/linuxkpi/common/include/net/mac80211.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2020-2025 The FreeBSD Foundation
- * Copyright (c) 2020-2022 Bjoern A. Zeeb
+ * Copyright (c) 2020-2025 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -41,6 +41,7 @@
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <linux/dcache.h>
+#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include <net/if_inet6.h>
@@ -86,6 +87,9 @@ enum mcast_filter_flags {
FIF_PSPOLL = BIT(5),
FIF_CONTROL = BIT(6),
FIF_MCAST_ACTION = BIT(7),
+
+ /* Must stay last. */
+ FIF_FLAGS_MASK = BIT(8)-1,
};
enum ieee80211_bss_changed {
@@ -208,19 +212,6 @@ struct ieee80211_bar {
uint16_t frame_control;
};
-struct ieee80211_p2p_noa_desc {
- uint32_t count; /* uint8_t ? */
- uint32_t duration;
- uint32_t interval;
- uint32_t start_time;
-};
-
-struct ieee80211_p2p_noa_attr {
- uint8_t index;
- uint8_t oppps_ctwindow;
- struct ieee80211_p2p_noa_desc desc[4];
-};
-
struct ieee80211_mutable_offsets {
/* TODO FIXME */
uint16_t tim_offset;
@@ -334,6 +325,7 @@ struct ieee80211_bss_conf {
uint8_t dtim_period;
uint8_t sync_dtim_count;
+ uint8_t bss_param_ch_cnt_link_id;
bool qos;
bool twt_broadcast;
bool use_cts_prot;
@@ -343,6 +335,7 @@ struct ieee80211_bss_conf {
bool eht_support;
bool csa_active;
bool mu_mimo_owner;
+ bool color_change_active;
uint32_t sync_device_ts;
uint64_t sync_tsf;
uint16_t beacon_int;
@@ -363,7 +356,6 @@ struct ieee80211_bss_conf {
int twt_requester, uora_exists, uora_ocw_range;
int assoc_capability, enable_beacon, hidden_ssid, ibss_joined, twt_protected;
int twt_responder, unsol_bcast_probe_resp_interval;
- int color_change_active;
};
struct ieee80211_channel_switch {
@@ -373,18 +365,6 @@ struct ieee80211_channel_switch {
struct cfg80211_chan_def chandef;
};
-struct ieee80211_cipher_scheme {
- uint32_t cipher;
- uint8_t iftype; /* We do not know the size of this. */
- uint8_t hdr_len;
- uint8_t pn_len;
- uint8_t pn_off;
- uint8_t key_idx_off;
- uint8_t key_idx_mask;
- uint8_t key_idx_shift;
- uint8_t mic_len;
-};
-
enum ieee80211_event_type {
BA_FRAME_TIMEOUT,
BAR_RX_EVENT,
@@ -436,11 +416,6 @@ struct ieee80211_ftm_responder_params {
int civicloc_len;
};
-struct ieee80211_he_mu_edca_param_ac_rec {
- /* TODO FIXME */
- int aifsn, ecw_min_max, mu_edca_timer;
-};
-
struct ieee80211_conf {
int dynamic_ps_timeout;
int power_level;
@@ -496,6 +471,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_DISALLOW_PUNCTURING_5GHZ,
IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
IEEE80211_HW_HANDLES_QUIET_CSA,
+ IEEE80211_HW_NO_VIRTUAL_MONITOR,
/* Keep last. */
NUM_IEEE80211_HW_FLAGS
@@ -508,8 +484,6 @@ struct ieee80211_hw {
/* TODO FIXME */
int extra_tx_headroom, weight_multiplier;
int max_rate_tries, max_rates, max_report_rates;
- struct ieee80211_cipher_scheme *cipher_schemes;
- int n_cipher_schemes;
const char *rate_control_algorithm;
struct {
uint16_t units_pos; /* radiotap "spec" is .. inconsistent. */
@@ -555,6 +529,11 @@ enum ieee802111_key_flag {
IEEE80211_KEY_FLAG_SPP_AMSDU = BIT(9),
};
+#define IEEE80211_KEY_FLAG_BITS \
+ "\20\1GENERATE_IV\2GENERATE_MMIC\3PAIRWISE\4PUT_IV_SPACE" \
+ "\5PUT_MIC_SPACE\6SW_MGMT_TX\7GENERATE_IV_MGMT\10GENERATE_MMIE" \
+ "\11RESERVE_TAILROOM\12SPP_AMSDU"
+
struct ieee80211_key_conf {
#if defined(__FreeBSD__)
const struct ieee80211_key *_k; /* backpointer to net80211 */
@@ -728,9 +707,10 @@ struct ieee80211_sta_rates {
/* XXX TODO */
/* XXX some _rcu thing */
struct {
- int idx;
- int flags;
- } rate[1]; /* XXX what is the real number? */
+ uint8_t idx;
+ uint8_t count;
+ uint16_t flags;
+ } rate[4]; /* XXX what is the real number? */
};
struct ieee80211_sta_txpwr {
@@ -754,10 +734,10 @@ struct ieee80211_link_sta {
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
struct ieee80211_sta_he_cap he_cap;
- struct ieee80211_sta_he_6ghz_capa he_6ghz_capa;
+ struct ieee80211_he_6ghz_capa he_6ghz_capa;
struct ieee80211_sta_eht_cap eht_cap;
uint8_t rx_nss;
- enum ieee80211_sta_rx_bw bandwidth;
+ enum ieee80211_sta_rx_bandwidth bandwidth;
enum ieee80211_smps_mode smps_mode;
struct ieee80211_sta_agg agg;
struct ieee80211_sta_txpwr txpwr;
@@ -772,6 +752,7 @@ struct ieee80211_sta {
uint8_t addr[ETH_ALEN];
uint16_t aid;
bool wme;
+ bool mlo;
uint8_t max_sp;
uint8_t uapsd_queues;
uint16_t valid_links;
@@ -823,6 +804,7 @@ enum ieee80211_vif_driver_flags {
#endif
IEEE80211_VIF_EML_ACTIVE = BIT(4),
IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW = BIT(5),
+ IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC = BIT(6),
};
#define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4
@@ -845,15 +827,13 @@ struct ieee80211_vif_cfg {
struct ieee80211_vif {
/* TODO FIXME */
enum nl80211_iftype type;
- int csa_active, mu_mimo_owner;
int cab_queue;
- int color_change_active, offload_flags;
+ int offload_flags;
enum ieee80211_vif_driver_flags driver_flags;
bool p2p;
bool probe_req_reg;
uint8_t addr[ETH_ALEN];
struct ieee80211_vif_cfg cfg;
- struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_txq *txq;
struct ieee80211_bss_conf bss_conf;
struct ieee80211_bss_conf *link_conf[IEEE80211_MLD_MAX_NUM_LINKS]; /* rcu? */
@@ -999,6 +979,7 @@ struct ieee80211_ops {
int (*config)(struct ieee80211_hw *, u32);
void (*reconfig_complete)(struct ieee80211_hw *, enum ieee80211_reconfig_type);
+ void (*prep_add_interface)(struct ieee80211_hw *, enum nl80211_iftype);
int (*add_interface)(struct ieee80211_hw *, struct ieee80211_vif *);
void (*remove_interface)(struct ieee80211_hw *, struct ieee80211_vif *);
int (*change_interface)(struct ieee80211_hw *, struct ieee80211_vif *, enum nl80211_iftype, bool);
@@ -1037,6 +1018,7 @@ struct ieee80211_ops {
int (*sta_state)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, enum ieee80211_sta_state, enum ieee80211_sta_state);
void (*sta_notify)(struct ieee80211_hw *, struct ieee80211_vif *, enum sta_notify_cmd, struct ieee80211_sta *);
void (*sta_rc_update)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, u32);
+ void (*link_sta_rc_update)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_link_sta *, u32);
void (*sta_rate_tbl_update)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *);
void (*sta_set_4addr)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, bool);
void (*sta_set_decap_offload)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, bool);
@@ -1111,7 +1093,7 @@ struct ieee80211_ops {
void (*update_vif_offload)(struct ieee80211_hw *, struct ieee80211_vif *);
- int (*get_txpower)(struct ieee80211_hw *, struct ieee80211_vif *, int *);
+ int (*get_txpower)(struct ieee80211_hw *, struct ieee80211_vif *, unsigned int, int *);
int (*get_stats)(struct ieee80211_hw *, struct ieee80211_low_level_stats *);
int (*set_radar_background)(struct ieee80211_hw *, struct cfg80211_chan_def *);
@@ -1128,6 +1110,8 @@ struct ieee80211_ops {
bool (*can_activate_links)(struct ieee80211_hw *, struct ieee80211_vif *, u16);
enum ieee80211_neg_ttlm_res (*can_neg_ttlm)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_neg_ttlm *);
+ void (*rfkill_poll)(struct ieee80211_hw *);
+
/* #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 *);
@@ -1154,7 +1138,7 @@ extern const struct cfg80211_ops linuxkpi_mac80211cfgops;
struct ieee80211_hw *linuxkpi_ieee80211_alloc_hw(size_t,
const struct ieee80211_ops *);
void linuxkpi_ieee80211_iffree(struct ieee80211_hw *);
-void linuxkpi_set_ieee80211_dev(struct ieee80211_hw *, char *);
+void linuxkpi_set_ieee80211_dev(struct ieee80211_hw *);
int linuxkpi_ieee80211_ifattach(struct ieee80211_hw *);
void linuxkpi_ieee80211_ifdetach(struct ieee80211_hw *);
void linuxkpi_ieee80211_unregister_hw(struct ieee80211_hw *);
@@ -1203,7 +1187,7 @@ struct wireless_dev *linuxkpi_ieee80211_vif_to_wdev(struct ieee80211_vif *);
void linuxkpi_ieee80211_connection_loss(struct ieee80211_vif *);
void linuxkpi_ieee80211_beacon_loss(struct ieee80211_vif *);
struct sk_buff *linuxkpi_ieee80211_probereq_get(struct ieee80211_hw *,
- uint8_t *, uint8_t *, size_t, size_t);
+ const uint8_t *, const uint8_t *, size_t, size_t);
void linuxkpi_ieee80211_tx_status(struct ieee80211_hw *, struct sk_buff *);
void linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *,
struct ieee80211_tx_status *);
@@ -1274,7 +1258,7 @@ SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev)
{
set_wiphy_dev(hw->wiphy, dev);
- linuxkpi_set_ieee80211_dev(hw, dev_name(dev));
+ linuxkpi_set_ieee80211_dev(hw);
IMPROVE();
}
@@ -1468,6 +1452,13 @@ ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
linuxkpi_ieee80211_handle_wake_tx_queue(hw, txq);
}
+static inline void
+ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
+ struct sk_buff_head *skbs)
+{
+ TODO();
+}
+
/* -------------------------------------------------------------------------- */
static __inline uint8_t
@@ -1721,26 +1712,6 @@ ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, const uint8_t *addr,
return (linuxkpi_ieee80211_find_sta_by_ifaddr(hw, addr, ourvifaddr));
}
-
-static __inline void
-ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
- struct sk_buff *skb_frag, u8 *key)
-{
- TODO();
-}
-
-static __inline void
-ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf,
- const u8 *addr, uint32_t iv32, u16 *p1k)
-{
-
- KASSERT(keyconf != NULL && addr != NULL && p1k != NULL,
- ("%s: keyconf %p addr %p p1k %p\n", __func__, keyconf, addr, p1k));
-
- TODO();
- memset(p1k, 0xfa, 5 * sizeof(*p1k)); /* Just initializing. */
-}
-
static __inline size_t
ieee80211_ie_split(const u8 *ies, size_t ies_len,
const u8 *ie_ids, size_t ie_ids_len, size_t start)
@@ -1773,12 +1744,15 @@ ieee80211_request_smps(struct ieee80211_vif *vif, u_int link_id,
"SMPS_STATIC",
"SMPS_DYNAMIC",
"SMPS_AUTOMATIC",
- "SMPS_NUM_MODES"
};
- if (linuxkpi_debug_80211 & D80211_TODO)
- printf("%s:%d: XXX LKPI80211 TODO smps %d %s\n",
- __func__, __LINE__, smps, smps_mode_name[smps]);
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (smps >= nitems(smps_mode_name))
+ panic("%s: unsupported smps value: %d\n", __func__, smps);
+
+ IMPROVE("XXX LKPI80211 TODO smps %d %s\n", smps, smps_mode_name[smps]);
}
static __inline void
@@ -1906,13 +1880,13 @@ ieee80211_rate_set_vht(struct ieee80211_tx_rate *r, uint8_t mcs, uint8_t nss)
}
static inline uint8_t
-ieee80211_rate_get_vht_nss(struct ieee80211_tx_rate *r)
+ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *r)
{
return (((r->idx >> 4) & 0x07) + 1);
}
static inline uint8_t
-ieee80211_rate_get_vht_mcs(struct ieee80211_tx_rate *r)
+ieee80211_rate_get_vht_mcs(const struct ieee80211_tx_rate *r)
{
return (r->idx & 0x0f);
}
@@ -2068,13 +2042,6 @@ ieee80211_sta_uapsd_trigger(struct ieee80211_sta *sta, int ntids)
TODO();
}
-static __inline void
-ieee80211_tkip_add_iv(u8 *crypto_hdr, struct ieee80211_key_conf *keyconf,
- uint64_t pn)
-{
- TODO();
-}
-
static inline struct sk_buff *
ieee80211_tx_dequeue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
@@ -2200,8 +2167,8 @@ ieee80211_nullfunc_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
static __inline struct sk_buff *
-ieee80211_probereq_get(struct ieee80211_hw *hw, uint8_t *addr,
- uint8_t *ssid, size_t ssid_len, size_t tailroom)
+ieee80211_probereq_get(struct ieee80211_hw *hw, const uint8_t *addr,
+ const uint8_t *ssid, size_t ssid_len, size_t tailroom)
{
return (linuxkpi_ieee80211_probereq_get(hw, addr, ssid, ssid_len,
@@ -2223,12 +2190,27 @@ ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *w)
linuxkpi_ieee80211_queue_work(hw, w);
}
+static __inline bool
+ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct sk_buff *skb, enum nl80211_band band, struct ieee80211_sta **sta)
+{
+ TODO();
+ return (false);
+}
+
static __inline void
ieee80211_tx_status_skb(struct ieee80211_hw *hw, struct sk_buff *skb)
{
linuxkpi_ieee80211_tx_status(hw, skb);
}
+static inline void
+ieee80211_tx_status_noskb(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ struct ieee80211_tx_info *info)
+{
+ TODO();
+}
+
static __inline void
ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)
{
@@ -2310,7 +2292,8 @@ ieee80211_txq_may_transmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
}
static __inline void
-ieee80211_radar_detected(struct ieee80211_hw *hw)
+ieee80211_radar_detected(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf)
{
TODO();
}
@@ -2349,19 +2332,7 @@ ieee80211_disconnect(struct ieee80211_vif *vif, bool _x)
}
static __inline void
-ieee80211_channel_switch_disconnect(struct ieee80211_vif *vif, bool _x)
-{
- TODO();
-}
-
-static __inline void
-ieee80211_key_mic_failure(struct ieee80211_key_conf *key)
-{
- TODO();
-}
-
-static __inline void
-ieee80211_key_replay(struct ieee80211_key_conf *key)
+ieee80211_channel_switch_disconnect(struct ieee80211_vif *vif)
{
TODO();
}
@@ -2382,7 +2353,7 @@ ieee80211_get_tx_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
}
static __inline void
-ieee80211_color_change_finish(struct ieee80211_vif *vif)
+ieee80211_color_change_finish(struct ieee80211_vif *vif, uint8_t link_id)
{
TODO();
}
@@ -2424,9 +2395,22 @@ ieee80211_data_to_8023(struct sk_buff *skb, const uint8_t *addr,
return (-1);
}
+/* -------------------------------------------------------------------------- */
+
static __inline void
-ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *key,
- uint32_t iv32, uint16_t *p1k)
+ieee80211_key_mic_failure(struct ieee80211_key_conf *key)
+{
+ TODO();
+}
+
+static __inline void
+ieee80211_key_replay(struct ieee80211_key_conf *key)
+{
+ TODO();
+}
+
+static __inline void
+ieee80211_remove_key(struct ieee80211_key_conf *key)
{
TODO();
}
@@ -2447,11 +2431,38 @@ ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const uint8_t *bssid,
}
static __inline void
-ieee80211_remove_key(struct ieee80211_key_conf *key)
+ieee80211_tkip_add_iv(u8 *crypto_hdr, struct ieee80211_key_conf *keyconf,
+ uint64_t pn)
+{
+ TODO();
+}
+
+static __inline void
+ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf,
+ const u8 *addr, uint32_t iv32, u16 *p1k)
+{
+
+ KASSERT(keyconf != NULL && addr != NULL && p1k != NULL,
+ ("%s: keyconf %p addr %p p1k %p\n", __func__, keyconf, addr, p1k));
+
+ TODO();
+ memset(p1k, 0xfa, 5 * sizeof(*p1k)); /* Just initializing. */
+}
+
+static __inline void
+ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *key,
+ uint32_t iv32, uint16_t *p1k)
{
TODO();
}
+static __inline void
+ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
+ struct sk_buff *skb_frag, u8 *key)
+{
+ TODO();
+}
+
static inline void
ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, int8_t tid,
struct ieee80211_key_seq *seq)
@@ -2461,28 +2472,46 @@ ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, int8_t tid,
KASSERT(keyconf != NULL && seq != NULL, ("%s: keyconf %p seq %p\n",
__func__, keyconf, seq));
- KASSERT(tid <= IEEE80211_NUM_TIDS, ("%s: tid out of bounds %d\n",
- __func__, tid));
k = keyconf->_k;
KASSERT(k != NULL, ("%s: keyconf %p ieee80211_key is NULL\n", __func__, keyconf));
switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (tid < 0 || tid >= IEEE80211_NUM_TIDS)
+ return;
+ /* See net80211::tkip_decrypt() */
+ seq->tkip.iv32 = TKIP_PN_TO_IV32(k->wk_keyrsc[tid]);
+ seq->tkip.iv16 = TKIP_PN_TO_IV16(k->wk_keyrsc[tid]);
+ break;
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_CCMP_256:
- if (tid < 0)
+ if (tid < -1 || tid >= IEEE80211_NUM_TIDS)
+ return;
+ if (tid == -1)
p = (const uint8_t *)&k->wk_keyrsc[IEEE80211_NUM_TIDS]; /* IEEE80211_NONQOS_TID */
else
p = (const uint8_t *)&k->wk_keyrsc[tid];
memcpy(seq->ccmp.pn, p, sizeof(seq->ccmp.pn));
break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ if (tid < -1 || tid >= IEEE80211_NUM_TIDS)
+ return;
+ if (tid == -1)
+ p = (const uint8_t *)&k->wk_keyrsc[IEEE80211_NUM_TIDS]; /* IEEE80211_NONQOS_TID */
+ else
+ p = (const uint8_t *)&k->wk_keyrsc[tid];
+ memcpy(seq->gcmp.pn, p, sizeof(seq->gcmp.pn));
+ break;
case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
TODO();
memset(seq->aes_cmac.pn, 0xfa, sizeof(seq->aes_cmac.pn)); /* XXX TODO */
break;
- case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
TODO();
- seq->tkip.iv32 = 0xfa; /* XXX TODO */
- seq->tkip.iv16 = 0xfa; /* XXX TODO */
+ memset(seq->aes_gmac.pn, 0xfa, sizeof(seq->aes_gmac.pn)); /* XXX TODO */
break;
default:
pr_debug("%s: unsupported cipher suite %d\n", __func__, keyconf->cipher);
@@ -2497,6 +2526,8 @@ ieee80211_set_key_rx_seq(struct ieee80211_key_conf *key, int tid,
TODO();
}
+/* -------------------------------------------------------------------------- */
+
static __inline void
ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
struct cfg80211_wowlan_wakeup *wakeup, gfp_t gfp)
diff --git a/sys/compat/linuxkpi/common/include/stdarg.h b/sys/compat/linuxkpi/common/include/stdarg.h
index ab2fdf7534e5..698ac45e9198 100644
--- a/sys/compat/linuxkpi/common/include/stdarg.h
+++ b/sys/compat/linuxkpi/common/include/stdarg.h
@@ -28,6 +28,6 @@
#ifndef _LINUXKPI_STDARG_H_
#define _LINUXKPI_STDARG_H_
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#endif /* _LINUXKPI_STDARG_H_ */
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index ec1798d2e886..d00734001a59 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -77,6 +77,8 @@
#include <linux/rculist.h>
#include "linux_80211.h"
+/* #define LKPI_80211_USE_SCANLIST */
+/* #define LKPI_80211_BGSCAN */
#define LKPI_80211_WME
#define LKPI_80211_HW_CRYPTO
#define LKPI_80211_HT
@@ -103,10 +105,18 @@ SYSCTL_DECL(_compat_linuxkpi);
SYSCTL_NODE(_compat_linuxkpi, OID_AUTO, 80211, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"LinuxKPI 802.11 compatibility layer");
+static bool lkpi_order_scanlist = false;
+SYSCTL_BOOL(_compat_linuxkpi_80211, OID_AUTO, order_scanlist, CTLFLAG_RW,
+ &lkpi_order_scanlist, 0, "Enable LinuxKPI 802.11 scan list shuffeling");
+
#if defined(LKPI_80211_HW_CRYPTO)
static bool lkpi_hwcrypto = false;
SYSCTL_BOOL(_compat_linuxkpi_80211, OID_AUTO, hw_crypto, CTLFLAG_RDTUN,
&lkpi_hwcrypto, 0, "Enable LinuxKPI 802.11 hardware crypto offload");
+
+static bool lkpi_hwcrypto_tkip = false;
+SYSCTL_BOOL(_compat_linuxkpi_80211, OID_AUTO, tkip, CTLFLAG_RDTUN,
+ &lkpi_hwcrypto_tkip, 0, "Enable LinuxKPI 802.11 TKIP crypto offload");
#endif
/* Keep public for as long as header files are using it too. */
@@ -163,6 +173,7 @@ const struct cfg80211_ops linuxkpi_mac80211cfgops = {
static struct lkpi_sta *lkpi_find_lsta_by_ni(struct lkpi_vif *,
struct ieee80211_node *);
#endif
+static void lkpi_sw_scan_task(void *, int);
static void lkpi_80211_txq_tx_one(struct lkpi_sta *, struct mbuf *);
static void lkpi_80211_txq_task(void *, int);
static void lkpi_80211_lhw_rxq_task(void *, int);
@@ -170,6 +181,7 @@ static void lkpi_ieee80211_free_skb_mbuf(void *);
#ifdef LKPI_80211_WME
static int lkpi_wme_update(struct lkpi_hw *, struct ieee80211vap *, bool);
#endif
+static void lkpi_ieee80211_wake_queues_locked(struct ieee80211_hw *);
static const char *
lkpi_rate_info_bw_to_str(enum rate_info_bw bw)
@@ -269,95 +281,119 @@ lkpi_nl80211_sta_info_to_str(struct sbuf *s, const char *prefix,
sbuf_printf(s, "\n");
}
-static int
-lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS)
+static void
+lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s)
{
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
struct ieee80211vap *vap;
- struct lkpi_vif *lvif;
struct ieee80211_vif *vif;
struct lkpi_sta *lsta;
struct ieee80211_sta *sta;
struct station_info sinfo;
- struct sbuf s;
int error;
- if (req->newptr)
- return (EPERM);
-
- lvif = (struct lkpi_vif *)arg1;
vif = LVIF_TO_VIF(lvif);
vap = LVIF_TO_VAP(lvif);
lhw = vap->iv_ic->ic_softc;
hw = LHW_TO_HW(lhw);
- sbuf_new_for_sysctl(&s, NULL, 1024, req);
-
wiphy_lock(hw->wiphy);
list_for_each_entry(lsta, &lvif->lsta_list, lsta_list) {
sta = LSTA_TO_STA(lsta);
- sbuf_putc(&s, '\n');
- sbuf_printf(&s, "lsta %p sta %p added_to_drv %d\n", lsta, sta, lsta->added_to_drv);
+ sbuf_putc(s, '\n');
+ sbuf_printf(s, "lsta %p sta %p added_to_drv %d\n", lsta, sta, lsta->added_to_drv);
memset(&sinfo, 0, sizeof(sinfo));
error = lkpi_80211_mo_sta_statistics(hw, vif, sta, &sinfo);
if (error == EEXIST) /* Not added to driver. */
continue;
if (error == ENOTSUPP) {
- sbuf_printf(&s, " sta_statistics not supported\n");
+ sbuf_printf(s, " sta_statistics not supported\n");
continue;
}
if (error != 0) {
- sbuf_printf(&s, " sta_statistics failed: %d\n", error);
+ sbuf_printf(s, " sta_statistics failed: %d\n", error);
continue;
}
- lkpi_nl80211_sta_info_to_str(&s, " nl80211_sta_info (valid fields)", sinfo.filled);
- sbuf_printf(&s, " connected_time %u inactive_time %u\n",
+ /* If no RX_BITRATE is reported, try to fill it in from the lsta sinfo. */
+ if ((sinfo.filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) == 0 &&
+ (lsta->sinfo.filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) != 0) {
+ memcpy(&sinfo.rxrate, &lsta->sinfo.rxrate, sizeof(sinfo.rxrate));
+ sinfo.filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
+ }
+ /* If no CHAIN_SIGNAL is reported, try to fill it in from the lsta sinfo. */
+ if ((sinfo.filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) == 0 &&
+ (lsta->sinfo.filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) != 0) {
+ sinfo.chains = lsta->sinfo.chains;
+ memcpy(sinfo.chain_signal, lsta->sinfo.chain_signal,
+ sizeof(sinfo.chain_signal));
+ sinfo.filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
+ }
+
+ lkpi_nl80211_sta_info_to_str(s, " nl80211_sta_info (valid fields)", sinfo.filled);
+ sbuf_printf(s, " connected_time %u inactive_time %u\n",
sinfo.connected_time, sinfo.inactive_time);
- sbuf_printf(&s, " rx_bytes %ju rx_packets %u rx_dropped_misc %u\n",
+ sbuf_printf(s, " rx_bytes %ju rx_packets %u rx_dropped_misc %u\n",
(uintmax_t)sinfo.rx_bytes, sinfo.rx_packets, sinfo.rx_dropped_misc);
- sbuf_printf(&s, " rx_duration %ju rx_beacon %u rx_beacon_signal_avg %d\n",
+ sbuf_printf(s, " rx_duration %ju rx_beacon %u rx_beacon_signal_avg %d\n",
(uintmax_t)sinfo.rx_duration, sinfo.rx_beacon, (int8_t)sinfo.rx_beacon_signal_avg);
- sbuf_printf(&s, " tx_bytes %ju tx_packets %u tx_failed %u\n",
+ sbuf_printf(s, " tx_bytes %ju tx_packets %u tx_failed %u\n",
(uintmax_t)sinfo.tx_bytes, sinfo.tx_packets, sinfo.tx_failed);
- sbuf_printf(&s, " tx_duration %ju tx_retries %u\n",
+ sbuf_printf(s, " tx_duration %ju tx_retries %u\n",
(uintmax_t)sinfo.tx_duration, sinfo.tx_retries);
- sbuf_printf(&s, " signal %d signal_avg %d ack_signal %d avg_ack_signal %d\n",
+ sbuf_printf(s, " signal %d signal_avg %d ack_signal %d avg_ack_signal %d\n",
sinfo.signal, sinfo.signal_avg, sinfo.ack_signal, sinfo.avg_ack_signal);
-
- sbuf_printf(&s, " generation %d assoc_req_ies_len %zu chains %d\n",
+ sbuf_printf(s, " generation %d assoc_req_ies_len %zu chains %#04x\n",
sinfo.generation, sinfo.assoc_req_ies_len, sinfo.chains);
- for (int i = 0; i < sinfo.chains && i < IEEE80211_MAX_CHAINS; i++) {
- sbuf_printf(&s, " chain[%d] signal %d signal_avg %d\n",
+ for (int i = 0; i < nitems(sinfo.chain_signal) && i < IEEE80211_MAX_CHAINS; i++) {
+ if (!(sinfo.chains & BIT(i)))
+ continue;
+ sbuf_printf(s, " chain[%d] signal %d signal_avg %d\n",
i, (int8_t)sinfo.chain_signal[i], (int8_t)sinfo.chain_signal_avg[i]);
}
/* assoc_req_ies, bss_param, sta_flags */
- sbuf_printf(&s, " rxrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
+ sbuf_printf(s, " rxrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
sinfo.rxrate.flags, CFG80211_RATE_INFO_FLAGS_BITS,
sinfo.rxrate.bw, lkpi_rate_info_bw_to_str(sinfo.rxrate.bw),
sinfo.rxrate.legacy * 100,
sinfo.rxrate.mcs, sinfo.rxrate.nss);
- sbuf_printf(&s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
+ sbuf_printf(s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
sinfo.rxrate.he_dcm, sinfo.rxrate.he_gi, sinfo.rxrate.he_ru_alloc,
sinfo.rxrate.eht_gi);
- sbuf_printf(&s, " txrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
+ sbuf_printf(s, " txrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
sinfo.txrate.flags, CFG80211_RATE_INFO_FLAGS_BITS,
sinfo.txrate.bw, lkpi_rate_info_bw_to_str(sinfo.txrate.bw),
sinfo.txrate.legacy * 100,
sinfo.txrate.mcs, sinfo.txrate.nss);
- sbuf_printf(&s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
+ sbuf_printf(s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
sinfo.txrate.he_dcm, sinfo.txrate.he_gi, sinfo.txrate.he_ru_alloc,
sinfo.txrate.eht_gi);
}
wiphy_unlock(hw->wiphy);
+}
+
+static int
+lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS)
+{
+ struct lkpi_vif *lvif;
+ struct sbuf s;
+
+ if (req->newptr)
+ return (EPERM);
+
+ lvif = (struct lkpi_vif *)arg1;
+
+ sbuf_new_for_sysctl(&s, NULL, 1024, req);
+
+ lkpi_80211_dump_lvif_stas(lvif, &s);
sbuf_finish(&s);
sbuf_delete(&s);
@@ -365,9 +401,81 @@ lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS)
return (0);
}
+static enum ieee80211_sta_rx_bandwidth
+lkpi_cw_to_rx_bw(enum nl80211_chan_width cw)
+{
+ switch (cw) {
+ case NL80211_CHAN_WIDTH_320:
+ return (IEEE80211_STA_RX_BW_320);
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_80P80:
+ return (IEEE80211_STA_RX_BW_160);
+ case NL80211_CHAN_WIDTH_80:
+ return (IEEE80211_STA_RX_BW_80);
+ case NL80211_CHAN_WIDTH_40:
+ return (IEEE80211_STA_RX_BW_40);
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ return (IEEE80211_STA_RX_BW_20);
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ /* Unsupported input. */
+ return (IEEE80211_STA_RX_BW_20);
+ }
+}
+
+static enum nl80211_chan_width
+lkpi_rx_bw_to_cw(enum ieee80211_sta_rx_bandwidth rx_bw)
+{
+ switch (rx_bw) {
+ case IEEE80211_STA_RX_BW_20:
+ return (NL80211_CHAN_WIDTH_20); /* _NOHT */
+ case IEEE80211_STA_RX_BW_40:
+ return (NL80211_CHAN_WIDTH_40);
+ case IEEE80211_STA_RX_BW_80:
+ return (NL80211_CHAN_WIDTH_80);
+ case IEEE80211_STA_RX_BW_160:
+ return (NL80211_CHAN_WIDTH_160); /* 80P80 */
+ case IEEE80211_STA_RX_BW_320:
+ return (NL80211_CHAN_WIDTH_320);
+ }
+}
+
+static void
+lkpi_sync_chanctx_cw_from_rx_bw(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_sta_rx_bandwidth old_bw;
+ uint32_t changed;
+
+ chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf,
+ lockdep_is_held(&hw->wiphy->mtx));
+ if (chanctx_conf == NULL)
+ return;
+
+ old_bw = lkpi_cw_to_rx_bw(chanctx_conf->def.width);
+ if (old_bw == sta->deflink.bandwidth)
+ return;
+
+ chanctx_conf->def.width = lkpi_rx_bw_to_cw(sta->deflink.bandwidth);
+ if (chanctx_conf->def.width == NL80211_CHAN_WIDTH_20 &&
+ !sta->deflink.ht_cap.ht_supported)
+ chanctx_conf->def.width = NL80211_CHAN_WIDTH_20_NOHT;
+
+ chanctx_conf->min_def = chanctx_conf->def;
+
+ vif->bss_conf.chanreq.oper.width = chanctx_conf->def.width;
+
+ changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
+ changed |= IEEE80211_CHANCTX_CHANGE_WIDTH;
+ lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed);
+}
+
#if defined(LKPI_80211_HT)
static void
-lkpi_sta_sync_ht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni)
+lkpi_sta_sync_ht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_node *ni)
{
struct ieee80211vap *vap;
uint8_t *ie;
@@ -399,11 +507,6 @@ lkpi_sta_sync_ht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni)
sta->deflink.ht_cap.cap = htcap->cap_info;
sta->deflink.ht_cap.mcs = htcap->mcs;
- if ((sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) != 0)
- sta->deflink.bandwidth = IEEE80211_STA_RX_BW_40;
- else
- sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20;
-
/*
* 802.11n-2009 20.6 Parameters for HT MCSs gives the mandatory/
* optional MCS for Nss=1..4. We need to check the first four
@@ -412,11 +515,21 @@ lkpi_sta_sync_ht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni)
*/
rx_nss = 0;
for (i = 0; i < 4; i++) {
- if (htcap->mcs.rx_mask[i])
+ if (htcap->mcs.rx_mask[i] != 0)
rx_nss++;
}
- if (rx_nss > 0)
+ if (rx_nss > 0) {
sta->deflink.rx_nss = rx_nss;
+ } else {
+ sta->deflink.ht_cap.ht_supported = false;
+ return;
+ }
+
+ if ((sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) != 0 &&
+ IEEE80211_IS_CHAN_HT40(ni->ni_chan))
+ sta->deflink.bandwidth = IEEE80211_STA_RX_BW_40;
+ else
+ sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20;
IMPROVE("sta->wme");
@@ -435,14 +548,17 @@ lkpi_sta_sync_ht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni)
#if defined(LKPI_80211_VHT)
static void
-lkpi_sta_sync_vht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni)
+lkpi_sta_sync_vht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_node *ni)
{
+ enum ieee80211_sta_rx_bandwidth bw;
uint32_t width;
int rx_nss;
uint16_t rx_mcs_map;
uint8_t mcs;
- if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0) {
+ if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0 ||
+ !IEEE80211_IS_CHAN_VHT_5GHZ(ni->ni_chan)) {
sta->deflink.vht_cap.vht_supported = false;
return;
}
@@ -459,23 +575,38 @@ lkpi_sta_sync_vht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni)
if (ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_USE_HT)
goto skip_bw;
+ bw = sta->deflink.bandwidth;
width = (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
switch (width) {
-#if 0
+ /* Deprecated. */
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
- sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160;
+ bw = IEEE80211_STA_RX_BW_160;
break;
-#endif
default:
/* Check if we do support 160Mhz somehow after all. */
-#if 0
if ((sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) != 0)
- sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160;
+ bw = IEEE80211_STA_RX_BW_160;
else
-#endif
- sta->deflink.bandwidth = IEEE80211_STA_RX_BW_80;
+ bw = IEEE80211_STA_RX_BW_80;
}
+ /*
+ * While we can set what is possibly supported we also need to be
+ * on a channel which supports that bandwidth; e.g., we can support
+ * VHT160 but the AP only does VHT80.
+ * Further ni_chan will also have filtered out what we disabled
+ * by configuration.
+ * Once net80211 channel selection is fixed for 802.11-2020 and
+ * VHT160 we can possibly spare ourselves the above.
+ */
+ if (bw == IEEE80211_STA_RX_BW_160 &&
+ !IEEE80211_IS_CHAN_VHT160(ni->ni_chan) &&
+ !IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan))
+ bw = IEEE80211_STA_RX_BW_80;
+ if (bw == IEEE80211_STA_RX_BW_80 &&
+ !IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
+ bw = sta->deflink.bandwidth;
+ sta->deflink.bandwidth = bw;
skip_bw:
rx_nss = 0;
@@ -507,15 +638,32 @@ skip_bw:
#endif
static void
-lkpi_sta_sync_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni)
+lkpi_sta_sync_from_ni(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct ieee80211_node *ni, bool updchnctx)
{
#if defined(LKPI_80211_HT)
- lkpi_sta_sync_ht_from_ni(sta, ni);
+ lkpi_sta_sync_ht_from_ni(vif, sta, ni);
#endif
#if defined(LKPI_80211_VHT)
- lkpi_sta_sync_vht_from_ni(sta, ni);
+ lkpi_sta_sync_vht_from_ni(vif, sta, ni);
#endif
+
+ /*
+ * Ensure rx_nss is at least 1 as otherwise drivers run into
+ * unexpected problems.
+ */
+ sta->deflink.rx_nss = MAX(1, sta->deflink.rx_nss);
+
+ /*
+ * We are also called from node allocation which net80211
+ * can do even on `ifconfig down`; in that case the chanctx
+ * may still be valid and we get a discrepancy between
+ * sta and chanctx. Thus do not try to update the chanctx
+ * when called from lkpi_lsta_alloc().
+ */
+ if (updchnctx)
+ lkpi_sync_chanctx_cw_from_rx_bw(hw, vif, sta);
}
static uint8_t
@@ -570,12 +718,11 @@ static void
lkpi_lsta_remove(struct lkpi_sta *lsta, struct lkpi_vif *lvif)
{
+ lockdep_assert_wiphy(lsta->hw->wiphy);
- wiphy_lock(lsta->hw->wiphy);
KASSERT(!list_empty(&lsta->lsta_list),
("%s: lsta %p ni %p\n", __func__, lsta, lsta->ni));
list_del_init(&lsta->lsta_list);
- wiphy_unlock(lsta->hw->wiphy);
}
static struct lkpi_sta *
@@ -686,7 +833,7 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20;
sta->deflink.rx_nss = 1;
- lkpi_sta_sync_from_ni(sta, ni);
+ lkpi_sta_sync_from_ni(hw, vif, sta, ni, false);
IMPROVE("he, eht, bw_320, ... smps_mode, ..");
@@ -696,11 +843,13 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
for (i = 1; i < nitems(sta->link); i++) {
IMPROVE("more links; only link[0] = deflink currently.");
}
+ IMPROVE("11be");
+ sta->mlo = false;
/* Deferred TX path. */
LKPI_80211_LSTA_TXQ_LOCK_INIT(lsta);
TASK_INIT(&lsta->txq_task, 0, lkpi_80211_txq_task, lsta);
- mbufq_init(&lsta->txq, IFQ_MAXLEN);
+ mbufq_init(&lsta->txq, 32 * NAPI_POLL_WEIGHT);
lsta->txq_ready = true;
return (lsta);
@@ -813,6 +962,30 @@ lkpi_nl80211_band_to_net80211_band(enum nl80211_band band)
return (0x00);
}
+#ifdef LINUXKPI_DEBUG_80211
+static const char *
+lkpi_nl80211_band_name(enum nl80211_band band)
+{
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ return "2Ghz";
+ break;
+ case NL80211_BAND_5GHZ:
+ return "5Ghz";
+ break;
+ case NL80211_BAND_60GHZ:
+ return "60Ghz";
+ break;
+ case NL80211_BAND_6GHZ:
+ return "6Ghz";
+ break;
+ default:
+ panic("%s: unsupported band %u\n", __func__, band);
+ break;
+ }
+}
+#endif
+
#if 0
static enum ieee80211_ac_numbers
lkpi_ac_net_to_l80211(int ac)
@@ -870,68 +1043,68 @@ lkpi_opmode_to_vif_type(enum ieee80211_opmode opmode)
static const char *
lkpi_cipher_suite_to_name(uint32_t wlan_cipher_suite)
{
-
switch (wlan_cipher_suite) {
case WLAN_CIPHER_SUITE_WEP40:
return ("WEP40");
+ case WLAN_CIPHER_SUITE_WEP104:
+ return ("WEP104");
case WLAN_CIPHER_SUITE_TKIP:
return ("TKIP");
case WLAN_CIPHER_SUITE_CCMP:
return ("CCMP");
- case WLAN_CIPHER_SUITE_WEP104:
- return ("WEP104");
- case WLAN_CIPHER_SUITE_AES_CMAC:
- return ("AES_CMAC");
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ return ("CCMP_256");
case WLAN_CIPHER_SUITE_GCMP:
return ("GCMP");
case WLAN_CIPHER_SUITE_GCMP_256:
return ("GCMP_256");
- case WLAN_CIPHER_SUITE_CCMP_256:
- return ("CCMP_256");
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ return ("AES_CMAC");
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ return ("BIP_CMAC_256");
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
return ("BIP_GMAC_128");
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
return ("BIP_GMAC_256");
- case WLAN_CIPHER_SUITE_BIP_CMAC_256:
- return ("BIP_CMAC_256");
default:
return ("??");
}
}
static uint32_t
-lkpi_l80211_to_net80211_cyphers(uint32_t wlan_cipher_suite)
+lkpi_l80211_to_net80211_cyphers(struct ieee80211com *ic,
+ uint32_t wlan_cipher_suite)
{
-
switch (wlan_cipher_suite) {
case WLAN_CIPHER_SUITE_WEP40:
return (IEEE80211_CRYPTO_WEP);
+ case WLAN_CIPHER_SUITE_WEP104:
+ return (IEEE80211_CRYPTO_WEP);
case WLAN_CIPHER_SUITE_TKIP:
return (IEEE80211_CRYPTO_TKIP);
case WLAN_CIPHER_SUITE_CCMP:
return (IEEE80211_CRYPTO_AES_CCM);
- case WLAN_CIPHER_SUITE_WEP104:
- return (IEEE80211_CRYPTO_WEP);
- case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ return (IEEE80211_CRYPTO_AES_CCM_256);
case WLAN_CIPHER_SUITE_GCMP:
+ return (IEEE80211_CRYPTO_AES_GCM_128);
case WLAN_CIPHER_SUITE_GCMP_256:
- case WLAN_CIPHER_SUITE_CCMP_256:
+ return (IEEE80211_CRYPTO_AES_GCM_256);
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ return (IEEE80211_CRYPTO_BIP_CMAC_128);
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ return (IEEE80211_CRYPTO_BIP_CMAC_256);
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ return (IEEE80211_CRYPTO_BIP_GMAC_128);
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
- case WLAN_CIPHER_SUITE_BIP_CMAC_256:
- printf("%s: unsupported WLAN Cipher Suite %#08x | %u (%s)\n",
- __func__,
- wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff,
- lkpi_cipher_suite_to_name(wlan_cipher_suite));
- break;
+ return (IEEE80211_CRYPTO_BIP_GMAC_256);
default:
- printf("%s: unknown WLAN Cipher Suite %#08x | %u (%s)\n",
+ ic_printf(ic, "%s: unknown WLAN Cipher Suite %#08x | %u (%s)\n",
__func__,
wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff,
lkpi_cipher_suite_to_name(wlan_cipher_suite));
+ return (0);
}
-
- return (0);
}
static uint32_t
@@ -939,18 +1112,42 @@ lkpi_net80211_to_l80211_cipher_suite(uint32_t cipher, uint8_t keylen)
{
switch (cipher) {
- case IEEE80211_CIPHER_TKIP:
- return (WLAN_CIPHER_SUITE_TKIP);
- case IEEE80211_CIPHER_AES_CCM:
- return (WLAN_CIPHER_SUITE_CCMP);
case IEEE80211_CIPHER_WEP:
- if (keylen < 8)
+ if (keylen == (40/NBBY))
return (WLAN_CIPHER_SUITE_WEP40);
- else
+ else if (keylen == (104/NBBY))
return (WLAN_CIPHER_SUITE_WEP104);
+ else {
+ printf("%s: WEP with unsupported keylen %d\n",
+ __func__, keylen * NBBY);
+ return (0);
+ }
break;
+ case IEEE80211_CIPHER_TKIP:
+ return (WLAN_CIPHER_SUITE_TKIP);
+ case IEEE80211_CIPHER_AES_CCM:
+ return (WLAN_CIPHER_SUITE_CCMP);
+ case IEEE80211_CIPHER_AES_CCM_256:
+ return (WLAN_CIPHER_SUITE_CCMP_256);
+ case IEEE80211_CIPHER_AES_GCM_128:
+ return (WLAN_CIPHER_SUITE_GCMP);
+ case IEEE80211_CIPHER_AES_GCM_256:
+ return (WLAN_CIPHER_SUITE_GCMP_256);
+ case IEEE80211_CIPHER_BIP_CMAC_128:
+ return (WLAN_CIPHER_SUITE_AES_CMAC);
+ case IEEE80211_CIPHER_BIP_CMAC_256:
+ return (WLAN_CIPHER_SUITE_BIP_CMAC_256);
+ case IEEE80211_CIPHER_BIP_GMAC_128:
+ return (WLAN_CIPHER_SUITE_BIP_GMAC_128);
+ case IEEE80211_CIPHER_BIP_GMAC_256:
+ return (WLAN_CIPHER_SUITE_BIP_GMAC_256);
+
case IEEE80211_CIPHER_AES_OCB:
case IEEE80211_CIPHER_TKIPMIC:
+ /*
+ * TKIP w/ hw MIC support
+ * (gone wrong; should really be a crypto flag in net80211).
+ */
case IEEE80211_CIPHER_CKIP:
case IEEE80211_CIPHER_NONE:
printf("%s: unsupported cipher %#010x\n", __func__, cipher);
@@ -1013,7 +1210,7 @@ lkpi_find_lkpi80211_chan(struct lkpi_hw *lhw,
channels = hw->wiphy->bands[band]->channels;
for (i = 0; i < nchans; i++) {
- if (channels[i].hw_value == c->ic_ieee)
+ if (channels[i].center_freq == c->ic_freq)
return (&channels[i]);
}
@@ -1093,12 +1290,21 @@ lkpi_sta_del_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
continue;
kc = lsta->kc[keyix];
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(lsta->ni->ni_ic, "%d %lu %s: running set_key cmd %d(%s) for "
+ "sta %6D: keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", lsta->sta.addr, ":",
+ kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
+#endif
+
err = lkpi_80211_mo_set_key(hw, DISABLE_KEY, vif,
LSTA_TO_STA(lsta), kc);
if (err != 0) {
- ic_printf(lsta->ni->ni_ic, "%s: set_key cmd %d(%s) for "
- "sta %6D failed: %d\n", __func__, DISABLE_KEY,
- "DISABLE", lsta->sta.addr, ":", err);
+ ic_printf(lsta->ni->ni_ic, "%d %lu %s: set_key cmd %d(%s) for "
+ "sta %6D failed: %d\n", curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", lsta->sta.addr, ":", err);
error++;
/*
@@ -1110,10 +1316,11 @@ lkpi_sta_del_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
- ic_printf(lsta->ni->ni_ic, "%s: set_key cmd %d(%s) for "
- "sta %6D succeeded: keyidx %u hw_key_idx %u flags %#10x\n",
- __func__, DISABLE_KEY, "DISABLE", lsta->sta.addr, ":",
- kc->keyidx, kc->hw_key_idx, kc->flags);
+ ic_printf(lsta->ni->ni_ic, "%d %lu %s: set_key cmd %d(%s) for "
+ "sta %6D succeeded: keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", lsta->sta.addr, ":",
+ kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
#endif
lsta->kc[keyix] = NULL;
@@ -1123,8 +1330,10 @@ lkpi_sta_del_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return (error);
}
+/* XXX-BZ one day we should replace this iterating over VIFs, or node list? */
+/* See also lkpi_sta_del_keys() these days. */
static int
-_lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
+lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
struct ieee80211com *ic;
struct lkpi_hw *lhw;
@@ -1143,11 +1352,41 @@ _lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
+ /*
+ * Make sure we do not make it here without going through
+ * lkpi_iv_key_update_begin() first.
+ */
+ lockdep_assert_wiphy(hw->wiphy);
+
+ /*
+ * While we are assoc we may still send packets. We cannot delete the
+ * keys as otherwise packets could go out unencrypted. Some firmware
+ * does not like this and will fire an assert.
+ * net80211 needs to drive this better but given we want the disassoc
+ * frame out and have to unlock we are open to a race currently.
+ * This check should prevent problems.
+ * How to test: run 800Mbit/s UDP traffic and during that restart your
+ * supplicant. You want to survive that.
+ */
+ if (vif->cfg.assoc) {
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: vif still assoc; not deleting keys\n",
+ curthread->td_tid, jiffies, __func__);
+ return (0);
+ }
+
+ if (IEEE80211_KEY_UNDEFINED(k)) {
+ ic_printf(ic, "%s: vap %p key %p is undefined: %p %u\n",
+ __func__, vap, k, k->wk_cipher, k->wk_keyix);
+ return (0);
+ }
+
if (vap->iv_bss == NULL) {
ic_printf(ic, "%s: iv_bss %p for vap %p is NULL\n",
__func__, vap->iv_bss, vap);
return (0);
}
+
ni = ieee80211_ref_node(vap->iv_bss);
lsta = ni->ni_drv_data;
if (lsta == NULL) {
@@ -1161,41 +1400,41 @@ _lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
if (lsta->kc[k->wk_keyix] == NULL) {
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
- ic_printf(ic, "%s: sta %6D and no key information, "
+ ic_printf(ic, "%d %lu %s: sta %6D and no key information, "
"keyidx %u wk_macaddr %6D; returning success\n",
- __func__, sta->addr, ":",
+ curthread->td_tid, jiffies, __func__, sta->addr, ":",
k->wk_keyix, k->wk_macaddr, ":");
#endif
ieee80211_free_node(ni);
return (1);
}
-
kc = lsta->kc[k->wk_keyix];
- /* Re-check under lock. */
- if (kc == NULL) {
+
#ifdef LINUXKPI_DEBUG_80211
- if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
- ic_printf(ic, "%s: sta %6D and key information vanished, "
- "returning success\n", __func__, sta->addr, ":");
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: running set_key cmd %d(%s) for sta %6D: "
+ "keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", sta->addr, ":",
+ kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
#endif
- error = 1;
- goto out;
- }
error = lkpi_80211_mo_set_key(hw, DISABLE_KEY, vif, sta, kc);
if (error != 0) {
- ic_printf(ic, "%s: set_key cmd %d(%s) for sta %6D failed: %d\n",
- __func__, DISABLE_KEY, "DISABLE", sta->addr, ":", error);
+ ic_printf(ic, "%d %lu %s: set_key cmd %d(%s) for sta %6D failed: %d\n",
+ curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", sta->addr, ":", error);
error = 0;
goto out;
}
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
- ic_printf(ic, "%s: set_key cmd %d(%s) for sta %6D succeeded: "
- "keyidx %u hw_key_idx %u flags %#10x\n", __func__,
+ ic_printf(ic, "%d %lu %s: set_key cmd %d(%s) for sta %6D succeeded: "
+ "keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
DISABLE_KEY, "DISABLE", sta->addr, ":",
- kc->keyidx, kc->hw_key_idx, kc->flags);
+ kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
#endif
lsta->kc[k->wk_keyix] = NULL;
free(kc, M_LKPI80211);
@@ -1206,16 +1445,7 @@ out:
}
static int
-lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
-{
-
- /* XXX-BZ one day we should replace this iterating over VIFs, or node list? */
- /* See also lkpi_sta_del_keys() these days. */
- return (_lkpi_iv_key_delete(vap, k));
-}
-
-static int
-_lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
+lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
struct ieee80211com *ic;
struct lkpi_hw *lhw;
@@ -1227,13 +1457,25 @@ _lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
struct ieee80211_node *ni;
struct ieee80211_key_conf *kc;
uint32_t lcipher;
+ uint16_t exp_flags;
+ uint8_t keylen;
int error;
ic = vap->iv_ic;
lhw = ic->ic_softc;
hw = LHW_TO_HW(lhw);
- lvif = VAP_TO_LVIF(vap);
- vif = LVIF_TO_VIF(lvif);
+
+ /*
+ * Make sure we do not make it here without going through
+ * lkpi_iv_key_update_begin() first.
+ */
+ lockdep_assert_wiphy(hw->wiphy);
+
+ if (IEEE80211_KEY_UNDEFINED(k)) {
+ ic_printf(ic, "%s: vap %p key %p is undefined: %p %u\n",
+ __func__, vap, k, k->wk_cipher, k->wk_keyix);
+ return (0);
+ }
if (vap->iv_bss == NULL) {
ic_printf(ic, "%s: iv_bss %p for vap %p is NULL\n",
@@ -1250,22 +1492,16 @@ _lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
}
sta = LSTA_TO_STA(lsta);
- if (lsta->kc[k->wk_keyix] != NULL) {
- IMPROVE("Still in firmware? Del first. Can we assert this cannot happen?");
- ic_printf(ic, "%s: sta %6D found with key information\n",
- __func__, sta->addr, ":");
- kc = lsta->kc[k->wk_keyix];
- lsta->kc[k->wk_keyix] = NULL;
- free(kc, M_LKPI80211);
- kc = NULL; /* safeguard */
- }
-
+ keylen = k->wk_keylen;
lcipher = lkpi_net80211_to_l80211_cipher_suite(
k->wk_cipher->ic_cipher, k->wk_keylen);
switch (lcipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ keylen += 2 * k->wk_cipher->ic_miclen;
+ break;
case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
break;
- case WLAN_CIPHER_SUITE_TKIP:
default:
ic_printf(ic, "%s: CIPHER SUITE %#x (%s) not supported\n",
__func__, lcipher, lkpi_cipher_suite_to_name(lcipher));
@@ -1274,9 +1510,18 @@ _lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
return (0);
}
- kc = malloc(sizeof(*kc) + k->wk_keylen, M_LKPI80211, M_WAITOK | M_ZERO);
+ if (lsta->kc[k->wk_keyix] != NULL) {
+ IMPROVE("Still in firmware? Del first. Can we assert this cannot happen?");
+ ic_printf(ic, "%s: sta %6D found with key information\n",
+ __func__, sta->addr, ":");
+ kc = lsta->kc[k->wk_keyix];
+ lsta->kc[k->wk_keyix] = NULL;
+ free(kc, M_LKPI80211);
+ kc = NULL; /* safeguard */
+ }
+
+ kc = malloc(sizeof(*kc) + keylen, M_LKPI80211, M_WAITOK | M_ZERO);
kc->_k = k; /* Save the pointer to net80211. */
- atomic64_set(&kc->tx_pn, k->wk_keytsc);
kc->cipher = lcipher;
kc->keyidx = k->wk_keyix;
#if 0
@@ -1291,12 +1536,17 @@ _lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
if (k->wk_flags & IEEE80211_KEY_GROUP)
kc->flags &= ~IEEE80211_KEY_FLAG_PAIRWISE;
+ kc->iv_len = k->wk_cipher->ic_header;
+ kc->icv_len = k->wk_cipher->ic_trailer;
+
switch (kc->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ memcpy(kc->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, k->wk_txmic, k->wk_cipher->ic_miclen);
+ memcpy(kc->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, k->wk_rxmic, k->wk_cipher->ic_miclen);
+ break;
case WLAN_CIPHER_SUITE_CCMP:
- kc->iv_len = k->wk_cipher->ic_header;
- kc->icv_len = k->wk_cipher->ic_trailer;
+ case WLAN_CIPHER_SUITE_GCMP:
break;
- case WLAN_CIPHER_SUITE_TKIP:
default:
/* currently UNREACH */
IMPROVE();
@@ -1304,10 +1554,22 @@ _lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
};
lsta->kc[k->wk_keyix] = kc;
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: running set_key cmd %d(%s) for sta %6D: "
+ "kc %p keyidx %u hw_key_idx %u keylen %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ SET_KEY, "SET", sta->addr, ":", kc, kc->keyidx, kc->hw_key_idx,
+ kc->keylen, kc->flags, IEEE80211_KEY_FLAG_BITS);
+#endif
+
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
error = lkpi_80211_mo_set_key(hw, SET_KEY, vif, sta, kc);
if (error != 0) {
- ic_printf(ic, "%s: set_key cmd %d(%s) for sta %6D failed: %d\n",
- __func__, SET_KEY, "SET", sta->addr, ":", error);
+ ic_printf(ic, "%d %lu %s: set_key cmd %d(%s) for sta %6D failed: %d\n",
+ curthread->td_tid, jiffies, __func__,
+ SET_KEY, "SET", sta->addr, ":", error);
lsta->kc[k->wk_keyix] = NULL;
free(kc, M_LKPI80211);
ieee80211_free_node(ni);
@@ -1316,21 +1578,63 @@ _lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
- ic_printf(ic, "%s: set_key cmd %d(%s) for sta %6D succeeded: "
- "kc %p keyidx %u hw_key_idx %u flags %#010x\n", __func__,
+ ic_printf(ic, "%d %lu %s: set_key cmd %d(%s) for sta %6D succeeded: "
+ "kc %p keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
SET_KEY, "SET", sta->addr, ":",
- kc, kc->keyidx, kc->hw_key_idx, kc->flags);
+ kc, kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
#endif
- ieee80211_free_node(ni);
- return (1);
-}
+ exp_flags = 0;
+ switch (kc->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ exp_flags = (IEEE80211_KEY_FLAG_PAIRWISE |
+ IEEE80211_KEY_FLAG_PUT_IV_SPACE |
+ IEEE80211_KEY_FLAG_GENERATE_MMIC |
+ IEEE80211_KEY_FLAG_PUT_MIC_SPACE);
+#define TKIP_INVAL_COMBINATION \
+ (IEEE80211_KEY_FLAG_PUT_MIC_SPACE|IEEE80211_KEY_FLAG_GENERATE_MMIC)
+ if ((kc->flags & TKIP_INVAL_COMBINATION) == TKIP_INVAL_COMBINATION) {
+ ic_printf(ic, "%s: SET_KEY for %s returned invalid "
+ "combination %b\n", __func__,
+ lkpi_cipher_suite_to_name(kc->cipher),
+ kc->flags, IEEE80211_KEY_FLAG_BITS);
+ }
+#undef TKIP_INVAL_COMBINATION
+#ifdef __notyet__
+ /* Do flags surgery; special see linuxkpi_ieee80211_ifattach(). */
+ if ((kc->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) != 0) {
+ k->wk_flags &= ~(IEEE80211_KEY_NOMICMGT|IEEE80211_KEY_NOMIC);
+ k->wk_flags |= IEEE80211_KEY_SWMIC;
+ ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC
+ }
+#endif
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
+ exp_flags = (IEEE80211_KEY_FLAG_PAIRWISE |
+ IEEE80211_KEY_FLAG_PUT_IV_SPACE |
+ IEEE80211_KEY_FLAG_GENERATE_IV |
+ IEEE80211_KEY_FLAG_GENERATE_IV_MGMT | /* Only needs IV geeration for MGMT frames. */
+ IEEE80211_KEY_FLAG_SW_MGMT_TX); /* MFP in software */
+ break;
+ }
+ if ((kc->flags & ~exp_flags) != 0)
+ ic_printf(ic, "%s: SET_KEY for %s returned unexpected key flags: "
+ " %#06x & ~%#06x = %b\n", __func__,
+ lkpi_cipher_suite_to_name(kc->cipher), kc->flags, exp_flags,
+ (kc->flags & ~exp_flags), IEEE80211_KEY_FLAG_BITS);
-static int
-lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
-{
+#ifdef __notyet__
+ /* Do flags surgery. */
+ if ((kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) == 0)
+ k->wk_flags |= IEEE80211_KEY_NOIVMGT;
+ if ((kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV) == 0)
+ k->wk_flags |= IEEE80211_KEY_NOIV;
+#endif
- return (_lkpi_iv_key_set(vap, k));
+ ieee80211_free_node(ni);
+ return (1);
}
static void
@@ -1341,7 +1645,8 @@ lkpi_iv_key_update_begin(struct ieee80211vap *vap)
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
struct lkpi_vif *lvif;
- bool islocked;
+ struct ieee80211_node *ni;
+ bool icislocked, ntislocked;
ic = vap->iv_ic;
lhw = ic->ic_softc;
@@ -1349,29 +1654,54 @@ lkpi_iv_key_update_begin(struct ieee80211vap *vap)
lvif = VAP_TO_LVIF(vap);
nt = &ic->ic_sta;
- islocked = IEEE80211_NODE_IS_LOCKED(nt);
+ icislocked = IEEE80211_IS_LOCKED(ic);
+ ntislocked = IEEE80211_NODE_IS_LOCKED(nt);
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
- ic_printf(vap->iv_ic, "%s: tid %d vap %p nt %p %slocked "
- "lvif nt_unlocked %d\n", __func__, curthread->td_tid,
- vap, nt, islocked ? "" : "un", lvif->nt_unlocked);
+ ic_printf(ic, "%d %lu %s: vap %p ic %p %slocked nt %p %slocked "
+ "lvif ic_unlocked %d nt_unlocked %d\n",
+ curthread->td_tid, jiffies, __func__, vap,
+ ic, icislocked ? "" : "un", nt, ntislocked ? "" : "un",
+ lvif->ic_unlocked, lvif->nt_unlocked);
#endif
- /* This is inconsistent net80211 locking to be fixed one day. */
- if (islocked)
+ /*
+ * This is inconsistent net80211 locking to be fixed one day.
+ */
+ /* Try to make sure the node does not go away while possibly unlocked. */
+ ni = NULL;
+ if (icislocked || ntislocked) {
+ if (vap->iv_bss != NULL)
+ ni = ieee80211_ref_node(vap->iv_bss);
+ }
+
+ if (icislocked)
+ IEEE80211_UNLOCK(ic);
+ if (ntislocked)
IEEE80211_NODE_UNLOCK(nt);
wiphy_lock(hw->wiphy);
+ KASSERT(lvif->key_update_iv_bss == NULL, ("%s: key_update_iv_bss not NULL %p",
+ __func__, lvif->key_update_iv_bss));
+ lvif->key_update_iv_bss = ni;
+
/*
- * nt_unlocked could be a bool given we are under the lock and there
+ * ic/nt_unlocked could be a bool given we are under the lock and there
* must only be a single thread.
* In case anything in the future disturbs the order the refcnt will
* help us catching problems a lot easier.
*/
- if (islocked)
+ if (icislocked)
+ refcount_acquire(&lvif->ic_unlocked);
+ if (ntislocked)
refcount_acquire(&lvif->nt_unlocked);
+
+ /*
+ * Stop the queues while doing key updates.
+ */
+ ieee80211_stop_queues(hw);
}
static void
@@ -1382,7 +1712,7 @@ lkpi_iv_key_update_end(struct ieee80211vap *vap)
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
struct lkpi_vif *lvif;
- bool islocked;
+ bool icislocked, ntislocked;
ic = vap->iv_ic;
lhw = ic->ic_softc;
@@ -1390,14 +1720,23 @@ lkpi_iv_key_update_end(struct ieee80211vap *vap)
lvif = VAP_TO_LVIF(vap);
nt = &ic->ic_sta;
- islocked = IEEE80211_NODE_IS_LOCKED(nt);
- MPASS(!islocked);
+ /*
+ * Re-enabled the queues after the key update.
+ */
+ lkpi_ieee80211_wake_queues_locked(hw);
+
+ icislocked = IEEE80211_IS_LOCKED(ic);
+ MPASS(!icislocked);
+ ntislocked = IEEE80211_NODE_IS_LOCKED(nt);
+ MPASS(!ntislocked);
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
- ic_printf(vap->iv_ic, "%s: tid %d vap %p nt %p %slocked "
- "lvif nt_unlocked %d\n", __func__, curthread->td_tid,
- vap, nt, islocked ? "" : "un", lvif->nt_unlocked);
+ ic_printf(ic, "%d %lu %s: vap %p ic %p %slocked nt %p %slocked "
+ "lvif ic_unlocked %d nt_unlocked %d\n",
+ curthread->td_tid, jiffies, __func__, vap,
+ ic, icislocked ? "" : "un", nt, ntislocked ? "" : "un",
+ lvif->ic_unlocked, lvif->nt_unlocked);
#endif
/*
@@ -1405,15 +1744,45 @@ lkpi_iv_key_update_end(struct ieee80211vap *vap)
* In case the refcnt gets out of sync locking in net80211 will
* quickly barf as well (trying to unlock a lock not held).
*/
- islocked = refcount_release_if_last(&lvif->nt_unlocked);
+ icislocked = refcount_release_if_last(&lvif->ic_unlocked);
+ ntislocked = refcount_release_if_last(&lvif->nt_unlocked);
+
+ if (lvif->key_update_iv_bss != NULL) {
+ ieee80211_free_node(lvif->key_update_iv_bss);
+ lvif->key_update_iv_bss = NULL;
+ }
+
wiphy_unlock(hw->wiphy);
- /* This is inconsistent net80211 locking to be fixed one day. */
- if (islocked)
+ /*
+ * This is inconsistent net80211 locking to be fixed one day.
+ * ic before nt to avoid a LOR.
+ */
+ if (icislocked)
+ IEEE80211_LOCK(ic);
+ if (ntislocked)
IEEE80211_NODE_LOCK(nt);
}
#endif
+static void
+lkpi_cleanup_mcast_list_locked(struct lkpi_hw *lhw)
+{
+ struct list_head *le, *next;
+ struct netdev_hw_addr *addr;
+
+ if (lhw->mc_list.count != 0) {
+ list_for_each_safe(le, next, &lhw->mc_list.addr_list) {
+ addr = list_entry(le, struct netdev_hw_addr, addr_list);
+ list_del(le);
+ lhw->mc_list.count--;
+ free(addr, M_LKPI80211);
+ }
+ }
+ KASSERT(lhw->mc_list.count == 0, ("%s: mc_list %p count %d != 0\n",
+ __func__, &lhw->mc_list, lhw->mc_list.count));
+}
+
static u_int
lkpi_ic_update_mcast_copy(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
@@ -1450,16 +1819,13 @@ lkpi_ic_update_mcast_copy(void *arg, struct sockaddr_dl *sdl, u_int cnt)
}
static void
-lkpi_update_mcast_filter(struct ieee80211com *ic, bool force)
+lkpi_update_mcast_filter(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
- struct netdev_hw_addr_list mc_list;
- struct list_head *le, *next;
- struct netdev_hw_addr *addr;
- struct ieee80211vap *vap;
u64 mc;
- unsigned int changed_flags, total_flags;
+ unsigned int changed_flags, flags;
+ bool scanning;
lhw = ic->ic_softc;
@@ -1467,44 +1833,32 @@ lkpi_update_mcast_filter(struct ieee80211com *ic, bool force)
lhw->ops->configure_filter == NULL)
return;
- if (!lhw->update_mc && !force)
- return;
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ scanning = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
- changed_flags = total_flags = 0;
- mc_list.count = 0;
- INIT_LIST_HEAD(&mc_list.addr_list);
- if (ic->ic_allmulti == 0) {
- TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
- if_foreach_llmaddr(vap->iv_ifp,
- lkpi_ic_update_mcast_copy, &mc_list);
- } else {
- changed_flags |= FIF_ALLMULTI;
- }
+ LKPI_80211_LHW_MC_LOCK(lhw);
+
+ flags = 0;
+ if (scanning)
+ flags |= FIF_BCN_PRBRESP_PROMISC;
+ if (lhw->mc_all_multi)
+ flags |= FIF_ALLMULTI;
hw = LHW_TO_HW(lhw);
- mc = lkpi_80211_mo_prepare_multicast(hw, &mc_list);
- /*
- * XXX-BZ make sure to get this sorted what is a change,
- * what gets all set; what was already set?
- */
- total_flags = changed_flags;
- lkpi_80211_mo_configure_filter(hw, changed_flags, &total_flags, mc);
+ mc = lkpi_80211_mo_prepare_multicast(hw, &lhw->mc_list);
+
+ changed_flags = (lhw->mc_flags ^ flags) & FIF_FLAGS_MASK;
+ lkpi_80211_mo_configure_filter(hw, changed_flags, &flags, mc);
+ lhw->mc_flags = flags;
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE)
- printf("%s: changed_flags %#06x count %d total_flags %#010x\n",
- __func__, changed_flags, mc_list.count, total_flags);
+ printf("%s: changed_flags %#06x count %d mc_flags %#010x\n",
+ __func__, changed_flags, lhw->mc_list.count, lhw->mc_flags);
#endif
- if (mc_list.count != 0) {
- list_for_each_safe(le, next, &mc_list.addr_list) {
- addr = list_entry(le, struct netdev_hw_addr, addr_list);
- free(addr, M_LKPI80211);
- mc_list.count--;
- }
- }
- KASSERT(mc_list.count == 0, ("%s: mc_list %p count %d != 0\n",
- __func__, &mc_list, mc_list.count));
+ LKPI_80211_LHW_MC_UNLOCK(lhw);
}
static enum ieee80211_bss_changed
@@ -1536,13 +1890,31 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
vif->bss_conf.beacon_int = 16;
bss_changed |= BSS_CHANGED_BEACON_INT;
}
- if (vif->bss_conf.dtim_period != vap->iv_dtim_period &&
- vap->iv_dtim_period > 0) {
- vif->bss_conf.dtim_period = vap->iv_dtim_period;
+
+ /*
+ * lkpi_iv_sta_recv_mgmt() will directly call into this function.
+ * iwlwifi(4) in iwl_mvm_bss_info_changed_station_common() will
+ * stop seesion protection the moment it sees
+ * BSS_CHANGED_BEACON_INFO (with the expectations that it was
+ * "a beacon from the associated AP"). It will also update
+ * the beacon filter in that case. This is the only place
+ * we set the BSS_CHANGED_BEACON_INFO on the non-teardown
+ * path so make sure we only do run this check once we are
+ * assoc. (*iv_recv_mgmt)() will be called before we enter
+ * here so the ni will be updates with information from the
+ * beacon via net80211::sta_recv_mgmt(). We also need to
+ * make sure we do not do it on every beacon we still may
+ * get so only do if something changed. vif->bss_conf.dtim_period
+ * should be 0 as we start up (we also reset it on teardown).
+ */
+ if (vif->cfg.assoc &&
+ vif->bss_conf.dtim_period != ni->ni_dtim_period &&
+ ni->ni_dtim_period > 0) {
+ vif->bss_conf.dtim_period = ni->ni_dtim_period;
bss_changed |= BSS_CHANGED_BEACON_INFO;
}
- vif->bss_conf.sync_dtim_count = vap->iv_dtim_count;
+ vif->bss_conf.sync_dtim_count = ni->ni_dtim_count;
vif->bss_conf.sync_tsf = le64toh(ni->ni_tstamp.tsf);
/* vif->bss_conf.sync_device_ts = set in linuxkpi_ieee80211_rx. */
@@ -1570,6 +1942,8 @@ lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif)
int error;
bool cancel;
+ TRACE_SCAN(lhw->ic, "scan_flags %b", lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
cancel = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
@@ -1579,10 +1953,10 @@ lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif)
hw = LHW_TO_HW(lhw);
IEEE80211_UNLOCK(lhw->ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
/* Need to cancel the scan. */
lkpi_80211_mo_cancel_hw_scan(hw, vif);
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
/* Need to make sure we see ieee80211_scan_completed. */
LKPI_80211_LHW_SCAN_LOCK(lhw);
@@ -1623,19 +1997,19 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
struct lkpi_hw *lhw)
{
enum ieee80211_bss_changed changed;
+ struct lkpi_vif *lvif;
changed = 0;
sta->aid = 0;
if (vif->cfg.assoc) {
- lhw->update_mc = true;
- lkpi_update_mcast_filter(lhw->ic, true);
-
vif->cfg.assoc = false;
vif->cfg.aid = 0;
changed |= BSS_CHANGED_ASSOC;
IMPROVE();
+ lkpi_update_mcast_filter(lhw->ic);
+
/*
* Executing the bss_info_changed(BSS_CHANGED_ASSOC) with
* assoc = false right away here will remove the sta from
@@ -1646,6 +2020,9 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
* bss_info_changed() update.
* See lkpi_sta_run_to_init() for more detailed comment.
*/
+
+ lvif = VIF_TO_LVIF(vif);
+ lvif->beacons = 0;
}
return (changed);
@@ -1699,11 +2076,14 @@ lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
static void
lkpi_80211_flush_tx(struct lkpi_hw *lhw, struct lkpi_sta *lsta)
{
+ struct ieee80211_hw *hw;
struct mbufq mq;
struct mbuf *m;
int len;
- LKPI_80211_LHW_UNLOCK_ASSERT(lhw);
+ /* There is no lockdep_assert_not_held_wiphy(). */
+ hw = LHW_TO_HW(lhw);
+ lockdep_assert_not_held(&hw->wiphy->mtx);
/* Do not accept any new packets until scan_to_auth or lsta_free(). */
LKPI_80211_LSTA_TXQ_LOCK(lsta);
@@ -1731,6 +2111,35 @@ lkpi_80211_flush_tx(struct lkpi_hw *lhw, struct lkpi_sta *lsta)
}
}
+
+static void
+lkpi_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct lkpi_chanctx *lchanctx;
+
+ chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf,
+ lockdep_is_held(&hw->wiphy->mtx));
+
+ if (chanctx_conf == NULL)
+ return;
+
+ /* Remove vif context. */
+ lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, chanctx_conf);
+
+ lkpi_hw_conf_idle(hw, true);
+
+ /* Remove chan ctx. */
+ lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
+
+ /* Cleanup. */
+ rcu_assign_pointer(vif->bss_conf.chanctx_conf, NULL);
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
+ list_del(&lchanctx->entry);
+ free(lchanctx, M_LKPI80211);
+}
+
+
/* -------------------------------------------------------------------------- */
static int
@@ -1759,6 +2168,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
struct ieee80211_prep_tx_info prep_tx_info;
uint32_t changed;
int error;
+ bool synched;
/*
* In here we use vap->iv_bss until lvif->lvif_bss is set.
@@ -1812,11 +2222,12 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
LKPI_80211_LVIF_UNLOCK(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
/* Add chanctx (or if exists, change it). */
- if (vif->chanctx_conf != NULL) {
- chanctx_conf = vif->chanctx_conf;
+ chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf,
+ lockdep_is_held(&hw->wiphy->mtx));
+ if (chanctx_conf != NULL) {
lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
IMPROVE("diff changes for changed, working on live copy, rcu");
} else {
@@ -1837,6 +2248,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
IMPROVE("Check vht_cap from band not just chan?");
KASSERT(ni->ni_chan != NULL && ni->ni_chan != IEEE80211_CHAN_ANYC,
("%s:%d: ni %p ni_chan %p\n", __func__, __LINE__, ni, ni->ni_chan));
+
#ifdef LKPI_80211_HT
if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
@@ -1846,15 +2258,12 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
}
#endif
#ifdef LKPI_80211_VHT
- if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
-#ifdef __notyet__
+ if (IEEE80211_IS_CHAN_VHT_5GHZ(ni->ni_chan)) {
if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan))
chanctx_conf->def.width = NL80211_CHAN_WIDTH_80P80;
else if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan))
chanctx_conf->def.width = NL80211_CHAN_WIDTH_160;
- else
-#endif
- if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
+ else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
chanctx_conf->def.width = NL80211_CHAN_WIDTH_80;
}
#endif
@@ -1884,6 +2293,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* vif->bss_conf.basic_rates ? Where exactly? */
+ lvif->beacons = 0;
/* Should almost assert it is this. */
vif->cfg.assoc = false;
vif->cfg.aid = 0;
@@ -1891,7 +2301,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
error = 0;
- if (vif->chanctx_conf != NULL) {
+ if (vif->bss_conf.chanctx_conf == chanctx_conf) {
changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
changed |= IEEE80211_CHANCTX_CHANGE_RADAR;
changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS;
@@ -1912,7 +2322,8 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
goto out;
}
- vif->bss_conf.chanctx_conf = chanctx_conf;
+ list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list);
+ rcu_assign_pointer(vif->bss_conf.chanctx_conf, chanctx_conf);
/* Assign vif chanctx. */
if (error == 0)
@@ -1924,7 +2335,9 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
ic_printf(vap->iv_ic, "%s:%d: mo_assign_vif_chanctx "
"failed: %d\n", __func__, __LINE__, error);
lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
+ rcu_assign_pointer(vif->bss_conf.chanctx_conf, NULL);
lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
+ list_del(&lchanctx->entry);
free(lchanctx, M_LKPI80211);
goto out;
}
@@ -1944,18 +2357,8 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
__func__, ni, ni->ni_drv_data));
lsta = ni->ni_drv_data;
- /*
- * Make sure in case the sta did not change and we re-add it,
- * that we can tx again.
- */
- LKPI_80211_LSTA_TXQ_LOCK(lsta);
- lsta->txq_ready = true;
- LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
-
- wiphy_lock(hw->wiphy);
/* Insert the [l]sta into the list of known stations. */
list_add_tail(&lsta->lsta_list, &lvif->lsta_list);
- wiphy_unlock(hw->wiphy);
/* Add (or adjust) sta and change state (from NOTEXIST) to NONE. */
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
@@ -2001,7 +2404,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
* (ideally we'd do that on a callback for something else ...)
*/
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
LKPI_80211_LVIF_LOCK(lvif);
@@ -2027,10 +2430,10 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
ieee80211_ref_node(lsta->ni);
lvif->lvif_bss = lsta;
if (lsta->ni == vap->iv_bss) {
- lvif->lvif_bss_synched = true;
+ lvif->lvif_bss_synched = synched = true;
} else {
/* Set to un-synched no matter what. */
- lvif->lvif_bss_synched = false;
+ lvif->lvif_bss_synched = synched = false;
/*
* We do not error as someone has to take us down.
* If we are followed by a 2nd, new net80211::join1() going to
@@ -2040,13 +2443,24 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
* to net80211 as we never used the node beyond alloc()/free()
* and we do not hold an extra reference for that anymore given
* ni : lsta == 1:1.
+ * Problem is if we do not error a MGMT/AUTH frame will be
+ * sent from net80211::sta_newstate(); disable lsta queue below.
*/
}
LKPI_80211_LVIF_UNLOCK(lvif);
+ /*
+ * Make sure in case the sta did not change and we re-added it,
+ * that we can tx again but only if the vif/iv_bss are in sync.
+ * Otherwise this should prevent the MGMT/AUTH frame from being
+ * sent triggering a warning in iwlwifi.
+ */
+ LKPI_80211_LSTA_TXQ_LOCK(lsta);
+ lsta->txq_ready = synched;
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
goto out_relocked;
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
out_relocked:
/*
@@ -2069,6 +2483,7 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
struct lkpi_sta *lsta;
struct ieee80211_sta *sta;
struct ieee80211_prep_tx_info prep_tx_info;
+ enum ieee80211_bss_changed bss_changed;
int error;
lhw = vap->iv_ic->ic_softc;
@@ -2098,7 +2513,7 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
/* flush, drop. */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
@@ -2140,6 +2555,11 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lsta->added_to_drv = false; /* mo manages. */
#endif
+ bss_changed = 0;
+ vif->bss_conf.dtim_period = 0; /* go back to 0. */
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
+ lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
LKPI_80211_LVIF_LOCK(lvif);
@@ -2157,26 +2577,10 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* conf_tx */
- /* Take the chan ctx down. */
- if (vif->chanctx_conf != NULL) {
- struct lkpi_chanctx *lchanctx;
- struct ieee80211_chanctx_conf *chanctx_conf;
-
- chanctx_conf = vif->chanctx_conf;
- /* Remove vif context. */
- lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
- /* NB: vif->chanctx_conf is NULL now. */
-
- lkpi_hw_conf_idle(hw, true);
-
- /* Remove chan ctx. */
- lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
- free(lchanctx, M_LKPI80211);
- }
+ lkpi_remove_chanctx(hw, vif);
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
return (error);
}
@@ -2209,7 +2613,7 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
/* XXX-BZ KASSERT later? */
@@ -2275,7 +2679,7 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
*/
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
return (error);
}
@@ -2298,7 +2702,7 @@ lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
/* XXX-BZ KASSERT later? */
@@ -2342,7 +2746,7 @@ lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
error = 0;
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
return (error);
@@ -2368,7 +2772,7 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
#ifdef LINUXKPI_DEBUG_80211
@@ -2404,7 +2808,7 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
lsta->in_mgd = true;
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
/* Call iv_newstate first so we get potential DEAUTH packet out. */
@@ -2420,7 +2824,7 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
/* Ensure the packets get out. */
lkpi_80211_flush_tx(lhw, lsta);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -2462,6 +2866,14 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
bss_changed = 0;
bss_changed |= lkpi_disassoc(sta, vif, lhw);
+#ifdef LKPI_80211_HW_CRYPTO
+ /*
+ * In theory we remove keys here but there must not exist any for this
+ * state change until we clean them up again into small steps and no
+ * code duplication.
+ */
+#endif
+
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
/* Adjust sta and change state (from NONE) to NOTEXIST. */
@@ -2484,6 +2896,8 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
vif->cfg.ssid_len = 0;
memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
bss_changed |= BSS_CHANGED_BSSID;
+ vif->bss_conf.dtim_period = 0; /* go back to 0. */
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
LKPI_80211_LVIF_LOCK(lvif);
@@ -2501,27 +2915,11 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
/* conf_tx */
- /* Take the chan ctx down. */
- if (vif->chanctx_conf != NULL) {
- struct lkpi_chanctx *lchanctx;
- struct ieee80211_chanctx_conf *chanctx_conf;
-
- chanctx_conf = vif->chanctx_conf;
- /* Remove vif context. */
- lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
- /* NB: vif->chanctx_conf is NULL now. */
-
- lkpi_hw_conf_idle(hw, true);
-
- /* Remove chan ctx. */
- lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
- free(lchanctx, M_LKPI80211);
- }
+ lkpi_remove_chanctx(hw, vif);
error = EALREADY;
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
outni:
return (error);
@@ -2580,7 +2978,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
/* XXX-BZ KASSERT later? */
@@ -2632,6 +3030,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
bss_changed |= lkpi_wme_update(lhw, vap, true);
#endif
if (!vif->cfg.assoc || vif->cfg.aid != IEEE80211_NODE_AID(ni)) {
+ lvif->beacons = 0;
vif->cfg.assoc = true;
vif->cfg.aid = IEEE80211_NODE_AID(ni);
bss_changed |= BSS_CHANGED_ASSOC;
@@ -2656,7 +3055,6 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
}
bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
-
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
/* - change_chanctx (if needed)
@@ -2681,17 +3079,19 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
* - set_key (?)
* - ipv6_addr_change (?)
*/
- /* Prepare_multicast && configure_filter. */
- lhw->update_mc = true;
- lkpi_update_mcast_filter(vap->iv_ic, true);
if (!ieee80211_node_is_authorized(ni)) {
IMPROVE("net80211 does not consider node authorized");
}
- sta->deflink.rx_nss = MAX(1, sta->deflink.rx_nss);
IMPROVE("Is this the right spot, has net80211 done all updates already?");
- lkpi_sta_sync_from_ni(sta, ni);
+ lkpi_sta_sync_from_ni(hw, vif, sta, ni, true);
+
+ /* Update thresholds. */
+ hw->wiphy->frag_threshold = vap->iv_fragthreshold;
+ lkpi_80211_mo_set_frag_threshold(hw, vap->iv_fragthreshold);
+ hw->wiphy->rts_threshold = vap->iv_rtsthreshold;
+ lkpi_80211_mo_set_rts_threshold(hw, vap->iv_rtsthreshold);
/* Update sta_state (ASSOC to AUTHORIZED). */
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
@@ -2717,8 +3117,11 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ /* Prepare_multicast && configure_filter. */
+ lkpi_update_mcast_filter(vap->iv_ic);
+
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
return (error);
}
@@ -2777,7 +3180,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
/* flush, drop. */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
@@ -2792,7 +3195,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lsta->in_mgd = true;
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
/* Call iv_newstate first so we get potential DISASSOC packet out. */
@@ -2808,7 +3211,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* Ensure the packets get out. */
lkpi_80211_flush_tx(lhw, lsta);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -2852,9 +3255,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
#ifdef LKPI_80211_HW_CRYPTO
if (lkpi_hwcrypto) {
- wiphy_lock(hw->wiphy);
error = lkpi_sta_del_keys(hw, vif, lsta);
- wiphy_unlock(hw->wiphy);
if (error != 0) {
ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys "
"failed: %d\n", __func__, __LINE__, error);
@@ -2888,7 +3289,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
error = EALREADY;
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
outni:
return (error);
@@ -2914,7 +3315,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
#ifdef LINUXKPI_DEBUG_80211
@@ -2950,7 +3351,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lsta->in_mgd = true;
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
/* Call iv_newstate first so we get potential DISASSOC packet out. */
@@ -2966,7 +3367,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* Ensure the packets get out. */
lkpi_80211_flush_tx(lhw, lsta);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -3008,9 +3409,17 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
#ifdef LKPI_80211_HW_CRYPTO
if (lkpi_hwcrypto) {
- wiphy_lock(hw->wiphy);
+ /*
+ * In theory we only need to do this if we changed assoc.
+ * If we were not assoc, there should be no keys and we
+ * should not be here.
+ */
+#ifdef notyet
+ KASSERT((bss_changed & BSS_CHANGED_ASSOC) != 0, ("%s: "
+ "trying to remove keys but were not assoc: %#010jx, lvif %p\n",
+ __func__, (uintmax_t)bss_changed, lvif));
+#endif
error = lkpi_sta_del_keys(hw, vif, lsta);
- wiphy_unlock(hw->wiphy);
if (error != 0) {
ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys "
"failed: %d\n", __func__, __LINE__, error);
@@ -3071,6 +3480,9 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
* 4) call unassign_vif_chanctx
* 5) call lkpi_hw_conf_idle
* 6) call remove_chanctx
+ *
+ * Note: vif->driver_flags & IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC
+ * might change this.
*/
bss_changed |= lkpi_disassoc(sta, vif, lhw);
@@ -3101,6 +3513,8 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
vif->bss_conf.use_short_preamble = false;
vif->bss_conf.qos = false;
/* XXX BSS_CHANGED_???? */
+ vif->bss_conf.dtim_period = 0; /* go back to 0. */
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
LKPI_80211_LVIF_LOCK(lvif);
@@ -3117,27 +3531,11 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* conf_tx */
- /* Take the chan ctx down. */
- if (vif->chanctx_conf != NULL) {
- struct lkpi_chanctx *lchanctx;
- struct ieee80211_chanctx_conf *chanctx_conf;
-
- chanctx_conf = vif->chanctx_conf;
- /* Remove vif context. */
- lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
- /* NB: vif->chanctx_conf is NULL now. */
-
- lkpi_hw_conf_idle(hw, true);
-
- /* Remove chan ctx. */
- lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
- free(lchanctx, M_LKPI80211);
- }
+ lkpi_remove_chanctx(hw, vif);
error = EALREADY;
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
outni:
return (error);
@@ -3236,7 +3634,7 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
vif = LVIF_TO_VIF(lvif);
/* No need to replicate this in most state handlers. */
- if (ostate == IEEE80211_S_SCAN && nstate != IEEE80211_S_SCAN)
+ if (nstate > IEEE80211_S_SCAN)
lkpi_stop_hw_scan(lhw, vif);
s = sta_state_fsm;
@@ -3341,6 +3739,9 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
int error;
uint16_t ac;
+ hw = LHW_TO_HW(lhw);
+ lockdep_assert_wiphy(hw->wiphy);
+
IMPROVE();
KASSERT(WME_NUM_AC == IEEE80211_NUM_ACS, ("%s: WME_NUM_AC %d != "
"IEEE80211_NUM_ACS %d\n", __func__, WME_NUM_AC, IEEE80211_NUM_ACS));
@@ -3367,12 +3768,10 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
wmeparr[ac] = chp.cap_wmeParams[ac];
IEEE80211_UNLOCK(ic);
- hw = LHW_TO_HW(lhw);
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
/* Configure tx queues (conf_tx) & send BSS_CHANGED_QOS. */
- LKPI_80211_LHW_LOCK(lhw);
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
struct wmeParams *wmep;
@@ -3387,7 +3786,6 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
__func__, ac, error);
}
- LKPI_80211_LHW_UNLOCK(lhw);
changed = BSS_CHANGED_QOS;
if (!planned)
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
@@ -3402,6 +3800,7 @@ lkpi_ic_wme_update(struct ieee80211com *ic)
#ifdef LKPI_80211_WME
struct ieee80211vap *vap;
struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
IMPROVE("Use the per-VAP callback in net80211.");
vap = TAILQ_FIRST(&ic->ic_vaps);
@@ -3409,12 +3808,57 @@ lkpi_ic_wme_update(struct ieee80211com *ic)
return (0);
lhw = ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ wiphy_lock(hw->wiphy);
lkpi_wme_update(lhw, vap, false);
+ wiphy_unlock(hw->wiphy);
#endif
return (0); /* unused */
}
+static void
+lkpi_iv_sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ enum ieee80211_bss_changed bss_changed;
+
+ lvif = VAP_TO_LVIF(ni->ni_vap);
+ vif = LVIF_TO_VIF(lvif);
+
+ lvif->iv_recv_mgmt(ni, m0, subtype, rxs, rssi, nf);
+
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ break;
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ /*
+ * Only count beacons when assoc. SCAN has its own logging.
+ * This is for connection/beacon loss/session protection almost
+ * over debugging when trying to get into a stable RUN state.
+ */
+ if (vif->cfg.assoc)
+ lvif->beacons++;
+ break;
+ default:
+ return;
+ }
+
+ lhw = ni->ni_ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+
+ /*
+ * If this direct call to mo_bss_info_changed will not work due to
+ * locking, see if queue_work() is fast enough.
+ */
+ bss_changed = lkpi_update_dtim_tsf(vif, ni, ni->ni_vap, __func__, __LINE__);
+ lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+}
+
/*
* Change link-layer address on the vif (if the vap is not started/"UP").
* This can happen if a user changes 'ether' using ifconfig.
@@ -3475,6 +3919,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
lvif = malloc(len, M_80211_VAP, M_WAITOK | M_ZERO);
mtx_init(&lvif->mtx, "lvif", NULL, MTX_DEF);
+ TASK_INIT(&lvif->sw_scan_task, 0, lkpi_sw_scan_task, lvif);
INIT_LIST_HEAD(&lvif->lsta_list);
lvif->lvif_bss = NULL;
refcount_init(&lvif->nt_unlocked, 0);
@@ -3492,7 +3937,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
/* XXX-BZ hardcoded for now! */
#if 1
- vif->chanctx_conf = NULL;
+ RCU_INIT_POINTER(vif->bss_conf.chanctx_conf, NULL);
vif->bss_conf.vif = vif;
/* vap->iv_myaddr is not set until net80211::vap_setup or vap_attach. */
IEEE80211_ADDR_COPY(vif->bss_conf.addr, mac);
@@ -3548,8 +3993,10 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
IMPROVE();
+ wiphy_lock(hw->wiphy);
error = lkpi_80211_mo_start(hw);
if (error != 0) {
+ wiphy_unlock(hw->wiphy);
ic_printf(ic, "%s: failed to start hw: %d\n", __func__, error);
mtx_destroy(&lvif->mtx);
free(lvif, M_80211_VAP);
@@ -3559,11 +4006,13 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
error = lkpi_80211_mo_add_interface(hw, vif);
if (error != 0) {
IMPROVE(); /* XXX-BZ mo_stop()? */
+ wiphy_unlock(hw->wiphy);
ic_printf(ic, "%s: failed to add interface: %d\n", __func__, error);
mtx_destroy(&lvif->mtx);
free(lvif, M_80211_VAP);
return (NULL);
}
+ wiphy_unlock(hw->wiphy);
LKPI_80211_LHW_LVIF_LOCK(lhw);
TAILQ_INSERT_TAIL(&lhw->lvif_head, lvif, lvif_entry);
@@ -3575,7 +4024,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
/* Configure tx queues (conf_tx), default WME & send BSS_CHANGED_QOS. */
IMPROVE("Hardcoded values; to fix see 802.11-2016, 9.4.2.29 EDCA Parameter Set element");
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
bzero(&txqp, sizeof(txqp));
@@ -3588,22 +4037,26 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
__func__, ac, error);
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
changed = BSS_CHANGED_QOS;
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
/* Force MC init. */
- lkpi_update_mcast_filter(ic, true);
-
- IMPROVE();
+ lkpi_update_mcast_filter(ic);
ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
+ /* Now we have a valid vap->iv_ifp. Any checksum offloading goes below. */
+
+ IMPROVE();
+
/* Override with LinuxKPI method so we can drive mac80211/cfg80211. */
lvif->iv_newstate = vap->iv_newstate;
vap->iv_newstate = lkpi_iv_newstate;
lvif->iv_update_bss = vap->iv_update_bss;
vap->iv_update_bss = lkpi_iv_update_bss;
+ lvif->iv_recv_mgmt = vap->iv_recv_mgmt;
+ vap->iv_recv_mgmt = lkpi_iv_sta_recv_mgmt;
#ifdef LKPI_80211_HW_CRYPTO
/* Key management. */
@@ -3630,13 +4083,9 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
* Modern chipset/fw/drv will do A-MPDU in drv/fw and fail
* to do so if they cannot do the crypto too.
*/
- if (!lkpi_hwcrypto && ieee80211_hw_check(hw, AMPDU_AGGREGATION))
+ if (!lkpi_hwcrypto && IEEE80211_CONF_AMPDU_OFFLOAD(ic))
vap->iv_flags_ht &= ~IEEE80211_FHT_AMPDU_RX;
#endif
-#if defined(LKPI_80211_HT)
- /* 20250125-BZ Keep A-MPDU TX cleared until we sorted out AddBA for all drivers. */
- vap->iv_flags_ht &= ~IEEE80211_FHT_AMPDU_TX;
-#endif
if (hw->max_listen_interval == 0)
hw->max_listen_interval = 7 * (ic->ic_lintval / ic->ic_bintval);
@@ -3705,6 +4154,8 @@ lkpi_ic_vap_delete(struct ieee80211vap *vap)
/* Clear up per-VIF/VAP sysctls. */
sysctl_ctx_free(&lvif->sysctl_ctx);
+ ieee80211_draintask(ic, &lvif->sw_scan_task);
+
LKPI_80211_LHW_LVIF_LOCK(lhw);
TAILQ_REMOVE(&lhw->lvif_head, lvif, lvif_entry);
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
@@ -3726,8 +4177,30 @@ lkpi_ic_vap_delete(struct ieee80211vap *vap)
static void
lkpi_ic_update_mcast(struct ieee80211com *ic)
{
+ struct ieee80211vap *vap;
+ struct lkpi_hw *lhw;
+
+ lhw = ic->ic_softc;
+ if (lhw->ops->prepare_multicast == NULL ||
+ lhw->ops->configure_filter == NULL)
+ return;
+
+ LKPI_80211_LHW_MC_LOCK(lhw);
+ /* Cleanup anything on the current list. */
+ lkpi_cleanup_mcast_list_locked(lhw);
+
+ /* Build up the new list (or allmulti). */
+ if (ic->ic_allmulti == 0) {
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if_foreach_llmaddr(vap->iv_ifp,
+ lkpi_ic_update_mcast_copy, &lhw->mc_list);
+ lhw->mc_all_multi = false;
+ } else {
+ lhw->mc_all_multi = true;
+ }
+ LKPI_80211_LHW_MC_UNLOCK(lhw);
- lkpi_update_mcast_filter(ic, false);
+ lkpi_update_mcast_filter(ic);
TRACEOK();
}
@@ -3750,8 +4223,8 @@ static void
lkpi_ic_parent(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
-#ifdef HW_START_STOP
struct ieee80211_hw *hw;
+#ifdef HW_START_STOP
int error;
#endif
bool start_all;
@@ -3759,13 +4232,11 @@ lkpi_ic_parent(struct ieee80211com *ic)
IMPROVE();
lhw = ic->ic_softc;
-#ifdef HW_START_STOP
hw = LHW_TO_HW(lhw);
-#endif
start_all = false;
/* IEEE80211_UNLOCK(ic); */
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
if (ic->ic_nrunning > 0) {
#ifdef HW_START_STOP
error = lkpi_80211_mo_start(hw);
@@ -3777,7 +4248,7 @@ lkpi_ic_parent(struct ieee80211com *ic)
lkpi_80211_mo_stop(hw, false); /* XXX SUSPEND */
#endif
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
/* IEEE80211_LOCK(ic); */
if (start_all)
@@ -3848,12 +4319,26 @@ lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
channels = supband->channels;
chan = NULL;
for (i = 0; i < supband->n_channels; i++) {
+ uint32_t flags;
if (channels[i].flags & IEEE80211_CHAN_DISABLED)
continue;
+ flags = 0;
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ flags |= IEEE80211_CHAN_G;
+ break;
+ case NL80211_BAND_5GHZ:
+ flags |= IEEE80211_CHAN_A;
+ break;
+ default:
+ panic("%s:%d: unupported band %d\n",
+ __func__, __LINE__, band);
+ }
+
chan = ieee80211_find_channel(ic,
- channels[i].center_freq, 0);
+ channels[i].center_freq, flags);
if (chan != NULL)
break;
}
@@ -3877,7 +4362,8 @@ lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
}
#endif
#if defined(LKPI_80211_VHT)
- if ((vap->iv_vht_flags & IEEE80211_FVHT_VHT) != 0) {
+ if (band == NL80211_BAND_5GHZ &&
+ (vap->iv_vht_flags & IEEE80211_FVHT_VHT) != 0) {
struct ieee80211_channel *c;
c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
@@ -3911,6 +4397,113 @@ lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
}
static void
+lkpi_enable_hw_scan(struct lkpi_hw *lhw)
+{
+
+ if (lhw->ops->hw_scan) {
+ /*
+ * Advertise full-offload scanning.
+ *
+ * Not limiting to SINGLE_SCAN_ON_ALL_BANDS here as otherwise
+ * we essentially disable hw_scan for all drivers not setting
+ * the flag.
+ */
+ lhw->ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD;
+ lhw->scan_flags |= LKPI_LHW_SCAN_HW;
+ }
+}
+
+#ifndef LKPI_80211_USE_SCANLIST
+static const uint32_t chan_pri[] = {
+ 5180, 5500, 5745,
+ 5260, 5580, 5660, 5825,
+ 5220, 5300, 5540, 5620, 5700, 5785, 5865,
+ 2437, 2412, 2422, 2462, 2472, 2432, 2452
+};
+
+static int
+lkpi_scan_chan_list_idx(const struct linuxkpi_ieee80211_channel *lc)
+{
+ int i;
+
+ for (i = 0; i < nitems(chan_pri); i++) {
+ if (lc->center_freq == chan_pri[i])
+ return (i);
+ }
+
+ return (-1);
+}
+
+static int
+lkpi_scan_chan_list_comp(const struct linuxkpi_ieee80211_channel *lc1,
+ const struct linuxkpi_ieee80211_channel *lc2)
+{
+ int idx1, idx2;
+
+ /* Find index in list. */
+ idx1 = lkpi_scan_chan_list_idx(lc1);
+ idx2 = lkpi_scan_chan_list_idx(lc2);
+
+ if (idx1 == -1 && idx2 != -1)
+ return (1);
+ if (idx1 != -1 && idx2 == -1)
+ return (-1);
+
+ /* Neither on the list, use center_freq. */
+ if (idx1 == -1 && idx2 == -1)
+ return (lc1->center_freq - lc2->center_freq);
+
+ /* Whichever is first in the list. */
+ return (idx1 - idx2);
+}
+
+static void
+lkpi_scan_chan_list_resort(struct linuxkpi_ieee80211_channel **cpp, size_t nchan)
+{
+ struct linuxkpi_ieee80211_channel *lc, *nc;
+ size_t i, j;
+ int rc;
+
+ for (i = (nchan - 1); i > 0; i--) {
+ for (j = i; j > 0 ; j--) {
+ lc = *(cpp + j);
+ nc = *(cpp + j - 1);
+ rc = lkpi_scan_chan_list_comp(lc, nc);
+ if (rc < 0) {
+ *(cpp + j) = nc;
+ *(cpp + j - 1) = lc;
+ }
+ }
+ }
+}
+
+static bool
+lkpi_scan_chan(struct linuxkpi_ieee80211_channel *c,
+ struct ieee80211com *ic, bool log)
+{
+
+ if ((c->flags & IEEE80211_CHAN_DISABLED) != 0) {
+ if (log)
+ TRACE_SCAN(ic, "Skipping disabled chan "
+ "on band %s [%#x/%u/%#x]",
+ lkpi_nl80211_band_name(c->band), c->hw_value,
+ c->center_freq, c->flags);
+ return (false);
+ }
+ if (isclr(ic->ic_chan_active, ieee80211_mhz2ieee(c->center_freq,
+ lkpi_nl80211_band_to_net80211_band(c->band)))) {
+ if (log)
+ TRACE_SCAN(ic, "Skipping !active chan "
+ "on band %s [%#x/%u/%#x]",
+ lkpi_nl80211_band_name(c->band), c->hw_value,
+ c->center_freq, c->flags);
+ return (false);
+ }
+ return (true);
+}
+#endif
+
+static void
lkpi_ic_scan_start(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
@@ -3923,33 +4516,52 @@ lkpi_ic_scan_start(struct ieee80211com *ic)
bool is_hw_scan;
lhw = ic->ic_softc;
+ ss = ic->ic_scan;
+ vap = ss->ss_vap;
+ TRACE_SCAN(ic, "scan_flags %b", lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0) {
/* A scan is still running. */
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ TRACE_SCAN(ic, "Trying to start new scan while still running; "
+ "cancelling new net80211 scan; scan_flags %b",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+ ieee80211_cancel_scan(vap);
return;
}
is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
- ss = ic->ic_scan;
- vap = ss->ss_vap;
+#if 0
if (vap->iv_state != IEEE80211_S_SCAN) {
- IMPROVE("We need to be able to scan if not in S_SCAN");
+ TODO("We need to be able to scan if not in S_SCAN");
+ TRACE_SCAN(ic, "scan_flags %b iv_state %d",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS, vap->iv_state);
+ ieee80211_cancel_scan(vap);
return;
}
+#endif
hw = LHW_TO_HW(lhw);
if (!is_hw_scan) {
/* If hw_scan is cleared clear FEXT_SCAN_OFFLOAD too. */
vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
-sw_scan:
+
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
if (vap->iv_state == IEEE80211_S_SCAN)
lkpi_hw_conf_idle(hw, false);
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ lhw->scan_flags |= LKPI_LHW_SCAN_RUNNING;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+
+ lkpi_update_mcast_filter(ic);
+
+ TRACE_SCAN(vap->iv_ic, "Starting SW_SCAN: scan_flags %b",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS);
lkpi_80211_mo_sw_scan_start(hw, vif, vif->addr);
/* net80211::scan_start() handled PS for us. */
IMPROVE();
@@ -3964,6 +4576,9 @@ sw_scan:
struct cfg80211_scan_6ghz_params *s6gp;
size_t chan_len, nchan, ssids_len, s6ghzlen;
int band, i, ssid_count, common_ie_len;
+#ifndef LKPI_80211_USE_SCANLIST
+ int n;
+#endif
uint32_t band_mask;
uint8_t *ie, *ieend;
bool running;
@@ -3975,7 +4590,8 @@ sw_scan:
band_mask = 0;
nchan = 0;
if (ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) {
-#if 0 /* Avoid net80211 scan lists until it has proper scan offload support. */
+#ifdef LKPI_80211_USE_SCANLIST
+ /* Avoid net80211 scan lists until it has proper scan offload support. */
for (i = ss->ss_next; i < ss->ss_last; i++) {
nchan++;
band = lkpi_net80211_chan_to_nl80211_band(
@@ -3993,8 +4609,17 @@ sw_scan:
continue;
}
if (hw->wiphy->bands[band] != NULL) {
- nchan += hw->wiphy->bands[band]->n_channels;
+ struct linuxkpi_ieee80211_channel *channels;
+ int n;
+
band_mask |= (1 << band);
+
+ channels = hw->wiphy->bands[band]->channels;
+ n = hw->wiphy->bands[band]->n_channels;
+ for (i = 0; i < n; i++) {
+ if (lkpi_scan_chan(&channels[i], ic, true))
+ nchan++;
+ }
}
}
#endif
@@ -4033,11 +4658,32 @@ sw_scan:
/* hw_req->req.wdev */
hw_req->req.wiphy = hw->wiphy;
hw_req->req.no_cck = false; /* XXX */
-#if 0
- /* This seems to pessimise default scanning behaviour. */
- hw_req->req.duration_mandatory = TICKS_2_USEC(ss->ss_mindwell);
- hw_req->req.duration = TICKS_2_USEC(ss->ss_maxdwell);
-#endif
+
+ /*
+ * In general setting duration[_mandatory] seems to pessimise
+ * default scanning behaviour. We only use it for BGSCANnig
+ * to keep the dwell times small.
+ * Setting duration_mandatory makes this the maximum dwell
+ * time (otherwise may be shorter). Duration is in TU.
+ */
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) != 0) {
+ unsigned long dwell;
+
+ if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0 ||
+ (vap->iv_flags & IEEE80211_F_BGSCAN) == 0)
+ ic_printf(ic, "BGSCAN despite off: %b, %b, %b\n",
+ ic->ic_flags_ext, IEEE80211_FEXT_BITS,
+ vap->iv_flags, IEEE80211_F_BITS,
+ ic->ic_caps, IEEE80211_C_BITS);
+
+ dwell = ss->ss_mindwell;
+ if (dwell == 0)
+ dwell = msecs_to_ticks(20);
+
+ hw_req->req.duration_mandatory = true;
+ hw_req->req.duration = TICKS_2_USEC(dwell) / 1024;
+ }
+
#ifdef __notyet__
hw_req->req.flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
memcpy(hw_req->req.mac_addr, xxx, IEEE80211_ADDR_LEN);
@@ -4048,16 +4694,16 @@ sw_scan:
hw_req->req.n_channels = nchan;
cpp = (struct linuxkpi_ieee80211_channel **)(hw_req + 1);
lc = (struct linuxkpi_ieee80211_channel *)(cpp + nchan);
+#ifdef LKPI_80211_USE_SCANLIST
for (i = 0; i < nchan; i++) {
*(cpp + i) =
(struct linuxkpi_ieee80211_channel *)(lc + i);
}
-#if 0 /* Avoid net80211 scan lists until it has proper scan offload support. */
+ /* Avoid net80211 scan lists until it has proper scan offload support. */
for (i = 0; i < nchan; i++) {
struct ieee80211_channel *c;
c = ss->ss_chans[ss->ss_next + i];
- lc->hw_value = c->ic_ieee;
lc->center_freq = c->ic_freq; /* XXX */
/* lc->flags */
lc->band = lkpi_net80211_chan_to_nl80211_band(c);
@@ -4066,7 +4712,9 @@ sw_scan:
lc++;
}
#else
- for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ /* Add bands in reverse order for scanning. */
+ n = 0;
+ for (band = NUM_NL80211_BANDS - 1; band >= 0; band--) {
struct ieee80211_supported_band *supband;
struct linuxkpi_ieee80211_channel *channels;
@@ -4081,9 +4729,27 @@ sw_scan:
channels = supband->channels;
for (i = 0; i < supband->n_channels; i++) {
- *lc = channels[i];
- lc++;
+ if (lkpi_scan_chan(&channels[i], ic, false))
+ *(cpp + n++) = &channels[i];
+ }
+ }
+ if (lkpi_order_scanlist)
+ lkpi_scan_chan_list_resort(cpp, nchan);
+
+ if ((linuxkpi_debug_80211 & D80211_SCAN) != 0) {
+ printf("%s:%d: %s SCAN Channel List (nchan=%zu): ",
+ __func__, __LINE__, ic->ic_name, nchan);
+ for (i = 0; i < nchan; i++) {
+ struct linuxkpi_ieee80211_channel *xc;
+
+ xc = *(cpp + i);
+ printf(" %d(%d)",
+ ieee80211_mhz2ieee(xc->center_freq,
+ lkpi_nl80211_band_to_net80211_band(
+ xc->band)),
+ xc->center_freq);
}
+ printf("\n");
}
#endif
@@ -4113,6 +4779,7 @@ sw_scan:
ieend = lkpi_scan_ies_add(ie, &hw_req->ies, band_mask, vap, hw);
hw_req->req.ie = ie;
hw_req->req.ie_len = ieend - ie;
+ hw_req->req.scan_start = jiffies;
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
@@ -4130,11 +4797,30 @@ sw_scan:
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
if (running) {
free(hw_req, M_LKPI80211);
+ TRACE_SCAN(ic, "Trying to start new scan while still "
+ "running (2); cancelling new net80211 scan; "
+ "scan_flags %b",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+ ieee80211_cancel_scan(vap);
return;
}
+ lkpi_update_mcast_filter(ic);
+ TRACE_SCAN(ic, "Starting HW_SCAN: scan_flags %b, "
+ "ie_len %d, n_ssids %d, n_chan %d, common_ie_len %d [%d, %d]",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS, hw_req->req.ie_len,
+ hw_req->req.n_ssids, hw_req->req.n_channels,
+ hw_req->ies.common_ie_len,
+ hw_req->ies.len[NL80211_BAND_2GHZ],
+ hw_req->ies.len[NL80211_BAND_5GHZ]);
+
error = lkpi_80211_mo_hw_scan(hw, vif, hw_req);
if (error != 0) {
+ bool scan_done;
+ int e;
+
+ TRACE_SCAN(ic, "hw_scan failed; scan_flags %b, error %d",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS, error);
ieee80211_cancel_scan(vap);
/*
@@ -4151,13 +4837,35 @@ sw_scan:
* So we cannot rely on that behaviour and have to check
* and balance between both code paths.
*/
+ e = 0;
+ scan_done = true;
LKPI_80211_LHW_SCAN_LOCK(lhw);
if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0) {
+
free(lhw->hw_req, M_LKPI80211);
lhw->hw_req = NULL;
+ /*
+ * The ieee80211_cancel_scan() above runs in a
+ * taskq and it may take ages for the previous
+ * scan to clear; starting a new one right away
+ * we run into the problem that the old one is
+ * still active.
+ */
+ e = msleep(lhw, &lhw->scan_mtx, 0, "lhwscanstop", hz);
+ scan_done = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
+
+ /*
+ * Now we can clear running if no one else did.
+ */
lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
}
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ lkpi_update_mcast_filter(ic);
+ if (!scan_done) {
+ ic_printf(ic, "ERROR: %s: timeout/error to wait "
+ "for ieee80211_cancel_scan: %d\n", __func__, e);
+ return;
+ }
/*
* XXX-SIGH magic number.
@@ -4165,24 +4873,15 @@ sw_scan:
* not possible. Fall back to sw scan in that case.
*/
if (error == 1) {
- LKPI_80211_LHW_SCAN_LOCK(lhw);
- lhw->scan_flags &= ~LKPI_LHW_SCAN_HW;
- LKPI_80211_LHW_SCAN_UNLOCK(lhw);
/*
- * XXX If we clear this now and later a driver
- * thinks it * can do a hw_scan again, we will
- * currently not re-enable it?
+ * We need to put this into some defered context
+ * the net80211 scan may not be done yet
+ * (ic_flags & IEEE80211_F_SCAN) and we cannot
+ * wait here; if we do scan_curchan_task always
+ * runs after our timeout to finalize the scan.
*/
- vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
- ieee80211_start_scan(vap,
- IEEE80211_SCAN_ACTIVE |
- IEEE80211_SCAN_NOPICK |
- IEEE80211_SCAN_ONCE,
- IEEE80211_SCAN_FOREVER,
- ss->ss_mindwell ? ss->ss_mindwell : msecs_to_ticks(20),
- ss->ss_maxdwell ? ss->ss_maxdwell : msecs_to_ticks(200),
- vap->iv_des_nssid, vap->iv_des_ssid);
- goto sw_scan;
+ ieee80211_runtask(ic, &lvif->sw_scan_task);
+ return;
}
ic_printf(ic, "ERROR: %s: hw_scan returned %d\n",
@@ -4192,12 +4891,50 @@ sw_scan:
}
static void
+lkpi_sw_scan_task(void *arg, int pending __unused)
+{
+ struct lkpi_hw *lhw;
+ struct lkpi_vif *lvif;
+ struct ieee80211vap *vap;
+ struct ieee80211_scan_state *ss;
+
+ lvif = arg;
+ vap = LVIF_TO_VAP(lvif);
+ lhw = vap->iv_ic->ic_softc;
+ ss = vap->iv_ic->ic_scan;
+
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ /*
+ * We will re-enable this at scan_end calling lkpi_enable_hw_scan().
+ * IEEE80211_FEXT_SCAN_OFFLOAD will be cleared by lkpi_ic_scan_start.
+ */
+ lhw->scan_flags &= ~LKPI_LHW_SCAN_HW;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+
+ TRACE_SCAN(vap->iv_ic, "Triggering SW_SCAN: pending %d, scan_flags %b",
+ pending, lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+
+ /*
+ * This will call ic_scan_start() and we will get into the right path
+ * unless other scans started in between.
+ */
+ ieee80211_start_scan(vap,
+ IEEE80211_SCAN_ONCE,
+ msecs_to_ticks(10000), /* 10000 ms (=~ 50 chan * 200 ms) */
+ ss->ss_mindwell ? ss->ss_mindwell : msecs_to_ticks(20),
+ ss->ss_maxdwell ? ss->ss_maxdwell : msecs_to_ticks(200),
+ vap->iv_des_nssid, vap->iv_des_ssid);
+}
+
+static void
lkpi_ic_scan_end(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
bool is_hw_scan;
lhw = ic->ic_softc;
+ TRACE_SCAN(ic, "scan_flags %b", lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) == 0) {
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
@@ -4226,6 +4963,16 @@ lkpi_ic_scan_end(struct ieee80211com *ic)
if (vap->iv_state == IEEE80211_S_SCAN)
lkpi_hw_conf_idle(hw, true);
}
+
+ /*
+ * In case we disabled the hw_scan in lkpi_ic_scan_start() and
+ * switched to swscan, re-enable hw_scan if available.
+ */
+ lkpi_enable_hw_scan(lhw);
+
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ wakeup(lhw);
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
}
static void
@@ -4236,6 +4983,10 @@ lkpi_ic_scan_curchan(struct ieee80211_scan_state *ss,
bool is_hw_scan;
lhw = ss->ss_ic->ic_softc;
+ TRACE_SCAN(ss->ss_ic, "scan_flags %b chan %d maxdwell %lu",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ ss->ss_ic->ic_curchan->ic_ieee, maxdwell);
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
@@ -4250,6 +5001,10 @@ lkpi_ic_scan_mindwell(struct ieee80211_scan_state *ss)
bool is_hw_scan;
lhw = ss->ss_ic->ic_softc;
+ TRACE_SCAN(ss->ss_ic, "scan_flags %b chan %d mindwell %lu",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ ss->ss_ic->ic_curchan->ic_ieee, ss->ss_mindwell);
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
@@ -4426,11 +5181,23 @@ lkpi_ic_node_free(struct ieee80211_node *ni)
lhw->ic_node_free(ni);
}
+/*
+ * lkpi_xmit() called from both the (*ic_raw_xmit) as well as the (*ic_transmit)
+ * call path.
+ * Unfortunately they have slightly different invariants. See
+ * ieee80211_raw_output() and ieee80211_parent_xmitpkt().
+ * Both take care of the ni reference in case of error, and otherwise during
+ * the callback after transmit.
+ * The difference is that in case of error (*ic_raw_xmit) needs us to release
+ * the mbuf, while (*ic_transmit) will free the mbuf itself.
+ */
static int
-lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
- const struct ieee80211_bpf_params *params __unused)
+lkpi_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params __unused,
+ bool freem)
{
struct lkpi_sta *lsta;
+ int error;
lsta = ni->ni_drv_data;
LKPI_80211_LSTA_TXQ_LOCK(lsta);
@@ -4445,16 +5212,24 @@ lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
if (!lsta->txq_ready) {
#endif
LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
- /*
- * Free the mbuf (do NOT release ni ref for the m_pkthdr.rcvif!
- * ieee80211_raw_output() does that in case of error).
- */
- m_free(m);
+ if (freem)
+ m_free(m);
return (ENETDOWN);
}
/* Queue the packet and enqueue the task to handle it. */
- mbufq_enqueue(&lsta->txq, m);
+ error = mbufq_enqueue(&lsta->txq, m);
+ if (error != 0) {
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
+ if (freem)
+ m_free(m);
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_TX)
+ ic_printf(ni->ni_ic, "%s: mbufq_enqueue failed: %d\n",
+ __func__, error);
+#endif
+ return (ENETDOWN);
+ }
taskqueue_enqueue(taskqueue_thread, &lsta->txq_task);
LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
@@ -4468,36 +5243,93 @@ lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
return (0);
}
+static int
+lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params __unused)
+{
+ return (lkpi_xmit(ni, m, NULL, true));
+}
+
#ifdef LKPI_80211_HW_CRYPTO
+/*
+ * This is a bit of a hack given we know we are operating on a
+ * single frame and we know that hardware will deal with it.
+ * But otherwise the enmic bit and the encrypt bit need to be
+ * decoupled.
+ */
static int
-lkpi_hw_crypto_prepare(struct lkpi_sta *lsta, struct ieee80211_key *k,
- struct sk_buff *skb)
+lkpi_hw_crypto_prepare_tkip(struct ieee80211_key *k,
+ struct ieee80211_key_conf *kc, struct sk_buff *skb)
{
- struct ieee80211_tx_info *info;
- struct ieee80211_key_conf *kc;
struct ieee80211_hdr *hdr;
uint32_t hlen, hdrlen;
uint8_t *p;
- KASSERT(lsta != NULL, ("%s: lsta is NULL", __func__));
- KASSERT(k != NULL, ("%s: key is NULL", __func__));
- KASSERT(skb != NULL, ("%s: skb is NULL", __func__));
+ /*
+ * TKIP only happens on data.
+ */
+ hdr = (void *)skb->data;
+ if (!ieee80211_is_data_present(hdr->frame_control))
+ return (0);
- kc = lsta->kc[k->wk_keyix];
+ /*
+ * "enmic" (though we do not do that).
+ */
+ /* any conditions to not apply this? */
+ if (skb_tailroom(skb) < k->wk_cipher->ic_miclen)
+ return (ENOBUFS);
- info = IEEE80211_SKB_CB(skb);
- info->control.hw_key = kc;
+ p = skb_put(skb, k->wk_cipher->ic_miclen);
+ if ((kc->flags & IEEE80211_KEY_FLAG_PUT_MIC_SPACE) != 0)
+ goto encrypt;
- /* MUST NOT happen. KASSERT? */
- if (kc == NULL) {
- ic_printf(lsta->ni->ni_ic, "%s: lsta %p k %p skb %p, "
- "kc is NULL on hw crypto offload\n", __func__, lsta, k, skb);
- return (ENXIO);
- }
+ /*
+ * (*enmic) which we hopefully do not have to do with hw accel.
+ * That means if we make it here we have a problem.
+ */
+ TODO("(*enmic)");
+ return (ENXIO);
+encrypt:
+ /*
+ * "encrypt" (though we do not do that).
+ */
+ /*
+ * Check if we have anything to do as requested by driver
+ * or if we are done?
+ */
+ if ((kc->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) == 0 &&
+ (kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV) == 0)
+ return (0);
+
+ hlen = k->wk_cipher->ic_header;
+ if (skb_headroom(skb) < hlen)
+ return (ENOBUFS);
+
+ hdr = (void *)skb->data;
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ p = skb_push(skb, hlen);
+ memmove(p, p + hlen, hdrlen);
+
+ /* If driver request space only we are done. */
+ if ((kc->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) != 0)
+ return (0);
+
+ p += hdrlen;
+ k->wk_cipher->ic_setiv(k, p);
+
+ /* If we make it hear we do sw encryption. */
+ TODO("sw encrypt");
+ return (ENXIO);
+}
- IMPROVE("the following should be WLAN_CIPHER_SUITE specific");
- /* We currently only support CCMP so we hardcode things here. */
+static int
+lkpi_hw_crypto_prepare_ccmp(struct ieee80211_key *k,
+ struct ieee80211_key_conf *kc, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+ uint32_t hlen, hdrlen;
+ uint8_t *p;
hdr = (void *)skb->data;
@@ -4514,7 +5346,7 @@ lkpi_hw_crypto_prepare(struct lkpi_sta *lsta, struct ieee80211_key *k,
hlen = k->wk_cipher->ic_header;
if (skb_headroom(skb) < hlen)
- return (ENOSPC);
+ return (ENOBUFS);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
p = skb_push(skb, hlen);
@@ -4529,6 +5361,68 @@ lkpi_hw_crypto_prepare(struct lkpi_sta *lsta, struct ieee80211_key *k,
return (0);
}
+
+static int
+lkpi_hw_crypto_prepare(struct lkpi_sta *lsta, struct ieee80211_key *k,
+ struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info;
+ struct ieee80211_key_conf *kc;
+
+ KASSERT(lsta != NULL, ("%s: lsta is NULL", __func__));
+ KASSERT(k != NULL, ("%s: key is NULL", __func__));
+ KASSERT(skb != NULL, ("%s: skb is NULL", __func__));
+
+ kc = lsta->kc[k->wk_keyix];
+
+ info = IEEE80211_SKB_CB(skb);
+ info->control.hw_key = kc;
+
+ /* MUST NOT happen. KASSERT? */
+ if (kc == NULL) {
+ ic_printf(lsta->ni->ni_ic, "%s: lsta %p k %p skb %p, "
+ "kc is NULL on hw crypto offload\n", __func__, lsta, k, skb);
+ return (ENXIO);
+ }
+
+ switch (kc->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ return (lkpi_hw_crypto_prepare_tkip(k, kc, skb));
+ case WLAN_CIPHER_SUITE_CCMP:
+ return (lkpi_hw_crypto_prepare_ccmp(k, kc, skb));
+ case WLAN_CIPHER_SUITE_GCMP:
+ return (lkpi_hw_crypto_prepare_ccmp(k, kc, skb));
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ default:
+ ic_printf(lsta->ni->ni_ic, "%s: lsta %p k %p kc %p skb %p, "
+ "unsupported cipher suite %u (%s)\n", __func__, lsta, k, kc,
+ skb, kc->cipher, lkpi_cipher_suite_to_name(kc->cipher));
+ return (EOPNOTSUPP);
+ }
+}
+
+static uint8_t
+lkpi_hw_crypto_tailroom(struct lkpi_sta *lsta, struct ieee80211_key *k)
+{
+ struct ieee80211_key_conf *kc;
+
+ kc = lsta->kc[k->wk_keyix];
+ if (kc == NULL)
+ return (0);
+
+ IMPROVE("which other flags need tailroom?");
+ if (kc->flags & (IEEE80211_KEY_FLAG_PUT_MIC_SPACE))
+ return (32); /* Large enough to hold everything and pow2. */
+
+ return (0);
+}
#endif
static void
@@ -4551,7 +5445,7 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
struct lkpi_txq *ltxq;
void *buf;
ieee80211_keyix keyix;
- uint8_t ac, tid;
+ uint8_t ac, tid, tailroom;
M_ASSERTPKTHDR(m);
#ifdef LINUXKPI_DEBUG_80211
@@ -4609,13 +5503,20 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
ieee80211_radiotap_tx(ni->ni_vap, m);
}
+#ifdef LKPI_80211_HW_CRYPTO
+ if (lkpi_hwcrypto && keyix != IEEE80211_KEYIX_NONE)
+ tailroom = lkpi_hw_crypto_tailroom(lsta, k);
+ else
+#endif
+ tailroom = 0;
+
/*
* net80211 should handle hw->extra_tx_headroom.
* Though for as long as we are copying we don't mind.
* XXX-BZ rtw88 asks for too much headroom for ipv6+tcp:
* https://lists.freebsd.org/archives/freebsd-transport/2022-February/000012.html
*/
- skb = dev_alloc_skb(hw->extra_tx_headroom + m->m_pkthdr.len);
+ skb = dev_alloc_skb(hw->extra_tx_headroom + tailroom + m->m_pkthdr.len);
if (skb == NULL) {
static uint8_t skb_alloc_failures = 0;
@@ -4732,19 +5633,19 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
skb_queue_tail(&ltxq->skbq, skb);
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_TX)
- printf("%s:%d mo_wake_tx_queue :: %d %u lsta %p sta %p "
+ printf("%s:%d mo_wake_tx_queue :: %d %lu lsta %p sta %p "
"ni %p %6D skb %p lxtq %p { qlen %u, ac %d tid %u } "
"WAKE_TX_Q ac %d prio %u qmap %u\n",
__func__, __LINE__,
- curthread->td_tid, (unsigned int)ticks,
+ curthread->td_tid, jiffies,
lsta, sta, ni, ni->ni_macaddr, ":", skb, ltxq,
skb_queue_len(&ltxq->skbq), ltxq->txq.ac,
ltxq->txq.tid, ac, skb->priority, skb->qmap);
#endif
LKPI_80211_LTXQ_UNLOCK(ltxq);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq);
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
return;
ops_tx:
@@ -4757,9 +5658,9 @@ ops_tx:
#endif
memset(&control, 0, sizeof(control));
control.sta = sta;
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
lkpi_80211_mo_tx(hw, &control, skb);
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
}
static void
@@ -4826,7 +5727,7 @@ lkpi_ic_transmit(struct ieee80211com *ic, struct mbuf *m)
struct ieee80211_node *ni;
ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
- return (lkpi_ic_raw_xmit(ni, m, NULL));
+ return (lkpi_xmit(ni, m, NULL, false));
}
#ifdef LKPI_80211_HT
@@ -4920,9 +5821,9 @@ lkpi_ic_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
params.amsdu = false;
IEEE80211_UNLOCK(ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(ic);
if (error != 0) {
ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p tap %p\n",
@@ -4998,9 +5899,9 @@ lkpi_ic_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap
}
IEEE80211_UNLOCK(ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(ic);
if (error != 0) {
ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p tap %p\n",
@@ -5057,9 +5958,9 @@ lkpi_ic_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
params.amsdu = false;
IEEE80211_UNLOCK(ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(ic);
if (error != 0) {
ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p tap %p\n",
@@ -5134,6 +6035,12 @@ lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap
return (-ENXIO);
}
+ if (lsta->state != IEEE80211_STA_AUTHORIZED) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, sta %p state %d not AUTHORIZED\n",
+ __func__, lsta, ni, vap, sta, lsta->state);
+ return (-ENXIO);
+ }
+
params.sta = sta;
params.action = IEEE80211_AMPDU_RX_START;
params.buf_size = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_BUFSIZ);
@@ -5155,9 +6062,9 @@ lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap
else
params.amsdu = false;
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
if (error != 0) {
ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n",
__func__, error, ni, rap);
@@ -5210,13 +6117,35 @@ lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
lsta = ni->ni_drv_data;
+ if (lsta == NULL) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, lsta is NULL\n",
+ __func__, lsta, ni, vap);
+ goto net80211_only;
+ }
sta = LSTA_TO_STA(lsta);
+ if (!lsta->added_to_drv) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, sta %p not added to firmware\n",
+ __func__, lsta, ni, vap, sta);
+ goto net80211_only;
+ }
+
+ if (lsta->state != IEEE80211_STA_AUTHORIZED) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, sta %p state %d not AUTHORIZED\n",
+ __func__, lsta, ni, vap, sta, lsta->state);
+ goto net80211_only;
+ }
+
IMPROVE_HT("This really should be passed from ht_recv_action_ba_delba.");
for (tid = 0; tid < WME_NUM_TID; tid++) {
if (&ni->ni_rx_ampdu[tid] == rap)
break;
}
+ if (tid == WME_NUM_TID) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, sta %p TID not found\n",
+ __func__, lsta, ni, vap, sta);
+ goto net80211_only;
+ }
params.sta = sta;
params.action = IEEE80211_AMPDU_RX_STOP;
@@ -5229,9 +6158,9 @@ lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
ic_locked = IEEE80211_IS_LOCKED(ic);
if (ic_locked)
IEEE80211_UNLOCK(ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
if (ic_locked)
IEEE80211_LOCK(ic);
if (error != 0)
@@ -5351,8 +6280,9 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
cflags &= ~NET80211_CBW_FLAG_HT40;
error = ieee80211_add_channel_cbw(c, maxchan, n,
- channels[i].hw_value, channels[i].center_freq,
- channels[i].max_power,
+ ieee80211_mhz2ieee(channels[i].center_freq,
+ lkpi_nl80211_band_to_net80211_band(channels[i].band)),
+ channels[i].center_freq, channels[i].max_power,
nflags, bands, cflags);
/* net80211::ENOBUFS: *n >= maxchans */
if (error != 0 && error != ENOBUFS)
@@ -5379,7 +6309,7 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
NL80211_BAND_5GHZ);
#ifdef LKPI_80211_VHT
- if (hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_supported){
+ if (hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_supported) {
ic->ic_flags_ext |= IEEE80211_FEXT_VHT;
ic->ic_vht_cap.vht_cap_info =
@@ -5423,8 +6353,9 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
cflags &= ~NET80211_CBW_FLAG_HT40;
error = ieee80211_add_channel_cbw(c, maxchan, n,
- channels[i].hw_value, channels[i].center_freq,
- channels[i].max_power,
+ ieee80211_mhz2ieee(channels[i].center_freq,
+ lkpi_nl80211_band_to_net80211_band(channels[i].band)),
+ channels[i].center_freq, channels[i].max_power,
nflags, bands, cflags);
/* net80211::ENOBUFS: *n >= maxchans */
if (error != 0 && error != ENOBUFS)
@@ -5469,20 +6400,25 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
lhw = wiphy_priv(wiphy);
lhw->ops = ops;
- LKPI_80211_LHW_LOCK_INIT(lhw);
LKPI_80211_LHW_SCAN_LOCK_INIT(lhw);
LKPI_80211_LHW_TXQ_LOCK_INIT(lhw);
+ spin_lock_init(&lhw->txq_lock);
sx_init_flags(&lhw->lvif_sx, "lhw-lvif", SX_RECURSE | SX_DUPOK);
+ LKPI_80211_LHW_MC_LOCK_INIT(lhw);
TAILQ_INIT(&lhw->lvif_head);
+ __hw_addr_init(&lhw->mc_list);
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
lhw->txq_generation[ac] = 1;
TAILQ_INIT(&lhw->scheduled_txqs[ac]);
}
+ /* Chanctx_conf */
+ INIT_LIST_HEAD(&lhw->lchanctx_list);
+
/* Deferred RX path. */
LKPI_80211_LHW_RXQ_LOCK_INIT(lhw);
TASK_INIT(&lhw->rxq_task, 0, lkpi_80211_lhw_rxq_task, lhw);
- mbufq_init(&lhw->rxq, IFQ_MAXLEN);
+ mbufq_init(&lhw->rxq, 32 * NAPI_POLL_WEIGHT);
lhw->rxq_stopped = false;
/*
@@ -5526,6 +6462,7 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
/* Flush mbufq (make sure to release ni refs!). */
m = mbufq_dequeue(&lhw->rxq);
while (m != NULL) {
+#ifdef LKPI_80211_USE_MTAG
struct m_tag *mtag;
mtag = m_tag_locate(m, MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI, NULL);
@@ -5535,6 +6472,14 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
ieee80211_free_node(rxni->ni);
}
+#else
+ if (m->m_pkthdr.PH_loc.ptr != NULL) {
+ struct ieee80211_node *ni;
+
+ ni = m->m_pkthdr.PH_loc.ptr;
+ ieee80211_free_node(ni);
+ }
+#endif
m_freem(m);
m = mbufq_dequeue(&lhw->rxq);
}
@@ -5542,26 +6487,60 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
__func__, lhw, mbufq_len(&lhw->rxq)));
LKPI_80211_LHW_RXQ_LOCK_DESTROY(lhw);
+ /* Chanctx_conf. */
+ if (!list_empty_careful(&lhw->lchanctx_list)) {
+ struct lkpi_chanctx *lchanctx, *next;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ list_for_each_entry_safe(lchanctx, next, &lhw->lchanctx_list, entry) {
+ if (lchanctx->added_to_drv) {
+ /* In reality we should panic? */
+ chanctx_conf = &lchanctx->chanctx_conf;
+ lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
+ }
+ list_del(&lchanctx->entry);
+ free(lchanctx, M_LKPI80211);
+ }
+ }
+
+ LKPI_80211_LHW_MC_LOCK(lhw);
+ lkpi_cleanup_mcast_list_locked(lhw);
+ LKPI_80211_LHW_MC_UNLOCK(lhw);
+
/* Cleanup more of lhw here or in wiphy_free()? */
+ spin_lock_destroy(&lhw->txq_lock);
LKPI_80211_LHW_TXQ_LOCK_DESTROY(lhw);
LKPI_80211_LHW_SCAN_LOCK_DESTROY(lhw);
- LKPI_80211_LHW_LOCK_DESTROY(lhw);
sx_destroy(&lhw->lvif_sx);
+ LKPI_80211_LHW_MC_LOCK_DESTROY(lhw)
IMPROVE();
}
void
-linuxkpi_set_ieee80211_dev(struct ieee80211_hw *hw, char *name)
+linuxkpi_set_ieee80211_dev(struct ieee80211_hw *hw)
{
struct lkpi_hw *lhw;
struct ieee80211com *ic;
+ struct device *dev;
lhw = HW_TO_LHW(hw);
ic = lhw->ic;
- /* Now set a proper name before ieee80211_ifattach(). */
+ /* Save the backpointer from net80211 to LinuxKPI. */
ic->ic_softc = lhw;
- ic->ic_name = name;
+
+ /*
+ * Set a proper name before ieee80211_ifattach() if dev is set.
+ * ath1xk also unset the dev so we need to check.
+ */
+ dev = wiphy_dev(hw->wiphy);
+ if (dev != NULL) {
+ ic->ic_name = dev_name(dev);
+ } else {
+ TODO("adjust arguments to still have the old dev or go through "
+ "the hoops of getting the bsddev from hw and detach; "
+ "or do in XXX; check ath1kx drivers");
+ }
/* XXX-BZ do we also need to set wiphy name? */
}
@@ -5638,21 +6617,25 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
IEEE80211_C_SHSLOT | /* short slot time supported */
IEEE80211_C_SHPREAMBLE /* short preamble supported */
;
-#if 0
- /* Scanning is a different kind of beast to re-work. */
- ic->ic_caps |= IEEE80211_C_BGSCAN;
+
+#ifdef LKPI_80211_BGSCAN
+ if (lhw->ops->hw_scan)
+ ic->ic_caps |= IEEE80211_C_BGSCAN;
+#endif
+
+ lkpi_enable_hw_scan(lhw);
+
+ /* Does HW support Fragmentation offload? */
+ if (ieee80211_hw_check(hw, SUPPORTS_TX_FRAG))
+ ic->ic_flags_ext |= IEEE80211_FEXT_FRAG_OFFLOAD;
+
+ /* Does HW support full AMPDU[-TX] offload? */
+ if (ieee80211_hw_check(hw, AMPDU_AGGREGATION))
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_OFFLOAD;
+#ifdef __notyet__
+ if (ieee80211_hw_check(hw, TX_AMSDU))
+ if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU))
#endif
- if (lhw->ops->hw_scan) {
- /*
- * Advertise full-offload scanning.
- *
- * Not limiting to SINGLE_SCAN_ON_ALL_BANDS here as otherwise
- * we essentially disable hw_scan for all drivers not setting
- * the flag.
- */
- ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD;
- lhw->scan_flags |= LKPI_LHW_SCAN_HW;
- }
/*
* The wiphy variables report bitmasks of avail antennas.
@@ -5673,9 +6656,45 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
ic->ic_cryptocaps = 0;
#ifdef LKPI_80211_HW_CRYPTO
if (lkpi_hwcrypto && hw->wiphy->n_cipher_suites > 0) {
- for (i = 0; i < hw->wiphy->n_cipher_suites; i++)
- ic->ic_cryptocaps |= lkpi_l80211_to_net80211_cyphers(
- hw->wiphy->cipher_suites[i]);
+ uint32_t hwciphers;
+
+ hwciphers = 0;
+ for (i = 0; i < hw->wiphy->n_cipher_suites; i++) {
+ uint32_t cs;
+
+ cs = lkpi_l80211_to_net80211_cyphers(
+ ic, hw->wiphy->cipher_suites[i]);
+ if (cs == IEEE80211_CRYPTO_TKIP) {
+ /*
+ * We do set this here. We will only find out
+ * when doing a SET_KEY operation depending on
+ * what the driver returns.
+ * net80211::ieee80211_crypto_newkey()
+ * checks this so we will have to do flags
+ * surgery later.
+ */
+ cs |= IEEE80211_CRYPTO_TKIPMIC;
+ }
+ hwciphers |= cs;
+ }
+ /*
+ * (20250415) nothing anywhere in the path checks we actually
+ * support all these in net80211.
+ * net80211 supports _256 variants but the ioctl does not.
+ */
+ IMPROVE("as net80211 grows more support, enable them");
+ hwciphers &= (IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_TKIP | IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_AES_CCM | IEEE80211_CRYPTO_AES_GCM_128);
+ /*
+ * We only support CCMP here, so further filter.
+ * Also permit TKIP if turned on.
+ */
+ hwciphers &= (IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_AES_GCM_128 |
+ (lkpi_hwcrypto_tkip ? (IEEE80211_CRYPTO_TKIP |
+ IEEE80211_CRYPTO_TKIPMIC) : 0));
+ ieee80211_set_hardware_ciphers(ic, hwciphers);
}
#endif
@@ -5829,8 +6848,10 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
hw->wiphy->max_scan_ie_len -= lhw->scan_ie_len;
}
- if (bootverbose)
+ if (bootverbose) {
+ ic_printf(ic, "netdev_features %b\n", hw->netdev_features, NETIF_F_BITS);
ieee80211_announce(ic);
+ }
return (0);
err:
@@ -5977,8 +6998,6 @@ linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *hw,
void *arg)
{
struct lkpi_hw *lhw;
- struct lkpi_vif *lvif;
- struct ieee80211_vif *vif;
struct lkpi_chanctx *lchanctx;
KASSERT(hw != NULL && iterfunc != NULL,
@@ -5986,22 +7005,13 @@ linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *hw,
lhw = HW_TO_LHW(hw);
- IMPROVE("lchanctx should be its own list somewhere");
-
- LKPI_80211_LHW_LVIF_LOCK(lhw);
- TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
-
- vif = LVIF_TO_VIF(lvif);
- if (vif->chanctx_conf == NULL)
- continue;
-
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(vif->chanctx_conf);
+ rcu_read_lock();
+ list_for_each_entry_rcu(lchanctx, &lhw->lchanctx_list, entry) {
if (!lchanctx->added_to_drv)
continue;
-
iterfunc(hw, &lchanctx->chanctx_conf, arg);
}
- LKPI_80211_LHW_LVIF_UNLOCK(lhw);
+ rcu_read_unlock();
}
void
@@ -6078,13 +7088,19 @@ linuxkpi_ieee80211_scan_completed(struct ieee80211_hw *hw,
ic = lhw->ic;
ss = ic->ic_scan;
+ TRACE_SCAN(ic, "scan_flags %b info { %ju, %6D, aborted %d }",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ (uintmax_t)info->scan_start_tsf, info->tsf_bssid, ":",
+ info->aborted);
+
ieee80211_scan_done(ss->ss_vap);
LKPI_80211_LHW_SCAN_LOCK(lhw);
free(lhw->hw_req, M_LKPI80211);
lhw->hw_req = NULL;
lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
- wakeup(lhw);
+ /* The wakeup(lhw) will be called from lkpi_ic_scan_end(). */
+ /* wakeup(lhw); */
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
return;
@@ -6094,10 +7110,13 @@ static void
lkpi_80211_lhw_rxq_rx_one(struct lkpi_hw *lhw, struct mbuf *m)
{
struct ieee80211_node *ni;
+#ifdef LKPI_80211_USE_MTAG
struct m_tag *mtag;
+#endif
int ok;
ni = NULL;
+#ifdef LKPI_80211_USE_MTAG
mtag = m_tag_locate(m, MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI, NULL);
if (mtag != NULL) {
struct lkpi_80211_tag_rxni *rxni;
@@ -6105,6 +7124,12 @@ lkpi_80211_lhw_rxq_rx_one(struct lkpi_hw *lhw, struct mbuf *m)
rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
ni = rxni->ni;
}
+#else
+ if (m->m_pkthdr.PH_loc.ptr != NULL) {
+ ni = m->m_pkthdr.PH_loc.ptr;
+ m->m_pkthdr.PH_loc.ptr = NULL;
+ }
+#endif
if (ni != NULL) {
ok = ieee80211_input_mimo(ni, m);
@@ -6151,15 +7176,17 @@ lkpi_80211_lhw_rxq_task(void *ctx, int pending)
}
static void
-lkpi_convert_rx_status(struct ieee80211_hw *hw,
+lkpi_convert_rx_status(struct ieee80211_hw *hw, struct lkpi_sta *lsta,
struct ieee80211_rx_status *rx_status,
struct ieee80211_rx_stats *rx_stats,
uint8_t *rssip)
{
struct ieee80211_supported_band *supband;
+ struct rate_info rxrate;
int i;
uint8_t rssi;
+ memset(&rxrate, 0, sizeof(rxrate));
memset(rx_stats, 0, sizeof(*rx_stats));
rx_stats->r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI;
/* XXX-BZ correct hardcoded noise floor, survey data? */
@@ -6226,30 +7253,56 @@ lkpi_convert_rx_status(struct ieee80211_hw *hw,
switch (rx_status->encoding) {
case RX_ENC_LEGACY:
+ {
+ uint32_t legacy = 0;
+
supband = hw->wiphy->bands[rx_status->band];
if (supband != NULL)
- rx_stats->c_rate = supband->bitrates[rx_status->rate_idx].bitrate;
+ legacy = supband->bitrates[rx_status->rate_idx].bitrate;
+ rx_stats->c_rate = legacy;
+ rxrate.legacy = legacy;
/* Is there a LinuxKPI way of reporting IEEE80211_RX_F_CCK / _OFDM? */
break;
+ }
case RX_ENC_HT:
rx_stats->c_pktflags |= IEEE80211_RX_F_HT;
- if ((rx_status->enc_flags & RX_ENC_FLAG_SHORT_GI) != 0)
- rx_stats->c_pktflags |= IEEE80211_RX_F_SHORTGI;
rx_stats->c_rate = rx_status->rate_idx; /* mcs */
+ rxrate.flags |= RATE_INFO_FLAGS_MCS;
+ rxrate.mcs = rx_status->rate_idx;
+ if ((rx_status->enc_flags & RX_ENC_FLAG_SHORT_GI) != 0) {
+ rx_stats->c_pktflags |= IEEE80211_RX_F_SHORTGI;
+ rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ }
break;
case RX_ENC_VHT:
rx_stats->c_pktflags |= IEEE80211_RX_F_VHT;
- if ((rx_status->enc_flags & RX_ENC_FLAG_SHORT_GI) != 0)
- rx_stats->c_pktflags |= IEEE80211_RX_F_SHORTGI;
rx_stats->c_rate = rx_status->rate_idx; /* mcs */
rx_stats->c_vhtnss = rx_status->nss;
+ rxrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
+ rxrate.mcs = rx_status->rate_idx;
+ rxrate.nss = rx_status->nss;
+ if ((rx_status->enc_flags & RX_ENC_FLAG_SHORT_GI) != 0) {
+ rx_stats->c_pktflags |= IEEE80211_RX_F_SHORTGI;
+ rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ }
break;
case RX_ENC_HE:
+ rxrate.flags |= RATE_INFO_FLAGS_HE_MCS;
+ rxrate.mcs = rx_status->rate_idx;
+ rxrate.nss = rx_status->nss;
+ /* XXX TODO */
+ TODO("net80211 has not matching encoding for %u", rx_status->encoding);
+ break;
case RX_ENC_EHT:
+ rxrate.flags |= RATE_INFO_FLAGS_EHT_MCS;
+ rxrate.mcs = rx_status->rate_idx;
+ rxrate.nss = rx_status->nss;
+ /* XXX TODO */
TODO("net80211 has not matching encoding for %u", rx_status->encoding);
break;
}
+ rxrate.bw = rx_status->bw;
switch (rx_status->bw) {
case RATE_INFO_BW_20:
rx_stats->c_width = IEEE80211_RX_FW_20MHZ;
@@ -6289,19 +7342,91 @@ lkpi_convert_rx_status(struct ieee80211_hw *hw,
if (rx_status->flag & RX_FLAG_PN_VALIDATED)
rx_stats->c_pktflags |= IEEE80211_RX_F_PN_VALIDATED;
}
+ if (rx_status->flag & RX_FLAG_IV_STRIPPED)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_IV_STRIP;
+ if (rx_status->flag & RX_FLAG_ICV_STRIPPED)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_ICV_STRIP;
+ if (rx_status->flag & RX_FLAG_MIC_STRIPPED)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_MIC_STRIP;
if (rx_status->flag & RX_FLAG_MMIC_STRIPPED)
rx_stats->c_pktflags |= IEEE80211_RX_F_MMIC_STRIP;
if (rx_status->flag & RX_FLAG_MMIC_ERROR)
rx_stats->c_pktflags |= IEEE80211_RX_F_FAIL_MMIC;
- if (rx_status->flag & RX_FLAG_MIC_STRIPPED)
- rx_stats->c_pktflags |= IEEE80211_RX_F_MIC_STRIP;
- if (rx_status->flag & RX_FLAG_IV_STRIPPED)
- rx_stats->c_pktflags |= IEEE80211_RX_F_IV_STRIP;
if (rx_status->flag & RX_FLAG_FAILED_FCS_CRC)
rx_stats->c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC;
#endif
+
+ /* Fill in some sinfo bits to fill gaps not reported byt the driver. */
+ if (lsta != NULL) {
+ memcpy(&lsta->sinfo.rxrate, &rxrate, sizeof(rxrate));
+ lsta->sinfo.filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
+
+ if (rx_status->chains != 0) {
+ lsta->sinfo.chains = rx_status->chains;
+ memcpy(lsta->sinfo.chain_signal, rx_status->chain_signal,
+ sizeof(lsta->sinfo.chain_signal));
+ lsta->sinfo.filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
+ }
+ }
}
+#ifdef LINUXKPI_DEBUG_80211
+static void
+lkpi_rx_log_beacon(struct mbuf *m, struct lkpi_hw *lhw,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_mgmt *f;
+ uint8_t *e;
+ char ssid[IEEE80211_NWID_LEN * 4 + 1];
+
+ memset(ssid, '\0', sizeof(ssid));
+
+ f = mtod(m, struct ieee80211_mgmt *);
+ e = f->u.beacon.variable;
+ /*
+ * Usually SSID is right after the fixed part and for debugging we will
+ * be fine should we miss it if it is not.
+ */
+ while ((e - (uint8_t *)f) < m->m_len) {
+ if (*e == IEEE80211_ELEMID_SSID)
+ break;
+ e += (2 + *(e + 1));
+ }
+ if (*e == IEEE80211_ELEMID_SSID) {
+ int i, len;
+ char *p;
+
+ p = ssid;
+ len = m->m_len - ((e + 2) - (uint8_t *)f);
+ if (len > *(e + 1))
+ len = *(e + 1);
+ e += 2;
+ for (i = 0; i < len; i++) {
+ /* Printable character? */
+ if (*e >= 0x20 && *e < 0x7f) {
+ *p++ = *e++;
+ } else {
+ snprintf(p, 5, "%#04x", *e++);
+ p += 4;
+ }
+ }
+ *p = '\0';
+ }
+
+ /* We print skb, skb->data, m as we are seeing 'ghost beacons'. */
+ TRACE_SCAN_BEACON(lhw->ic, "Beacon: scan_flags %b, band %s freq %u chan %-4d "
+ "len %d { %#06x %#06x %6D %6D %6D %#06x %ju %u %#06x SSID '%s' }",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ lkpi_nl80211_band_name(rx_status->band), rx_status->freq,
+ linuxkpi_ieee80211_frequency_to_channel(rx_status->freq, 0),
+ m->m_pkthdr.len, f->frame_control, f->duration_id,
+ f->da, ":", f->sa, ":", f->bssid, ":", f->seq_ctrl,
+ (uintmax_t)le64_to_cpu(f->u.beacon.timestamp),
+ le16_to_cpu(f->u.beacon.beacon_int),
+ le16_to_cpu(f->u.beacon.capab_info), ssid);
+}
+#endif
+
/* For %list see comment towards the end of the function. */
void
linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
@@ -6318,12 +7443,16 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211vap *vap;
struct ieee80211_hdr *hdr;
struct lkpi_sta *lsta;
- int i, offset, ok;
+ int i, offset, ok, error;
uint8_t rssi;
bool is_beacon;
+ lhw = HW_TO_LHW(hw);
+ ic = lhw->ic;
+
if (skb->len < 2) {
/* Need 80211 stats here. */
+ counter_u64_add(ic->ic_ierrors, 1);
IMPROVE();
goto err;
}
@@ -6332,9 +7461,11 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
* For now do the data copy; we can later improve things. Might even
* have an mbuf backing the skb data then?
*/
- m = m_get2(skb->len, M_NOWAIT, MT_DATA, M_PKTHDR);
- if (m == NULL)
+ m = m_get3(skb->len, M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ counter_u64_add(ic->ic_ierrors, 1);
goto err;
+ }
m_copyback(m, 0, skb->tail - skb->data, skb->data);
shinfo = skb_shinfo(skb);
@@ -6352,13 +7483,21 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
is_beacon = ieee80211_is_beacon(hdr->frame_control);
#ifdef LINUXKPI_DEBUG_80211
- if (is_beacon && (linuxkpi_debug_80211 & D80211_TRACE_RX_BEACONS) == 0)
+ /*
+ * We use the mbuf here as otherwise the variable part might
+ * be in skb frags.
+ */
+ if (is_beacon && ((linuxkpi_debug_80211 & D80211_SCAN_BEACON) != 0))
+ lkpi_rx_log_beacon(m, lhw, rx_status);
+
+ if (is_beacon && (linuxkpi_debug_80211 & D80211_TRACE_RX_BEACONS) == 0 &&
+ (linuxkpi_debug_80211 & D80211_SCAN_BEACON) == 0)
goto no_trace_beacons;
if (linuxkpi_debug_80211 & D80211_TRACE_RX)
- printf("TRACE-RX: %s: skb %p a/l/d/t-len (%u/%u/%u/%u) "
+ printf("TRACE-RX: %s: skb %p l/d/t-len (%u/%u/%u) "
"h %p d %p t %p e %p sh %p (%u) m %p plen %u len %u%s\n",
- __func__, skb, skb->_alloc_len, skb->len, skb->data_len,
+ __func__, skb, skb->len, skb->data_len,
skb->truesize, skb->head, skb->data, skb->tail, skb->end,
shinfo, shinfo->nr_frags,
m, m->m_pkthdr.len, m->m_len, is_beacon ? " beacon" : "");
@@ -6367,7 +7506,8 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
hexdump(mtod(m, const void *), m->m_len, "RX (raw) ", 0);
/* Implement a dump_rxcb() !!! */
- if (linuxkpi_debug_80211 & D80211_TRACE_RX)
+ if ((linuxkpi_debug_80211 & D80211_TRACE_RX) != 0 ||
+ (linuxkpi_debug_80211 & D80211_SCAN_BEACON) != 0)
printf("TRACE-RX: %s: RXCB: %ju %ju %u, %b, %u, %#0x, %#0x, "
"%u band %u, %u { %d %d %d %d }, %d, %#x %#x %#x %#x %u %u %u\n",
__func__,
@@ -6396,19 +7536,6 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
no_trace_beacons:
#endif
- rssi = 0;
- lkpi_convert_rx_status(hw, rx_status, &rx_stats, &rssi);
-
- lhw = HW_TO_LHW(hw);
- ic = lhw->ic;
-
- ok = ieee80211_add_rx_params(m, &rx_stats);
- if (ok == 0) {
- m_freem(m);
- counter_u64_add(ic->ic_ierrors, 1);
- goto err;
- }
-
lsta = NULL;
if (sta != NULL) {
lsta = STA_TO_LSTA(sta);
@@ -6422,6 +7549,16 @@ no_trace_beacons:
lsta = ni->ni_drv_data;
}
+ rssi = 0;
+ lkpi_convert_rx_status(hw, lsta, rx_status, &rx_stats, &rssi);
+
+ ok = ieee80211_add_rx_params(m, &rx_stats);
+ if (ok == 0) {
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto err;
+ }
+
if (ni != NULL)
vap = ni->ni_vap;
else
@@ -6504,32 +7641,47 @@ skip_device_ts:
}
#endif
- /*
- * Attach meta-information to the mbuf for the deferred RX path.
- * Currently this is best-effort. Should we need to be hard,
- * drop the frame and goto err;
- */
+ /* Attach meta-information to the mbuf for the deferred RX path. */
if (ni != NULL) {
+#ifdef LKPI_80211_USE_MTAG
struct m_tag *mtag;
struct lkpi_80211_tag_rxni *rxni;
mtag = m_tag_alloc(MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI,
sizeof(*rxni), IEEE80211_M_NOWAIT);
- if (mtag != NULL) {
- rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
- rxni->ni = ni; /* We hold a reference. */
- m_tag_prepend(m, mtag);
+ if (mtag == NULL) {
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto err;
}
+ rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
+ rxni->ni = ni; /* We hold a reference. */
+ m_tag_prepend(m, mtag);
+#else
+ m->m_pkthdr.PH_loc.ptr = ni; /* We hold a reference. */
+#endif
}
LKPI_80211_LHW_RXQ_LOCK(lhw);
if (lhw->rxq_stopped) {
LKPI_80211_LHW_RXQ_UNLOCK(lhw);
m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
goto err;
}
- mbufq_enqueue(&lhw->rxq, m);
+ error = mbufq_enqueue(&lhw->rxq, m);
+ if (error != 0) {
+ LKPI_80211_LHW_RXQ_UNLOCK(lhw);
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_RX)
+ ic_printf(ni->ni_ic, "%s: mbufq_enqueue failed: %d\n",
+ __func__, error);
+#endif
+ goto err;
+ }
taskqueue_enqueue(taskqueue_thread, &lhw->rxq_task);
LKPI_80211_LHW_RXQ_UNLOCK(lhw);
@@ -6662,7 +7814,7 @@ lkpi_wiphy_delayed_work_timer(struct timer_list *tl)
{
struct wiphy_delayed_work *wdwk;
- wdwk = from_timer(wdwk, tl, timer);
+ wdwk = timer_container_of(wdwk, tl, timer);
wiphy_work_queue(wdwk->wiphy, &wdwk->work);
}
@@ -6872,6 +8024,7 @@ linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct lkpi_vif *lvif;
struct sk_buff *skb;
+ IMPROVE("wiphy_lock? or assert?");
skb = NULL;
ltxq = TXQ_TO_LTXQ(txq);
ltxq->seen_dequeue = true;
@@ -7113,8 +8266,8 @@ linuxkpi_ieee80211_queue_work(struct ieee80211_hw *hw,
}
struct sk_buff *
-linuxkpi_ieee80211_probereq_get(struct ieee80211_hw *hw, uint8_t *addr,
- uint8_t *ssid, size_t ssid_len, size_t tailroom)
+linuxkpi_ieee80211_probereq_get(struct ieee80211_hw *hw, const uint8_t *addr,
+ const uint8_t *ssid, size_t ssid_len, size_t tailroom)
{
struct sk_buff *skb;
struct ieee80211_frame *wh;
@@ -7236,8 +8389,11 @@ linuxkpi_ieee80211_connection_loss(struct ieee80211_vif *vif)
nstate = IEEE80211_S_INIT;
arg = 0; /* Not a valid reason. */
- ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
- vif, vap, ieee80211_state_name[vap->iv_state]);
+ ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s (synched %d, assoc %d "
+ "beacons %d dtim_period %d)\n", __func__, vif, vap,
+ ieee80211_state_name[vap->iv_state],
+ lvif->lvif_bss_synched, vif->cfg.assoc, lvif->beacons,
+ vif->bss_conf.dtim_period);
ieee80211_new_state(vap, nstate, arg);
}
@@ -7250,8 +8406,11 @@ linuxkpi_ieee80211_beacon_loss(struct ieee80211_vif *vif)
lvif = VIF_TO_LVIF(vif);
vap = LVIF_TO_VAP(lvif);
- ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
- vif, vap, ieee80211_state_name[vap->iv_state]);
+ ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s (synched %d, assoc %d "
+ "beacons %d dtim_period %d)\n", __func__, vif, vap,
+ ieee80211_state_name[vap->iv_state],
+ lvif->lvif_bss_synched, vif->cfg.assoc, lvif->beacons,
+ vif->bss_conf.dtim_period);
ieee80211_beacon_miss(vap->iv_ic);
}
@@ -7375,8 +8534,8 @@ lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
ltxq->stopped = false;
- /* XXX-BZ see when this explodes with all the locking. taskq? */
- lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
+ if (!skb_queue_empty(&ltxq->skbq))
+ lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
}
}
rcu_read_unlock();
@@ -7386,8 +8545,8 @@ lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
}
-void
-linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
+static void
+lkpi_ieee80211_wake_queues_locked(struct ieee80211_hw *hw)
{
int i;
@@ -7397,13 +8556,32 @@ linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
}
void
+linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+ struct lkpi_hw *lhw;
+ unsigned long flags;
+
+ lhw = HW_TO_LHW(hw);
+
+ spin_lock_irqsave(&lhw->txq_lock, flags);
+ lkpi_ieee80211_wake_queues_locked(hw);
+ spin_unlock_irqrestore(&lhw->txq_lock, flags);
+}
+
+void
linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
{
+ struct lkpi_hw *lhw;
+ unsigned long flags;
KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
__func__, qnum, hw->queues, hw));
+ lhw = HW_TO_LHW(hw);
+
+ spin_lock_irqsave(&lhw->txq_lock, flags);
lkpi_ieee80211_wake_queues(hw, qnum);
+ spin_unlock_irqrestore(&lhw->txq_lock, flags);
}
/* This is just hardware queues. */
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h
index c2d29b2dcc4b..0dfcd7646c34 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -59,6 +59,8 @@
#define D80211_IMPROVE_TXQ 0x00000004
#define D80211_TRACE 0x00000010
#define D80211_TRACEOK 0x00000020
+#define D80211_SCAN 0x00000040
+#define D80211_SCAN_BEACON 0x00000080
#define D80211_TRACE_TX 0x00000100
#define D80211_TRACE_TX_DUMP 0x00000200
#define D80211_TRACE_RX 0x00001000
@@ -75,6 +77,20 @@
#define D80211_TRACE_MODE_HE 0x04000000
#define D80211_TRACE_MODE_EHT 0x08000000
+#ifdef LINUXKPI_DEBUG_80211
+#define TRACE_SCAN(ic, fmt, ...) \
+ if (linuxkpi_debug_80211 & D80211_SCAN) \
+ printf("%s:%d: %s SCAN " fmt "\n", \
+ __func__, __LINE__, ic->ic_name, ##__VA_ARGS__)
+#define TRACE_SCAN_BEACON(ic, fmt, ...) \
+ if (linuxkpi_debug_80211 & D80211_SCAN_BEACON) \
+ printf("%s:%d: %s SCAN " fmt "\n", \
+ __func__, __LINE__, ic->ic_name, ##__VA_ARGS__)
+#else
+#define TRACE_SCAN(...) do {} while (0)
+#define TRACE_SCAN_BEACON(...) do {} while (0)
+#endif
+
#define IMPROVE_TXQ(...) \
if (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) \
printf("%s:%d: XXX LKPI80211 IMPROVE_TXQ\n", __func__, __LINE__)
@@ -86,6 +102,7 @@
#define MTAG_ABI_LKPI80211 1707696513 /* LinuxKPI 802.11 KBI */
+#ifdef LKPI_80211_USE_MTAG
/*
* Deferred RX path.
* We need to pass *ni along (and possibly more in the future so
@@ -95,6 +112,7 @@
struct lkpi_80211_tag_rxni {
struct ieee80211_node *ni; /* MUST hold a reference to it. */
};
+#endif
struct lkpi_radiotap_tx_hdr {
struct ieee80211_radiotap_header wt_ihdr;
@@ -161,6 +179,8 @@ struct lkpi_sta {
bool added_to_drv; /* Driver knows; i.e. we called ...(). */
bool in_mgd; /* XXX-BZ should this be per-vif? */
+ struct station_info sinfo; /* statistics */
+
/* Must be last! */
struct ieee80211_sta sta __aligned(CACHE_LINE_SIZE);
};
@@ -183,11 +203,20 @@ struct lkpi_vif {
enum ieee80211_state, int);
struct ieee80211_node * (*iv_update_bss)(struct ieee80211vap *,
struct ieee80211_node *);
+ void (*iv_recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int,
+ const struct ieee80211_rx_stats *,
+ int, int);
+ struct task sw_scan_task;
+
struct list_head lsta_list;
struct lkpi_sta *lvif_bss;
+ struct ieee80211_node *key_update_iv_bss;
+ int ic_unlocked; /* Count of ic unlocks pending (*mo_set_key) */
int nt_unlocked; /* Count of nt unlocks pending (*mo_set_key) */
+ int beacons; /* # of beacons since assoc */
bool lvif_bss_synched;
bool added_to_drv; /* Driver knows; i.e. we called add_interface(). */
@@ -216,11 +245,15 @@ struct lkpi_hw { /* name it mac80211_sc? */
TAILQ_HEAD(, lkpi_vif) lvif_head;
struct sx lvif_sx;
- struct sx sx; /* XXX-BZ Can this be wiphy->mtx in the future? */
+ struct list_head lchanctx_list;
+ struct netdev_hw_addr_list mc_list;
+ unsigned int mc_flags;
+ struct sx mc_sx;
struct mtx txq_mtx;
uint32_t txq_generation[IEEE80211_NUM_ACS];
TAILQ_HEAD(, lkpi_txq) scheduled_txqs[IEEE80211_NUM_ACS];
+ spinlock_t txq_lock;
/* Deferred RX path. */
struct task rxq_task;
@@ -273,7 +306,7 @@ struct lkpi_hw { /* name it mac80211_sc? */
int max_rates; /* Maximum number of bitrates supported in any channel. */
int scan_ie_len; /* Length of common per-band scan IEs. */
- bool update_mc;
+ bool mc_all_multi;
bool update_wme;
bool rxq_stopped;
@@ -283,8 +316,14 @@ struct lkpi_hw { /* name it mac80211_sc? */
#define LHW_TO_HW(_lhw) (&(_lhw)->hw)
#define HW_TO_LHW(_hw) container_of(_hw, struct lkpi_hw, hw)
+#define LKPI_LHW_SCAN_BITS \
+ "\010\1RUNING\2HW"
+
struct lkpi_chanctx {
+ struct list_head entry;
+
bool added_to_drv; /* Managed by MO */
+
struct ieee80211_chanctx_conf chanctx_conf __aligned(CACHE_LINE_SIZE);
};
#define LCHANCTX_TO_CHANCTX_CONF(_lchanctx) \
@@ -318,19 +357,6 @@ struct lkpi_wiphy {
#define LKPI_80211_LWIPHY_WORK_UNLOCK_ASSERT(_lwiphy) \
mtx_assert(&(_lwiphy)->wwk_mtx, MA_NOTOWNED)
-#define LKPI_80211_LHW_LOCK_INIT(_lhw) \
- sx_init_flags(&(_lhw)->sx, "lhw", SX_RECURSE);
-#define LKPI_80211_LHW_LOCK_DESTROY(_lhw) \
- sx_destroy(&(_lhw)->sx);
-#define LKPI_80211_LHW_LOCK(_lhw) \
- sx_xlock(&(_lhw)->sx)
-#define LKPI_80211_LHW_UNLOCK(_lhw) \
- sx_xunlock(&(_lhw)->sx)
-#define LKPI_80211_LHW_LOCK_ASSERT(_lhw) \
- sx_assert(&(_lhw)->sx, SA_LOCKED)
-#define LKPI_80211_LHW_UNLOCK_ASSERT(_lhw) \
- sx_assert(&(_lhw)->sx, SA_UNLOCKED)
-
#define LKPI_80211_LHW_SCAN_LOCK_INIT(_lhw) \
mtx_init(&(_lhw)->scan_mtx, "lhw-scan", NULL, MTX_DEF | MTX_RECURSE);
#define LKPI_80211_LHW_SCAN_LOCK_DESTROY(_lhw) \
@@ -373,6 +399,13 @@ struct lkpi_wiphy {
#define LKPI_80211_LHW_LVIF_LOCK(_lhw) sx_xlock(&(_lhw)->lvif_sx)
#define LKPI_80211_LHW_LVIF_UNLOCK(_lhw) sx_xunlock(&(_lhw)->lvif_sx)
+#define LKPI_80211_LHW_MC_LOCK_INIT(_lhw) \
+ sx_init_flags(&lhw->mc_sx, "lhw-mc", 0);
+#define LKPI_80211_LHW_MC_LOCK_DESTROY(_lhw) \
+ sx_destroy(&lhw->mc_sx);
+#define LKPI_80211_LHW_MC_LOCK(_lhw) sx_xlock(&(_lhw)->mc_sx)
+#define LKPI_80211_LHW_MC_UNLOCK(_lhw) sx_xunlock(&(_lhw)->mc_sx)
+
#define LKPI_80211_LVIF_LOCK(_lvif) mtx_lock(&(_lvif)->mtx)
#define LKPI_80211_LVIF_UNLOCK(_lvif) mtx_unlock(&(_lvif)->mtx)
@@ -425,7 +458,7 @@ int lkpi_80211_mo_config(struct ieee80211_hw *, uint32_t);
int lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *, struct ieee80211_vif *,
struct ieee80211_bss_conf *, struct ieee80211_chanctx_conf *);
void lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *, struct ieee80211_vif *,
- struct ieee80211_bss_conf *, struct ieee80211_chanctx_conf **);
+ struct ieee80211_bss_conf *, struct ieee80211_chanctx_conf *);
int lkpi_80211_mo_add_chanctx(struct ieee80211_hw *, struct ieee80211_chanctx_conf *);
void lkpi_80211_mo_change_chanctx(struct ieee80211_hw *,
struct ieee80211_chanctx_conf *, uint32_t);
diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
index fde23d02af5e..1046b753574f 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
@@ -40,9 +40,9 @@
#ifdef LINUXKPI_DEBUG_80211
#define LKPI_80211_TRACE_MO(fmt, ...) \
if (linuxkpi_debug_80211 & D80211_TRACE_MO) \
- printf("LKPI_80211_TRACE_MO %s:%d: %d %d %u_" fmt "\n", \
+ printf("LKPI_80211_TRACE_MO %s:%d: %d %d %lu: " fmt "\n", \
__func__, __LINE__, curcpu, curthread->td_tid, \
- (unsigned int)ticks, __VA_ARGS__)
+ jiffies, __VA_ARGS__)
#else
#define LKPI_80211_TRACE_MO(...) do { } while(0)
#endif
@@ -53,6 +53,8 @@ lkpi_80211_mo_start(struct ieee80211_hw *hw)
struct lkpi_hw *lhw;
int error;
+ lockdep_assert_wiphy(hw->wiphy);
+
lhw = HW_TO_LHW(hw);
if (lhw->ops->start == NULL) {
error = EOPNOTSUPP;
@@ -458,7 +460,7 @@ lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *
hw, vif, conf, chanctx_conf);
error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf);
if (error == 0)
- vif->chanctx_conf = chanctx_conf;
+ vif->bss_conf.chanctx_conf = chanctx_conf;
out:
return (error);
@@ -466,21 +468,23 @@ out:
void
lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf **chanctx_conf)
+ struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
{
struct lkpi_hw *lhw;
+ might_sleep();
+ lockdep_assert_wiphy(hw->wiphy);
+
lhw = HW_TO_LHW(hw);
if (lhw->ops->unassign_vif_chanctx == NULL)
return;
- if (*chanctx_conf == NULL)
+ if (chanctx_conf == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
- hw, vif, conf, *chanctx_conf);
- lhw->ops->unassign_vif_chanctx(hw, vif, conf, *chanctx_conf);
- *chanctx_conf = NULL;
+ hw, vif, conf, chanctx_conf);
+ lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf);
}
@@ -551,6 +555,9 @@ lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vi
lhw->ops->bss_info_changed == NULL)
return;
+ if (changed == 0)
+ return;
+
LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed);
if (lhw->ops->link_info_changed != NULL)
lhw->ops->link_info_changed(hw, vif, conf, changed);
diff --git a/sys/compat/linuxkpi/common/src/linux_acpi.c b/sys/compat/linuxkpi/common/src/linux_acpi.c
index 6a9afb3ddff0..43783bb8727b 100644
--- a/sys/compat/linuxkpi/common/src/linux_acpi.c
+++ b/sys/compat/linuxkpi/common/src/linux_acpi.c
@@ -39,6 +39,7 @@
#include <linux/notifier.h>
#include <linux/suspend.h>
+#include <linux/uuid.h>
#include <acpi/acpi_bus.h>
#include <acpi/video.h>
@@ -71,8 +72,9 @@ bsd_acpi_get_handle(device_t bsddev)
}
bool
-acpi_check_dsm(ACPI_HANDLE handle, const char *uuid, int rev, uint64_t funcs)
+acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)
{
+ UINT64 ret;
if (funcs == 0)
return (false);
@@ -86,17 +88,33 @@ acpi_check_dsm(ACPI_HANDLE handle, const char *uuid, int rev, uint64_t funcs)
*/
funcs |= 1 << 0;
- return ((acpi_DSMQuery(handle, uuid, rev) & funcs) == funcs);
+ ret = acpi_DSMQuery(handle, (const uint8_t *)uuid, rev);
+ return ((ret & funcs) == funcs);
}
ACPI_OBJECT *
-acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const char *uuid, int rev,
+acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,
int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)
{
ACPI_BUFFER buf;
+ ACPI_STATUS status;
- return (ACPI_SUCCESS(acpi_EvaluateDSMTyped(handle, uuid, rev, func,
- argv4, &buf, type)) ? (ACPI_OBJECT *)buf.Pointer : NULL);
+ status = acpi_EvaluateDSMTyped(handle, (const uint8_t *)uuid, rev, func,
+ argv4, &buf, type);
+ return (ACPI_SUCCESS(status) ? (ACPI_OBJECT *)buf.Pointer : NULL);
+}
+
+union linuxkpi_acpi_object *
+acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
+ UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)
+{
+ ACPI_BUFFER buf;
+ ACPI_STATUS status;
+
+ status = acpi_EvaluateDSM(ObjHandle, (const uint8_t *)guid, rev, func,
+ (ACPI_OBJECT *)pkg, &buf);
+ return (ACPI_SUCCESS(status) ?
+ (union linuxkpi_acpi_object *)buf.Pointer : NULL);
}
static void
@@ -311,18 +329,25 @@ bsd_acpi_get_handle(device_t bsddev)
}
bool
-acpi_check_dsm(ACPI_HANDLE handle, const char *uuid, int rev, uint64_t funcs)
+acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)
{
return (false);
}
ACPI_OBJECT *
-acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const char *uuid, int rev,
+acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,
int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)
{
return (NULL);
}
+union linuxkpi_acpi_object *
+acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
+ UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)
+{
+ return (NULL);
+}
+
int
register_acpi_notifier(struct notifier_block *nb)
{
diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c
index af6cc01d8020..458744a9fec6 100644
--- a/sys/compat/linuxkpi/common/src/linux_compat.c
+++ b/sys/compat/linuxkpi/common/src/linux_compat.c
@@ -50,6 +50,7 @@
#include <sys/rwlock.h>
#include <sys/mman.h>
#include <sys/stack.h>
+#include <sys/stdarg.h>
#include <sys/sysent.h>
#include <sys/time.h>
#include <sys/user.h>
@@ -59,8 +60,7 @@
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pager.h>
-
-#include <machine/stdarg.h>
+#include <vm/vm_radix.h>
#if defined(__i386__) || defined(__amd64__)
#include <machine/cputypes.h>
@@ -96,6 +96,8 @@
#include <linux/rcupdate.h>
#include <linux/interval_tree.h>
#include <linux/interval_tree_generic.h>
+#include <linux/printk.h>
+#include <linux/seq_file.h>
#if defined(__i386__) || defined(__amd64__)
#include <asm/smp.h>
@@ -645,6 +647,7 @@ int
zap_vma_ptes(struct vm_area_struct *vma, unsigned long address,
unsigned long size)
{
+ struct pctrie_iter pages;
vm_object_t obj;
vm_page_t m;
@@ -652,9 +655,8 @@ zap_vma_ptes(struct vm_area_struct *vma, unsigned long address,
if (obj == NULL || (obj->flags & OBJ_UNMANAGED) != 0)
return (-ENOTSUP);
VM_OBJECT_RLOCK(obj);
- for (m = vm_page_find_least(obj, OFF_TO_IDX(address));
- m != NULL && m->pindex < OFF_TO_IDX(address + size);
- m = TAILQ_NEXT(m, listq))
+ vm_page_iter_limit_init(&pages, obj, OFF_TO_IDX(address + size));
+ VM_RADIX_FOREACH_FROM(m, &pages, OFF_TO_IDX(address))
pmap_remove_all(m);
VM_OBJECT_RUNLOCK(obj);
return (0);
@@ -1969,6 +1971,84 @@ kasprintf(gfp_t gfp, const char *fmt, ...)
return (p);
}
+int
+__lkpi_hexdump_printf(void *arg1 __unused, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = vprintf(fmt, ap);
+ va_end(ap);
+ return (result);
+}
+
+int
+__lkpi_hexdump_sbuf_printf(void *arg1, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = sbuf_vprintf(arg1, fmt, ap);
+ va_end(ap);
+ return (result);
+}
+
+void
+lkpi_hex_dump(int(*_fpf)(void *, const char *, ...), void *arg1,
+ const char *level, const char *prefix_str,
+ const int prefix_type, const int rowsize, const int groupsize,
+ const void *buf, size_t len, const bool ascii)
+{
+ typedef const struct { long long value; } __packed *print_64p_t;
+ typedef const struct { uint32_t value; } __packed *print_32p_t;
+ typedef const struct { uint16_t value; } __packed *print_16p_t;
+ const void *buf_old = buf;
+ int row;
+
+ while (len > 0) {
+ if (level != NULL)
+ _fpf(arg1, "%s", level);
+ if (prefix_str != NULL)
+ _fpf(arg1, "%s ", prefix_str);
+
+ switch (prefix_type) {
+ case DUMP_PREFIX_ADDRESS:
+ _fpf(arg1, "[%p] ", buf);
+ break;
+ case DUMP_PREFIX_OFFSET:
+ _fpf(arg1, "[%#tx] ", ((const char *)buf -
+ (const char *)buf_old));
+ break;
+ default:
+ break;
+ }
+ for (row = 0; row != rowsize; row++) {
+ if (groupsize == 8 && len > 7) {
+ _fpf(arg1, "%016llx ", ((print_64p_t)buf)->value);
+ buf = (const uint8_t *)buf + 8;
+ len -= 8;
+ } else if (groupsize == 4 && len > 3) {
+ _fpf(arg1, "%08x ", ((print_32p_t)buf)->value);
+ buf = (const uint8_t *)buf + 4;
+ len -= 4;
+ } else if (groupsize == 2 && len > 1) {
+ _fpf(arg1, "%04x ", ((print_16p_t)buf)->value);
+ buf = (const uint8_t *)buf + 2;
+ len -= 2;
+ } else if (len > 0) {
+ _fpf(arg1, "%02x ", *(const uint8_t *)buf);
+ buf = (const uint8_t *)buf + 1;
+ len--;
+ } else {
+ break;
+ }
+ }
+ _fpf(arg1, "\n");
+ }
+}
+
static void
linux_timer_callback_wrapper(void *context)
{
@@ -1990,8 +2070,24 @@ linux_timer_callback_wrapper(void *context)
timer->function(timer->data);
}
+static int
+linux_timer_jiffies_until(unsigned long expires)
+{
+ unsigned long delta = expires - jiffies;
+
+ /*
+ * Guard against already expired values and make sure that the value can
+ * be used as a tick count, rather than a jiffies count.
+ */
+ if ((long)delta < 1)
+ delta = 1;
+ else if (delta > INT_MAX)
+ delta = INT_MAX;
+ return ((int)delta);
+}
+
int
-mod_timer(struct timer_list *timer, int expires)
+mod_timer(struct timer_list *timer, unsigned long expires)
{
int ret;
@@ -2024,7 +2120,7 @@ add_timer_on(struct timer_list *timer, int cpu)
}
int
-del_timer(struct timer_list *timer)
+timer_delete(struct timer_list *timer)
{
if (callout_stop(&(timer)->callout) == -1)
@@ -2033,7 +2129,7 @@ del_timer(struct timer_list *timer)
}
int
-del_timer_sync(struct timer_list *timer)
+timer_delete_sync(struct timer_list *timer)
{
if (callout_drain(&(timer)->callout) == -1)
@@ -2042,13 +2138,6 @@ del_timer_sync(struct timer_list *timer)
}
int
-timer_delete_sync(struct timer_list *timer)
-{
-
- return (del_timer_sync(timer));
-}
-
-int
timer_shutdown_sync(struct timer_list *timer)
{
@@ -2187,12 +2276,12 @@ intr:
/*
* Time limited wait for done != 0 with or without signals.
*/
-int
-linux_wait_for_timeout_common(struct completion *c, int timeout, int flags)
+unsigned long
+linux_wait_for_timeout_common(struct completion *c, unsigned long timeout,
+ int flags)
{
struct task_struct *task;
- int end = jiffies + timeout;
- int error;
+ unsigned long end = jiffies + timeout, error;
if (SCHEDULER_STOPPED())
return (0);
@@ -2767,8 +2856,8 @@ linux_compat_init(void *arg)
boot_cpu_data.x86_model = CPUID_TO_MODEL(cpu_id);
boot_cpu_data.x86_vendor = x86_vendor;
- __cpu_data = mallocarray(mp_maxid + 1,
- sizeof(*__cpu_data), M_KMALLOC, M_WAITOK | M_ZERO);
+ __cpu_data = kmalloc_array(mp_maxid + 1,
+ sizeof(*__cpu_data), M_WAITOK | M_ZERO);
CPU_FOREACH(i) {
__cpu_data[i].x86_clflush_size = cpu_clflush_line_size;
__cpu_data[i].x86_max_cores = mp_ncpus;
@@ -2810,8 +2899,8 @@ linux_compat_init(void *arg)
* This is used by cpumask_of() (and possibly others in the future) for,
* e.g., drivers to pass hints to irq_set_affinity_hint().
*/
- static_single_cpu_mask = mallocarray(mp_maxid + 1,
- sizeof(static_single_cpu_mask), M_KMALLOC, M_WAITOK | M_ZERO);
+ static_single_cpu_mask = kmalloc_array(mp_maxid + 1,
+ sizeof(static_single_cpu_mask), M_WAITOK | M_ZERO);
/*
* When the number of CPUs reach a threshold, we start to save memory
@@ -2830,9 +2919,9 @@ linux_compat_init(void *arg)
* (_BITSET_BITS / 8)' bytes (for comparison with the
* overlapping scheme).
*/
- static_single_cpu_mask_lcs = mallocarray(mp_ncpus,
+ static_single_cpu_mask_lcs = kmalloc_array(mp_ncpus,
sizeof(*static_single_cpu_mask_lcs),
- M_KMALLOC, M_WAITOK | M_ZERO);
+ M_WAITOK | M_ZERO);
sscm_ptr = static_single_cpu_mask_lcs;
CPU_FOREACH(i) {
diff --git a/sys/compat/linuxkpi/common/src/linux_devres.c b/sys/compat/linuxkpi/common/src/linux_devres.c
index 84f03ba0dd7d..23c91cb5ab2f 100644
--- a/sys/compat/linuxkpi/common/src/linux_devres.c
+++ b/sys/compat/linuxkpi/common/src/linux_devres.c
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2020-2021 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
*
* This software was developed by Bj\xc3\xb6rn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -223,6 +223,30 @@ lkpi_devm_kmalloc_release(struct device *dev __unused, void *p __unused)
/* Nothing to do. Freed with the devres. */
}
+static int
+lkpi_devm_kmalloc_match(struct device *dev __unused, void *p, void *mp)
+{
+ return (p == mp);
+}
+
+void
+lkpi_devm_kfree(struct device *dev, const void *p)
+{
+ void *mp;
+ int error;
+
+ if (p == NULL)
+ return;
+
+ /* I assume Linux simply casts the const away... */
+ mp = __DECONST(void *, p);
+ error = lkpi_devres_destroy(dev, lkpi_devm_kmalloc_release,
+ lkpi_devm_kmalloc_match, mp);
+ if (error != 0)
+ dev_warn(dev, "%s: lkpi_devres_destroy failed with %d\n",
+ __func__, error);
+}
+
struct devres_action {
void *data;
void (*action)(void *);
diff --git a/sys/compat/linuxkpi/common/src/linux_folio.c b/sys/compat/linuxkpi/common/src/linux_folio.c
new file mode 100644
index 000000000000..c2af7792be04
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_folio.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ * Copyright (c) 2024-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 <linux/gfp.h>
+#include <linux/mm.h>
+#include <linux/mm_types.h>
+#include <linux/page.h>
+#include <linux/pagevec.h>
+
+struct folio *
+folio_alloc(gfp_t gfp, unsigned int order)
+{
+ struct page *page;
+ struct folio *folio;
+
+ /*
+ * Allocated pages are wired already. There is no need to increase a
+ * refcount here.
+ */
+ page = alloc_pages(gfp | __GFP_COMP, order);
+ folio = (struct folio *)page;
+
+ return (folio);
+}
+
+void
+__folio_batch_release(struct folio_batch *fbatch)
+{
+ release_pages(fbatch->folios, folio_batch_count(fbatch));
+
+ folio_batch_reinit(fbatch);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_i2c.c b/sys/compat/linuxkpi/common/src/linux_i2c.c
index d3e69d5df212..f18570202f74 100644
--- a/sys/compat/linuxkpi/common/src/linux_i2c.c
+++ b/sys/compat/linuxkpi/common/src/linux_i2c.c
@@ -85,7 +85,7 @@ lkpi_iic_attach(device_t dev)
struct lkpi_iic_softc *sc;
sc = device_get_softc(dev);
- sc->iicbus = device_add_child(dev, "iicbus", -1);
+ sc->iicbus = device_add_child(dev, "iicbus", DEVICE_UNIT_ANY);
if (sc->iicbus == NULL) {
device_printf(dev, "Couldn't add iicbus child, aborting\n");
return (ENXIO);
@@ -323,7 +323,8 @@ lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
device_printf(adapter->dev.parent->bsddev,
"Adding i2c adapter %s\n", adapter->name);
sx_xlock(&lkpi_sx_i2c);
- lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
+ lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic",
+ DEVICE_UNIT_ANY);
if (lkpi_iic == NULL) {
device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iic\n");
sx_xunlock(&lkpi_sx_i2c);
diff --git a/sys/compat/linuxkpi/common/src/linux_i2cbb.c b/sys/compat/linuxkpi/common/src/linux_i2cbb.c
index f266a1404af7..48a018ec2533 100644
--- a/sys/compat/linuxkpi/common/src/linux_i2cbb.c
+++ b/sys/compat/linuxkpi/common/src/linux_i2cbb.c
@@ -90,7 +90,7 @@ lkpi_iicbb_attach(device_t dev)
struct lkpi_iicbb_softc *sc;
sc = device_get_softc(dev);
- sc->iicbb = device_add_child(dev, "iicbb", -1);
+ sc->iicbb = device_add_child(dev, "iicbb", DEVICE_UNIT_ANY);
if (sc->iicbb == NULL) {
device_printf(dev, "Couldn't add iicbb child, aborting\n");
return (ENXIO);
@@ -308,7 +308,8 @@ lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter)
device_printf(adapter->dev.parent->bsddev,
"Adding i2c adapter %s\n", adapter->name);
sx_xlock(&lkpi_sx_i2cbb);
- lkpi_iicbb = device_add_child(adapter->dev.parent->bsddev, "lkpi_iicbb", -1);
+ lkpi_iicbb = device_add_child(adapter->dev.parent->bsddev, "lkpi_iicbb",
+ DEVICE_UNIT_ANY);
if (lkpi_iicbb == NULL) {
device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iicbb\n");
sx_xunlock(&lkpi_sx_i2cbb);
diff --git a/sys/compat/linuxkpi/common/src/linux_idr.c b/sys/compat/linuxkpi/common/src/linux_idr.c
index 59c375194689..664177835c85 100644
--- a/sys/compat/linuxkpi/common/src/linux_idr.c
+++ b/sys/compat/linuxkpi/common/src/linux_idr.c
@@ -34,8 +34,7 @@
#include <sys/sysctl.h>
#include <sys/lock.h>
#include <sys/mutex.h>
-
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#include <linux/bitmap.h>
#include <linux/kobject.h>
diff --git a/sys/compat/linuxkpi/common/src/linux_kobject.c b/sys/compat/linuxkpi/common/src/linux_kobject.c
index 02f8b8d5b709..2b9d3dcffa4b 100644
--- a/sys/compat/linuxkpi/common/src/linux_kobject.c
+++ b/sys/compat/linuxkpi/common/src/linux_kobject.c
@@ -122,10 +122,10 @@ kobject_add_complete(struct kobject *kobj)
for (attr = t->default_attrs; *attr != NULL; attr++) {
error = sysfs_create_file(kobj, *attr);
- if (error)
+ if (error != 0)
break;
}
- if (error)
+ if (error != 0)
sysfs_remove_dir(kobj);
}
diff --git a/sys/compat/linuxkpi/common/src/linux_netdev.c b/sys/compat/linuxkpi/common/src/linux_netdev.c
index c36684f9fd97..ce9153614104 100644
--- a/sys/compat/linuxkpi/common/src/linux_netdev.c
+++ b/sys/compat/linuxkpi/common/src/linux_netdev.c
@@ -63,22 +63,22 @@ SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug_napi, CTLFLAG_RWTUN,
#define DNAPI_DIRECT_DISPATCH 0x1000
#define NAPI_TRACE(_n) if (debug_napi & DNAPI_TRACE) \
- printf("NAPI_TRACE %s:%d %u %p (%#jx %b)\n", __func__, __LINE__, \
- (unsigned int)ticks, _n, (uintmax_t)(_n)->state, \
+ printf("NAPI_TRACE %s:%d %lu %p (%#jx %b)\n", __func__, __LINE__, \
+ jiffies, _n, (uintmax_t)(_n)->state, \
(int)(_n)->state, LKPI_NAPI_FLAGS)
#define NAPI_TRACE2D(_n, _d) if (debug_napi & DNAPI_TRACE) \
- printf("NAPI_TRACE %s:%d %u %p (%#jx %b) %d\n", __func__, __LINE__, \
- (unsigned int)ticks, _n, (uintmax_t)(_n)->state, \
+ printf("NAPI_TRACE %s:%d %lu %p (%#jx %b) %d\n", __func__, __LINE__, \
+ jiffies, _n, (uintmax_t)(_n)->state, \
(int)(_n)->state, LKPI_NAPI_FLAGS, _d)
#define NAPI_TRACE_TASK(_n, _p, _c) if (debug_napi & DNAPI_TRACE_TASK) \
- printf("NAPI_TRACE %s:%d %u %p (%#jx %b) pending %d count %d " \
+ printf("NAPI_TRACE %s:%d %lu %p (%#jx %b) pending %d count %d " \
"rx_count %d\n", __func__, __LINE__, \
- (unsigned int)ticks, _n, (uintmax_t)(_n)->state, \
+ jiffies, _n, (uintmax_t)(_n)->state, \
(int)(_n)->state, LKPI_NAPI_FLAGS, _p, _c, (_n)->rx_count)
#define NAPI_TODO() if (debug_napi & DNAPI_TODO) \
- printf("NAPI_TODO %s:%d %d\n", __func__, __LINE__, ticks)
+ printf("NAPI_TODO %s:%d %lu\n", __func__, __LINE__, jiffies)
#define NAPI_IMPROVE() if (debug_napi & DNAPI_IMPROVE) \
- printf("NAPI_IMPROVE %s:%d %d\n", __func__, __LINE__, ticks)
+ printf("NAPI_IMPROVE %s:%d %lu\n", __func__, __LINE__, jiffies)
#define NAPI_DIRECT_DISPATCH() ((debug_napi & DNAPI_DIRECT_DISPATCH) != 0)
#else
diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c
index b5a0d34a6ad7..628af17df853 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -83,7 +83,7 @@ si_meminfo(struct sysinfo *si)
}
void *
-linux_page_address(struct page *page)
+linux_page_address(const struct page *page)
{
if (page->object != kernel_object) {
@@ -106,6 +106,7 @@ linux_alloc_pages(gfp_t flags, unsigned int order)
if ((flags & M_ZERO) != 0)
req |= VM_ALLOC_ZERO;
+
if (order == 0 && (flags & GFP_DMA32) == 0) {
page = vm_page_alloc_noobj(req);
if (page == NULL)
@@ -113,6 +114,10 @@ linux_alloc_pages(gfp_t flags, unsigned int order)
} else {
vm_paddr_t pmax = (flags & GFP_DMA32) ?
BUS_SPACE_MAXADDR_32BIT : BUS_SPACE_MAXADDR;
+
+ if ((flags & __GFP_NORETRY) != 0)
+ req |= VM_ALLOC_NORECLAIM;
+
retry:
page = vm_page_alloc_noobj_contig(req, npages, 0, pmax,
PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
@@ -165,8 +170,24 @@ linux_free_pages(struct page *page, unsigned int order)
for (x = 0; x != npages; x++) {
vm_page_t pgo = page + x;
- if (vm_page_unwire_noq(pgo))
- vm_page_free(pgo);
+ /*
+ * The "free page" function is used in several
+ * contexts.
+ *
+ * Some pages are allocated by `linux_alloc_pages()`
+ * above, but not all of them are. For instance in the
+ * DRM drivers, some pages come from
+ * `shmem_read_mapping_page_gfp()`.
+ *
+ * That's why we need to check if the page is managed
+ * or not here.
+ */
+ if ((pgo->oflags & VPO_UNMANAGED) == 0) {
+ vm_page_unwire(pgo, PQ_ACTIVE);
+ } else {
+ if (vm_page_unwire_noq(pgo))
+ vm_page_free(pgo);
+ }
}
} else {
vm_offset_t vaddr;
@@ -177,6 +198,17 @@ linux_free_pages(struct page *page, unsigned int order)
}
}
+void
+linux_release_pages(release_pages_arg arg, int nr)
+{
+ int i;
+
+ CTASSERT(offsetof(struct folio, page) == 0);
+
+ for (i = 0; i < nr; i++)
+ __free_page(arg.pages[i]);
+}
+
vm_offset_t
linux_alloc_kmem(gfp_t flags, unsigned int order)
{
@@ -296,23 +328,27 @@ vm_fault_t
lkpi_vmf_insert_pfn_prot_locked(struct vm_area_struct *vma, unsigned long addr,
unsigned long pfn, pgprot_t prot)
{
+ struct pctrie_iter pages;
vm_object_t vm_obj = vma->vm_obj;
vm_object_t tmp_obj;
vm_page_t page;
vm_pindex_t pindex;
VM_OBJECT_ASSERT_WLOCKED(vm_obj);
+ vm_page_iter_init(&pages, vm_obj);
pindex = OFF_TO_IDX(addr - vma->vm_start);
if (vma->vm_pfn_count == 0)
vma->vm_pfn_first = pindex;
MPASS(pindex <= OFF_TO_IDX(vma->vm_end));
retry:
- page = vm_page_grab(vm_obj, pindex, VM_ALLOC_NOCREAT);
+ page = vm_page_grab_iter(vm_obj, pindex, VM_ALLOC_NOCREAT, &pages);
if (page == NULL) {
page = PHYS_TO_VM_PAGE(IDX_TO_OFF(pfn));
- if (!vm_page_busy_acquire(page, VM_ALLOC_WAITFAIL))
+ if (!vm_page_busy_acquire(page, VM_ALLOC_WAITFAIL)) {
+ pctrie_iter_reset(&pages);
goto retry;
+ }
if (page->object != NULL) {
tmp_obj = page->object;
vm_page_xunbusy(page);
@@ -336,10 +372,11 @@ retry:
vm_page_remove(page);
}
VM_OBJECT_WUNLOCK(tmp_obj);
+ pctrie_iter_reset(&pages);
VM_OBJECT_WLOCK(vm_obj);
goto retry;
}
- if (vm_page_insert(page, vm_obj, pindex)) {
+ if (vm_page_iter_insert(page, vm_obj, pindex, &pages) != 0) {
vm_page_xunbusy(page);
return (VM_FAULT_OOM);
}
diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c
index fa5e3c71fad0..43fd6ad28ac4 100644
--- a/sys/compat/linuxkpi/common/src/linux_pci.c
+++ b/sys/compat/linuxkpi/common/src/linux_pci.c
@@ -1,7 +1,7 @@
/*-
* Copyright (c) 2015-2016 Mellanox Technologies, Ltd.
* All rights reserved.
- * Copyright (c) 2020-2022 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
*
* Portions of this software were developed by Björn Zeeb
* under sponsorship from the FreeBSD Foundation.
@@ -43,13 +43,13 @@
#include <sys/pctrie.h>
#include <sys/rman.h>
#include <sys/rwlock.h>
+#include <sys/stdarg.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/resource.h>
-#include <machine/stdarg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pci_private.h>
@@ -111,6 +111,9 @@ static device_method_t pci_methods[] = {
DEVMETHOD(pci_iov_uninit, linux_pci_iov_uninit),
DEVMETHOD(pci_iov_add_vf, linux_pci_iov_add_vf),
+ /* Bus interface. */
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+
/* backlight interface */
DEVMETHOD(backlight_update_status, linux_backlight_update_status),
DEVMETHOD(backlight_get_status, linux_backlight_get_status),
@@ -145,6 +148,23 @@ struct linux_dma_priv {
#define DMA_PRIV_LOCK(priv) mtx_lock(&(priv)->lock)
#define DMA_PRIV_UNLOCK(priv) mtx_unlock(&(priv)->lock)
+static void
+lkpi_set_pcim_iomap_devres(struct pcim_iomap_devres *dr, int bar,
+ void *res)
+{
+ dr->mmio_table[bar] = (void *)rman_get_bushandle(res);
+ dr->res_table[bar] = res;
+}
+
+static bool
+lkpi_pci_bar_id_valid(int bar)
+{
+ if (bar < 0 || bar > PCIR_MAX_BAR_0)
+ return (false);
+
+ return (true);
+}
+
static int
linux_pdev_dma_uninit(struct pci_dev *pdev)
{
@@ -285,20 +305,30 @@ linux_pci_find(device_t dev, const struct pci_device_id **idp)
}
struct pci_dev *
-lkpi_pci_get_device(uint16_t vendor, uint16_t device, struct pci_dev *odev)
+lkpi_pci_get_device(uint32_t vendor, uint32_t device, struct pci_dev *odev)
{
- struct pci_dev *pdev;
-
- KASSERT(odev == NULL, ("%s: odev argument not yet supported\n", __func__));
+ struct pci_dev *pdev, *found;
+ found = NULL;
spin_lock(&pci_lock);
list_for_each_entry(pdev, &pci_devices, links) {
- if (pdev->vendor == vendor && pdev->device == device)
+ /* Walk until we find odev. */
+ if (odev != NULL) {
+ if (pdev == odev)
+ odev = NULL;
+ continue;
+ }
+
+ if ((pdev->vendor == vendor || vendor == PCI_ANY_ID) &&
+ (pdev->device == device || device == PCI_ANY_ID)) {
+ found = pdev;
break;
+ }
}
+ pci_dev_get(found);
spin_unlock(&pci_lock);
- return (pdev);
+ return (found);
}
static void
@@ -309,9 +339,18 @@ lkpi_pci_dev_release(struct device *dev)
spin_lock_destroy(&dev->devres_lock);
}
-static void
+static int
lkpifill_pci_dev(device_t dev, struct pci_dev *pdev)
{
+ int error;
+
+ error = kobject_init_and_add(&pdev->dev.kobj, &linux_dev_ktype,
+ &linux_root_device.kobj, device_get_nameunit(dev));
+ if (error != 0) {
+ printf("%s:%d: kobject_init_and_add returned %d\n",
+ __func__, __LINE__, error);
+ return (error);
+ }
pdev->devfn = PCI_DEVFN(pci_get_slot(dev), pci_get_function(dev));
pdev->vendor = pci_get_vendor(dev);
@@ -341,12 +380,10 @@ lkpifill_pci_dev(device_t dev, struct pci_dev *pdev)
pdev->msi_desc = malloc(pci_msi_count(dev) *
sizeof(*pdev->msi_desc), M_DEVBUF, M_WAITOK | M_ZERO);
- kobject_init(&pdev->dev.kobj, &linux_dev_ktype);
- kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev));
- kobject_add(&pdev->dev.kobj, &linux_root_device.kobj,
- kobject_name(&pdev->dev.kobj));
spin_lock_init(&pdev->dev.devres_lock);
INIT_LIST_HEAD(&pdev->dev.devres_head);
+
+ return (0);
}
static void
@@ -374,9 +411,14 @@ struct pci_dev *
lkpinew_pci_dev(device_t dev)
{
struct pci_dev *pdev;
+ int error;
pdev = malloc(sizeof(*pdev), M_DEVBUF, M_WAITOK|M_ZERO);
- lkpifill_pci_dev(dev, pdev);
+ error = lkpifill_pci_dev(dev, pdev);
+ if (error != 0) {
+ free(pdev, M_DEVBUF);
+ return (NULL);
+ }
pdev->dev.release = lkpinew_pci_dev_release;
return (pdev);
@@ -525,7 +567,10 @@ linux_pci_attach_device(device_t dev, struct pci_driver *pdrv,
device_set_ivars(dev, dinfo);
}
- lkpifill_pci_dev(dev, pdev);
+ error = lkpifill_pci_dev(dev, pdev);
+ if (error != 0)
+ return (error);
+
if (isdrm)
PCI_GET_ID(device_get_parent(parent), parent, PCI_ID_RID, &rid);
else
@@ -733,11 +778,14 @@ linuxkpi_pcim_iomap_table(struct pci_dev *pdev)
}
static struct resource *
-_lkpi_pci_iomap(struct pci_dev *pdev, int bar, int mmio_size __unused)
+_lkpi_pci_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen __unused)
{
struct pci_mmio_region *mmio, *p;
int type;
+ if (!lkpi_pci_bar_id_valid(bar))
+ return (NULL);
+
type = pci_resource_type(pdev, bar);
if (type < 0) {
device_printf(pdev->dev.bsddev, "%s: bar %d type %d\n",
@@ -773,25 +821,60 @@ _lkpi_pci_iomap(struct pci_dev *pdev, int bar, int mmio_size __unused)
}
void *
-linuxkpi_pci_iomap_range(struct pci_dev *pdev, int mmio_bar,
- unsigned long mmio_off, unsigned long mmio_size)
+linuxkpi_pci_iomap_range(struct pci_dev *pdev, int bar,
+ unsigned long off, unsigned long maxlen)
{
struct resource *res;
- res = _lkpi_pci_iomap(pdev, mmio_bar, mmio_size);
+ if (!lkpi_pci_bar_id_valid(bar))
+ return (NULL);
+
+ res = _lkpi_pci_iomap(pdev, bar, maxlen);
if (res == NULL)
return (NULL);
/* This is a FreeBSD extension so we can use bus_*(). */
if (pdev->want_iomap_res)
return (res);
- MPASS(mmio_off < rman_get_size(res));
- return ((void *)(rman_get_bushandle(res) + mmio_off));
+ MPASS(off < rman_get_size(res));
+ return ((void *)(rman_get_bushandle(res) + off));
}
void *
-linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size)
+linuxkpi_pci_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen)
{
- return (linuxkpi_pci_iomap_range(pdev, mmio_bar, 0, mmio_size));
+ if (!lkpi_pci_bar_id_valid(bar))
+ return (NULL);
+
+ return (linuxkpi_pci_iomap_range(pdev, bar, 0, maxlen));
+}
+
+void *
+linuxkpi_pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen)
+{
+ struct pcim_iomap_devres *dr;
+ void *res;
+
+ if (!lkpi_pci_bar_id_valid(bar))
+ return (NULL);
+
+ dr = lkpi_pcim_iomap_devres_find(pdev);
+ if (dr == NULL)
+ return (NULL);
+
+ if (dr->res_table[bar] != NULL)
+ return (dr->res_table[bar]);
+
+ res = linuxkpi_pci_iomap(pdev, bar, maxlen);
+ if (res == NULL) {
+ /*
+ * Do not free the devres in case there were
+ * other valid mappings before already.
+ */
+ return (NULL);
+ }
+ lkpi_set_pcim_iomap_devres(dr, bar, res);
+
+ return (res);
}
void
@@ -845,8 +928,7 @@ linuxkpi_pcim_iomap_regions(struct pci_dev *pdev, uint32_t mask, const char *nam
res = _lkpi_pci_iomap(pdev, bar, 0);
if (res == NULL)
goto err;
- dr->mmio_table[bar] = (void *)rman_get_bushandle(res);
- dr->res_table[bar] = res;
+ lkpi_set_pcim_iomap_devres(dr, bar, res);
mappings |= (1 << bar);
}
@@ -1080,8 +1162,9 @@ pci_resource_len(struct pci_dev *pdev, int bar)
return (rle->count);
}
-int
-pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
+static int
+lkpi_pci_request_region(struct pci_dev *pdev, int bar, const char *res_name,
+ bool managed)
{
struct resource *res;
struct pci_devres *dr;
@@ -1089,9 +1172,20 @@ pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
int rid;
int type;
+ if (!lkpi_pci_bar_id_valid(bar))
+ return (-EINVAL);
+
+ /*
+ * If the bar is not valid, return success without adding the BAR;
+ * otherwise linuxkpi_pcim_request_all_regions() will error.
+ */
+ if (pci_resource_len(pdev, bar) == 0)
+ return (0);
+ /* Likewise if it is neither IO nor MEM, nothing to do for us. */
type = pci_resource_type(pdev, bar);
if (type < 0)
- return (-ENODEV);
+ return (0);
+
rid = PCIR_BAR(bar);
res = bus_alloc_resource_any(pdev->dev.bsddev, type, &rid,
RF_ACTIVE|RF_SHAREABLE);
@@ -1104,11 +1198,16 @@ pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
/*
* It seems there is an implicit devres tracking on these if the device
- * is managed; otherwise the resources are not automatiaclly freed on
- * FreeBSD/LinuxKPI tough they should be/are expected to be by Linux
- * drivers.
+ * is managed (lkpi_pci_devres_find() case); otherwise the resources are
+ * not automatically freed on FreeBSD/LinuxKPI though they should be/are
+ * expected to be by Linux drivers.
+ * Otherwise if we are called from a pcim-function with the managed
+ * argument set, we need to track devres independent of pdev->managed.
*/
- dr = lkpi_pci_devres_find(pdev);
+ if (managed)
+ dr = lkpi_pci_devres_get_alloc(pdev);
+ else
+ dr = lkpi_pci_devres_find(pdev);
if (dr != NULL) {
dr->region_mask |= (1 << bar);
dr->region_table[bar] = res;
@@ -1125,6 +1224,12 @@ pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
}
int
+linuxkpi_pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
+{
+ return (lkpi_pci_request_region(pdev, bar, res_name, false));
+}
+
+int
linuxkpi_pci_request_regions(struct pci_dev *pdev, const char *res_name)
{
int error;
@@ -1140,6 +1245,24 @@ linuxkpi_pci_request_regions(struct pci_dev *pdev, const char *res_name)
return (0);
}
+int
+linuxkpi_pcim_request_all_regions(struct pci_dev *pdev, const char *res_name)
+{
+ int bar, error;
+
+ for (bar = 0; bar <= PCIR_MAX_BAR_0; bar++) {
+ error = lkpi_pci_request_region(pdev, bar, res_name, true);
+ if (error != 0) {
+ device_printf(pdev->dev.bsddev, "%s: bar %d res_name '%s': "
+ "lkpi_pci_request_region returned %d\n", __func__,
+ bar, res_name, error);
+ pci_release_regions(pdev);
+ return (error);
+ }
+ }
+ return (0);
+}
+
void
linuxkpi_pci_release_region(struct pci_dev *pdev, int bar)
{
@@ -1511,17 +1634,34 @@ linux_dma_map_phys_common(struct device *dev __unused, vm_paddr_t phys,
#endif
dma_addr_t
-linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len)
+lkpi_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len,
+ enum dma_data_direction direction, unsigned long attrs)
{
struct linux_dma_priv *priv;
+ dma_addr_t dma;
priv = dev->dma_priv;
- return (linux_dma_map_phys_common(dev, phys, len, priv->dmat));
+ dma = linux_dma_map_phys_common(dev, phys, len, priv->dmat);
+ if (dma_mapping_error(dev, dma))
+ return (dma);
+
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
+ dma_sync_single_for_device(dev, dma, len, direction);
+
+ return (dma);
+}
+
+/* For backward compat only so we can MFC this. Remove before 15. */
+dma_addr_t
+linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len)
+{
+ return (lkpi_dma_map_phys(dev, phys, len, DMA_NONE, 0));
}
#if defined(__i386__) || defined(__amd64__) || defined(__aarch64__)
void
-linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
+lkpi_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len,
+ enum dma_data_direction direction, unsigned long attrs)
{
struct linux_dma_priv *priv;
struct linux_dma_obj *obj;
@@ -1538,6 +1678,10 @@ linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
return;
}
LINUX_DMA_PCTRIE_REMOVE(&priv->ptree, dma_addr);
+
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
+ dma_sync_single_for_cpu(dev, dma_addr, len, direction);
+
bus_dmamap_unload(obj->dmat, obj->dmamap);
bus_dmamap_destroy(obj->dmat, obj->dmamap);
DMA_PRIV_UNLOCK(priv);
@@ -1546,11 +1690,19 @@ linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
}
#else
void
-linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
+lkpi_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len,
+ enum dma_data_direction direction, unsigned long attrs)
{
}
#endif
+/* For backward compat only so we can MFC this. Remove before 15. */
+void
+linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
+{
+ lkpi_dma_unmap(dev, dma_addr, len, DMA_NONE, 0);
+}
+
void *
linux_dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)
@@ -1652,7 +1804,7 @@ linuxkpi_dma_sync(struct device *dev, dma_addr_t dma_addr, size_t size,
int
linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
- enum dma_data_direction direction, unsigned long attrs __unused)
+ enum dma_data_direction direction, unsigned long attrs)
{
struct linux_dma_priv *priv;
struct scatterlist *sg;
@@ -1686,6 +1838,9 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
sg_dma_address(sg) = seg.ds_addr;
}
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) != 0)
+ goto skip_sync;
+
switch (direction) {
case DMA_BIDIRECTIONAL:
bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE);
@@ -1699,6 +1854,7 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
default:
break;
}
+skip_sync:
DMA_PRIV_UNLOCK(priv);
@@ -1708,7 +1864,7 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
void
linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
int nents __unused, enum dma_data_direction direction,
- unsigned long attrs __unused)
+ unsigned long attrs)
{
struct linux_dma_priv *priv;
@@ -1716,6 +1872,9 @@ linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
DMA_PRIV_LOCK(priv);
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) != 0)
+ goto skip_sync;
+
switch (direction) {
case DMA_BIDIRECTIONAL:
bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTREAD);
@@ -1730,6 +1889,7 @@ linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
default:
break;
}
+skip_sync:
bus_dmamap_unload(priv->dmat, sgl->dma_map);
bus_dmamap_destroy(priv->dmat, sgl->dma_map);
diff --git a/sys/compat/linuxkpi/common/src/linux_schedule.c b/sys/compat/linuxkpi/common/src/linux_schedule.c
index fa20a11f5ec7..c6b7a2ebbd66 100644
--- a/sys/compat/linuxkpi/common/src/linux_schedule.c
+++ b/sys/compat/linuxkpi/common/src/linux_schedule.c
@@ -38,29 +38,47 @@
#include <linux/spinlock.h>
#include <linux/wait.h>
+/*
+ * Convert a relative time in jiffies to a tick count, suitable for use with
+ * native FreeBSD interfaces (callouts, sleepqueues, etc.).
+ */
+static int
+linux_jiffies_timeout_to_ticks(long timeout)
+{
+ if (timeout < 1)
+ return (1);
+ else if (timeout == MAX_SCHEDULE_TIMEOUT)
+ return (0);
+ else if (timeout > INT_MAX)
+ return (INT_MAX);
+ else
+ return (timeout);
+}
+
static int
linux_add_to_sleepqueue(void *wchan, struct task_struct *task,
- const char *wmesg, int timeout, int state)
+ const char *wmesg, long timeout, int state)
{
- int flags, ret;
+ int flags, ret, stimeout;
MPASS((state & ~(TASK_PARKED | TASK_NORMAL)) == 0);
flags = SLEEPQ_SLEEP | ((state & TASK_INTERRUPTIBLE) != 0 ?
SLEEPQ_INTERRUPTIBLE : 0);
+ stimeout = linux_jiffies_timeout_to_ticks(timeout);
sleepq_add(wchan, NULL, wmesg, flags, 0);
- if (timeout != 0)
- sleepq_set_timeout(wchan, timeout);
+ if (stimeout != 0)
+ sleepq_set_timeout(wchan, stimeout);
DROP_GIANT();
if ((state & TASK_INTERRUPTIBLE) != 0) {
- if (timeout == 0)
+ if (stimeout == 0)
ret = -sleepq_wait_sig(wchan, 0);
else
ret = -sleepq_timedwait_sig(wchan, 0);
} else {
- if (timeout == 0) {
+ if (stimeout == 0) {
sleepq_wait(wchan, 0);
ret = 0;
} else
@@ -182,6 +200,65 @@ default_wake_function(wait_queue_t *wq, unsigned int state, int flags,
return (wake_up_task(wq->private, state));
}
+long
+linux_wait_woken(wait_queue_t *wq, unsigned state, long timeout)
+{
+ void *wchan;
+ struct task_struct *task;
+ int ret;
+ int remainder;
+
+ task = current;
+ wchan = wq->private;
+
+ remainder = jiffies + timeout;
+
+ set_task_state(task, state);
+
+ sleepq_lock(wchan);
+ if (!(wq->flags & WQ_FLAG_WOKEN)) {
+ ret = linux_add_to_sleepqueue(wchan, task, "woken",
+ timeout, state);
+ } else {
+ sleepq_release(wchan);
+ ret = 0;
+ }
+
+ set_task_state(task, TASK_RUNNING);
+ wq->flags &= ~WQ_FLAG_WOKEN;
+
+ if (timeout == MAX_SCHEDULE_TIMEOUT)
+ return (MAX_SCHEDULE_TIMEOUT);
+
+ /* range check return value */
+ remainder -= jiffies;
+
+ /* range check return value */
+ if (ret == -ERESTARTSYS && remainder < 1)
+ remainder = 1;
+ else if (remainder < 0)
+ remainder = 0;
+ else if (remainder > timeout)
+ remainder = timeout;
+ return (remainder);
+}
+
+int
+woken_wake_function(wait_queue_t *wq, unsigned int state,
+ int flags __unused, void *key __unused)
+{
+ void *wchan;
+
+ wchan = wq->private;
+
+ sleepq_lock(wchan);
+ wq->flags |= WQ_FLAG_WOKEN;
+ sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);
+ sleepq_release(wchan);
+
+ return (1);
+}
+
void
linux_init_wait_entry(wait_queue_t *wq, int flags)
{
@@ -249,7 +326,7 @@ linux_waitqueue_active(wait_queue_head_t *wqh)
}
int
-linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout,
+linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, long timeout,
unsigned int state, spinlock_t *lock)
{
struct task_struct *task;
@@ -258,12 +335,6 @@ linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout,
if (lock != NULL)
spin_unlock_irq(lock);
- /* range check timeout */
- if (timeout < 1)
- timeout = 1;
- else if (timeout == MAX_SCHEDULE_TIMEOUT)
- timeout = 0;
-
task = current;
sleepq_lock(task);
@@ -280,23 +351,16 @@ linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout,
return (ret);
}
-int
-linux_schedule_timeout(int timeout)
+long
+linux_schedule_timeout(long timeout)
{
struct task_struct *task;
- int ret;
- int state;
- int remainder;
+ long remainder;
+ int ret, state;
task = current;
- /* range check timeout */
- if (timeout < 1)
- timeout = 1;
- else if (timeout == MAX_SCHEDULE_TIMEOUT)
- timeout = 0;
-
- remainder = ticks + timeout;
+ remainder = jiffies + timeout;
sleepq_lock(task);
state = atomic_read(&task->state);
@@ -309,11 +373,11 @@ linux_schedule_timeout(int timeout)
}
set_task_state(task, TASK_RUNNING);
- if (timeout == 0)
+ if (timeout == MAX_SCHEDULE_TIMEOUT)
return (MAX_SCHEDULE_TIMEOUT);
/* range check return value */
- remainder -= ticks;
+ remainder -= jiffies;
/* range check return value */
if (ret == -ERESTARTSYS && remainder < 1)
@@ -344,18 +408,12 @@ linux_wake_up_bit(void *word, int bit)
int
linux_wait_on_bit_timeout(unsigned long *word, int bit, unsigned int state,
- int timeout)
+ long timeout)
{
struct task_struct *task;
void *wchan;
int ret;
- /* range check timeout */
- if (timeout < 1)
- timeout = 1;
- else if (timeout == MAX_SCHEDULE_TIMEOUT)
- timeout = 0;
-
task = current;
wchan = bit_to_wchan(word, bit);
for (;;) {
diff --git a/sys/compat/linuxkpi/common/src/linux_seq_file.c b/sys/compat/linuxkpi/common/src/linux_seq_file.c
index 8b426825cc78..9c06fe27bebe 100644
--- a/sys/compat/linuxkpi/common/src/linux_seq_file.c
+++ b/sys/compat/linuxkpi/common/src/linux_seq_file.c
@@ -64,13 +64,10 @@ seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
return (-EINVAL);
size = min(rc - *ppos, size);
- rc = strscpy(ubuf, sbuf_data(sbuf) + *ppos, size + 1);
+ memcpy(ubuf, sbuf_data(sbuf) + *ppos, size);
+ *ppos += size;
- /* add 1 for null terminator */
- if (rc > 0)
- rc += 1;
-
- return (rc);
+ return (size);
}
int
diff --git a/sys/compat/linuxkpi/common/src/linux_skbuff.c b/sys/compat/linuxkpi/common/src/linux_skbuff.c
index 16a7083123be..abfb642ba708 100644
--- a/sys/compat/linuxkpi/common/src/linux_skbuff.c
+++ b/sys/compat/linuxkpi/common/src/linux_skbuff.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2022 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
* Copyright (c) 2021-2022 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -42,6 +42,8 @@
#include <sys/malloc.h>
#include <sys/sysctl.h>
+#include <vm/uma.h>
+
#ifdef DDB
#include <ddb/ddb.h>
#endif
@@ -63,68 +65,82 @@ SYSCTL_INT(_compat_linuxkpi_skb, OID_AUTO, debug, CTLFLAG_RWTUN,
&linuxkpi_debug_skb, 0, "SKB debug level");
#endif
-#ifdef __LP64__
+static uma_zone_t skbzone;
+
+#define SKB_DMA32_MALLOC
+#ifdef SKB_DMA32_MALLOC
/*
* Realtek wireless drivers (e.g., rtw88) require 32bit DMA in a single segment.
* busdma(9) has a hard time providing this currently for 3-ish pages at large
* quantities (see lkpi_pci_nseg1_fail in linux_pci.c).
* Work around this for now by allowing a tunable to enforce physical addresses
- * allocation limits on 64bit platforms using "old-school" contigmalloc(9) to
- * avoid bouncing.
+ * allocation limits using "old-school" contigmalloc(9) to avoid bouncing.
+ * Note: with the malloc/contigmalloc + kmalloc changes also providing physical
+ * contiguous memory, and the nseg=1 limit for bouncing we should in theory be
+ * fine now and not need any of this anymore, however busdma still has troubles
+ * boncing three contiguous pages so for now this stays.
*/
static int linuxkpi_skb_memlimit;
SYSCTL_INT(_compat_linuxkpi_skb, OID_AUTO, mem_limit, CTLFLAG_RDTUN,
&linuxkpi_skb_memlimit, 0, "SKB memory limit: 0=no limit, "
"1=32bit, 2=36bit, other=undef (currently 32bit)");
-#endif
static MALLOC_DEFINE(M_LKPISKB, "lkpiskb", "Linux KPI skbuff compat");
+#endif
struct sk_buff *
linuxkpi_alloc_skb(size_t size, gfp_t gfp)
{
struct sk_buff *skb;
+ void *p;
size_t len;
- len = sizeof(*skb) + size + sizeof(struct skb_shared_info);
+ skb = uma_zalloc(skbzone, linux_check_m_flags(gfp) | M_ZERO);
+ if (skb == NULL)
+ return (NULL);
+
+ skb->prev = skb->next = skb;
+ skb->truesize = size;
+ skb->shinfo = (struct skb_shared_info *)(skb + 1);
+
+ if (size == 0)
+ return (skb);
+
+ len = size;
+#ifdef SKB_DMA32_MALLOC
/*
* Using our own type here not backing my kmalloc.
* We assume no one calls kfree directly on the skb.
*/
-#ifdef __LP64__
- if (__predict_true(linuxkpi_skb_memlimit == 0)) {
- skb = malloc(len, M_LKPISKB, linux_check_m_flags(gfp) | M_ZERO);
- } else {
+ if (__predict_false(linuxkpi_skb_memlimit != 0)) {
vm_paddr_t high;
switch (linuxkpi_skb_memlimit) {
+#ifdef __LP64__
case 2:
high = (0xfffffffff); /* 1<<36 really. */
break;
+#endif
case 1:
default:
high = (0xffffffff); /* 1<<32 really. */
break;
}
len = roundup_pow_of_two(len);
- skb = contigmalloc(len, M_LKPISKB,
+ p = contigmalloc(len, M_LKPISKB,
linux_check_m_flags(gfp) | M_ZERO, 0, high, PAGE_SIZE, 0);
- }
-#else
- skb = malloc(len, M_LKPISKB, linux_check_m_flags(gfp) | M_ZERO);
+ } else
#endif
- if (skb == NULL)
- return (skb);
- skb->_alloc_len = len;
- skb->truesize = size;
+ p = __kmalloc(len, linux_check_m_flags(gfp) | M_ZERO);
+ if (p == NULL) {
+ uma_zfree(skbzone, skb);
+ return (NULL);
+ }
- skb->head = skb->data = skb->tail = (uint8_t *)(skb+1);
+ skb->head = skb->data = (uint8_t *)p;
+ skb_reset_tail_pointer(skb);
skb->end = skb->head + size;
- skb->prev = skb->next = skb;
-
- skb->shinfo = (struct skb_shared_info *)(skb->end);
-
SKB_TRACE_FMT(skb, "data %p size %zu", (skb) ? skb->data : NULL, size);
return (skb);
}
@@ -162,8 +178,8 @@ linuxkpi_build_skb(void *data, size_t fragsz)
skb->_flags |= _SKB_FLAGS_SKBEXTFRAG;
skb->truesize = fragsz;
skb->head = skb->data = data;
- skb_reset_tail_pointer(skb); /* XXX is that correct? */
- skb->end = (void *)((uintptr_t)skb->head + fragsz);
+ skb_reset_tail_pointer(skb);
+ skb->end = skb->head + fragsz;
return (skb);
}
@@ -256,17 +272,34 @@ linuxkpi_kfree_skb(struct sk_buff *skb)
p = skb->head;
skb_free_frag(p);
+ skb->head = NULL;
}
-#ifdef __LP64__
- if (__predict_true(linuxkpi_skb_memlimit == 0))
- free(skb, M_LKPISKB);
+#ifdef SKB_DMA32_MALLOC
+ if (__predict_false(linuxkpi_skb_memlimit != 0))
+ free(skb->head, M_LKPISKB);
else
- contigfree(skb, skb->_alloc_len, M_LKPISKB);
-#else
- free(skb, M_LKPISKB);
#endif
+ kfree(skb->head);
+ uma_zfree(skbzone, skb);
+}
+
+static void
+lkpi_skbuff_init(void *arg __unused)
+{
+ skbzone = uma_zcreate("skbuff",
+ sizeof(struct sk_buff) + sizeof(struct skb_shared_info),
+ NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+ /* Do we need to apply limits? */
+}
+SYSINIT(linuxkpi_skbuff, SI_SUB_DRIVERS, SI_ORDER_FIRST, lkpi_skbuff_init, NULL);
+
+static void
+lkpi_skbuff_destroy(void *arg __unused)
+{
+ uma_zdestroy(skbzone);
}
+SYSUNINIT(linuxkpi_skbuff, SI_SUB_DRIVERS, SI_ORDER_SECOND, lkpi_skbuff_destroy, NULL);
#ifdef DDB
DB_SHOW_COMMAND(skb, db_show_skb)
@@ -284,9 +317,8 @@ DB_SHOW_COMMAND(skb, db_show_skb)
db_printf("skb %p\n", skb);
db_printf("\tnext %p prev %p\n", skb->next, skb->prev);
db_printf("\tlist %p\n", &skb->list);
- db_printf("\t_alloc_len %u len %u data_len %u truesize %u mac_len %u\n",
- skb->_alloc_len, skb->len, skb->data_len, skb->truesize,
- skb->mac_len);
+ db_printf("\tlen %u data_len %u truesize %u mac_len %u\n",
+ skb->len, skb->data_len, skb->truesize, skb->mac_len);
db_printf("\tcsum %#06x l3hdroff %u l4hdroff %u priority %u qmap %u\n",
skb->csum, skb->l3hdroff, skb->l4hdroff, skb->priority, skb->qmap);
db_printf("\tpkt_type %d dev %p sk %p\n",
diff --git a/sys/compat/linuxkpi/common/src/linux_slab.c b/sys/compat/linuxkpi/common/src/linux_slab.c
index bc780ab3f609..6f71d17a3770 100644
--- a/sys/compat/linuxkpi/common/src/linux_slab.c
+++ b/sys/compat/linuxkpi/common/src/linux_slab.c
@@ -1,6 +1,10 @@
/*-
* Copyright (c) 2017 Mellanox Technologies, Ltd.
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Björn Zeeb
+ * 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
@@ -208,6 +212,18 @@ linux_kmem_cache_destroy(struct linux_kmem_cache *c)
}
void *
+lkpi___kmalloc_node(size_t size, gfp_t flags, int node)
+{
+ if (size <= PAGE_SIZE)
+ return (malloc_domainset(size, M_KMALLOC,
+ linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+ else
+ return (contigmalloc_domainset(size, M_KMALLOC,
+ linux_get_vm_domain_set(node), linux_check_m_flags(flags),
+ 0, -1UL, PAGE_SIZE, 0));
+}
+
+void *
lkpi___kmalloc(size_t size, gfp_t flags)
{
size_t _s;
@@ -222,6 +238,41 @@ lkpi___kmalloc(size_t size, gfp_t flags)
0, -1UL, PAGE_SIZE, 0));
}
+void *
+lkpi_krealloc(void *ptr, size_t size, gfp_t flags)
+{
+ void *nptr;
+ size_t osize;
+
+ /*
+ * First handle invariants based on function arguments.
+ */
+ if (ptr == NULL)
+ return (kmalloc(size, flags));
+
+ osize = ksize(ptr);
+ if (size <= osize)
+ return (ptr);
+
+ /*
+ * We know the new size > original size. realloc(9) does not (and cannot)
+ * know about our requirements for physically contiguous memory, so we can
+ * only call it for sizes up to and including PAGE_SIZE, and otherwise have
+ * to replicate its functionality using kmalloc to get the contigmalloc(9)
+ * backing.
+ */
+ if (size <= PAGE_SIZE)
+ return (realloc(ptr, size, M_KMALLOC, linux_check_m_flags(flags)));
+
+ nptr = kmalloc(size, flags);
+ if (nptr == NULL)
+ return (NULL);
+
+ memcpy(nptr, ptr, osize);
+ kfree(ptr);
+ return (nptr);
+}
+
struct lkpi_kmalloc_ctx {
size_t size;
gfp_t flags;
@@ -246,6 +297,23 @@ lkpi_kmalloc(size_t size, gfp_t flags)
}
static void
+lkpi_kvmalloc_cb(void *ctx)
+{
+ struct lkpi_kmalloc_ctx *lmc = ctx;
+
+ lmc->addr = malloc(lmc->size, M_KMALLOC, linux_check_m_flags(lmc->flags));
+}
+
+void *
+lkpi_kvmalloc(size_t size, gfp_t flags)
+{
+ struct lkpi_kmalloc_ctx lmc = { .size = size, .flags = flags };
+
+ lkpi_fpu_safe_exec(&lkpi_kvmalloc_cb, &lmc);
+ return(lmc.addr);
+}
+
+static void
linux_kfree_async_fn(void *context, int pending)
{
struct llist_node *freed;
@@ -256,7 +324,7 @@ linux_kfree_async_fn(void *context, int pending)
static struct task linux_kfree_async_task =
TASK_INITIALIZER(0, linux_kfree_async_fn, &linux_kfree_async_task);
-void
+static void
linux_kfree_async(void *addr)
{
if (addr == NULL)
@@ -264,3 +332,16 @@ linux_kfree_async(void *addr)
llist_add(addr, &linux_kfree_async_list);
taskqueue_enqueue(linux_irq_work_tq, &linux_kfree_async_task);
}
+
+void
+lkpi_kfree(const void *ptr)
+{
+ if (ZERO_OR_NULL_PTR(ptr))
+ return;
+
+ if (curthread->td_critnest != 0)
+ linux_kfree_async(__DECONST(void *, ptr));
+ else
+ free(__DECONST(void *, ptr), M_KMALLOC);
+}
+
diff --git a/sys/compat/linuxkpi/common/src/linux_work.c b/sys/compat/linuxkpi/common/src/linux_work.c
index 939bdbbc1434..b1975d16025e 100644
--- a/sys/compat/linuxkpi/common/src/linux_work.c
+++ b/sys/compat/linuxkpi/common/src/linux_work.c
@@ -212,7 +212,7 @@ linux_flush_rcu_work(struct rcu_work *rwork)
*/
bool
linux_queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
- struct delayed_work *dwork, unsigned delay)
+ struct delayed_work *dwork, unsigned long delay)
{
static const uint8_t states[WORK_ST_MAX] __aligned(8) = {
[WORK_ST_IDLE] = WORK_ST_TIMER, /* start timeout */
@@ -226,6 +226,13 @@ linux_queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
if (atomic_read(&wq->draining) != 0)
return (!work_pending(&dwork->work));
+ /*
+ * Clamp the delay to a valid ticks value, some consumers pass
+ * MAX_SCHEDULE_TIMEOUT.
+ */
+ if (delay > INT_MAX)
+ delay = INT_MAX;
+
mtx_lock(&dwork->timer.mtx);
switch (linux_update_state(&dwork->work.state, states)) {
case WORK_ST_EXEC:
diff --git a/sys/compat/linuxkpi/dummy/include/kunit/skbuff.h b/sys/compat/linuxkpi/dummy/include/kunit/skbuff.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/sys/compat/linuxkpi/dummy/include/kunit/skbuff.h
diff --git a/sys/compat/linuxkpi/dummy/include/kunit/test-bug.h b/sys/compat/linuxkpi/dummy/include/kunit/test-bug.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/sys/compat/linuxkpi/dummy/include/kunit/test-bug.h
diff --git a/sys/compat/linuxkpi/dummy/include/kunit/test.h b/sys/compat/linuxkpi/dummy/include/kunit/test.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/sys/compat/linuxkpi/dummy/include/kunit/test.h
diff --git a/sys/compat/linuxkpi/dummy/include/linux/regmap.h b/sys/compat/linuxkpi/dummy/include/linux/regmap.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/sys/compat/linuxkpi/dummy/include/linux/regmap.h
diff --git a/sys/compat/linuxkpi/dummy/include/linux/unaligned.h b/sys/compat/linuxkpi/dummy/include/linux/unaligned.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/sys/compat/linuxkpi/dummy/include/linux/unaligned.h
diff --git a/sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h b/sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/sys/compat/linuxkpi/dummy/include/net/page_pool/helpers.h