diff options
Diffstat (limited to 'sys/compat/linuxkpi/common')
131 files changed, 7926 insertions, 2718 deletions
diff --git a/sys/compat/linuxkpi/common/include/acpi/acpi.h b/sys/compat/linuxkpi/common/include/acpi/acpi.h index e0218bdde12e..1e398d05ba20 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_verison + * 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 * @@ -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 f107902a26ad..47195e7d66a6 100644 --- a/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h +++ b/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h @@ -29,6 +29,9 @@ #ifndef _LINUXKPI_ACPI_ACPI_BUS_H_ #define _LINUXKPI_ACPI_ACPI_BUS_H_ +/* Aliase struct acpi_device to device_t */ +#define acpi_device _device + typedef char acpi_device_class[20]; struct acpi_bus_event { @@ -38,6 +41,8 @@ struct acpi_bus_event { }; #define acpi_dev_present(...) lkpi_acpi_dev_present(__VA_ARGS__) +#define acpi_dev_get_first_match_dev(...) \ + 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, @@ -50,5 +55,13 @@ int unregister_acpi_notifier(struct notifier_block *nb); uint32_t acpi_target_system_state(void); bool lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv); +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/barrier.h b/sys/compat/linuxkpi/common/include/asm/barrier.h index d09f9c6d2c97..39c5139cb322 100644 --- a/sys/compat/linuxkpi/common/include/asm/barrier.h +++ b/sys/compat/linuxkpi/common/include/asm/barrier.h @@ -58,4 +58,7 @@ #define smp_mb__before_atomic() barrier() #define smp_mb__after_atomic() barrier() +#define smp_store_release(p, v) do { smp_mb(); WRITE_ONCE(*p, v); } while (0) +#define smp_load_acquire(p) ({ typeof(*p) _v = READ_ONCE(*p); smp_mb(); _v; }) + #endif /* _LINUXKPI_ASM_BARRIER_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/acpi.h b/sys/compat/linuxkpi/common/include/linux/acpi.h index 610aa0784cb9..3e1ec1b20626 100644 --- a/sys/compat/linuxkpi/common/include/linux/acpi.h +++ b/sys/compat/linuxkpi/common/include/linux/acpi.h @@ -39,6 +39,10 @@ #define ACPI_HANDLE(dev) \ ((dev)->bsddev != NULL ? bsd_acpi_get_handle((dev)->bsddev) : NULL) +#define acpi_device_handle(dev) \ + ((dev) != NULL ? bsd_acpi_get_handle(dev) : NULL) +static inline void acpi_dev_put(struct acpi_device *adev) {} +#define acpi_handle_debug(handle, fmt, ...) #endif 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/aperture.h b/sys/compat/linuxkpi/common/include/linux/aperture.h index e0387ed0225d..7eced3cc3cb1 100644 --- a/sys/compat/linuxkpi/common/include/linux/aperture.h +++ b/sys/compat/linuxkpi/common/include/linux/aperture.h @@ -16,7 +16,9 @@ int devm_aperture_acquire_for_platform_device(struct platform_device *pdev, resource_size_t size); int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size, - bool primary, const char *name); + const char *name); + +int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev); int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *name); #else @@ -28,7 +30,12 @@ static inline int devm_aperture_acquire_for_platform_device(struct platform_devi } static inline int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size, - bool primary, const char *name) + const char *name) +{ + return 0; +} + +static inline int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev) { return 0; } @@ -41,7 +48,6 @@ static inline int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, /** * aperture_remove_all_conflicting_devices - remove all existing framebuffers - * @primary: also kick vga16fb if present; only relevant for VGA devices * @name: a descriptive name of the requesting driver * * This function removes all graphics device drivers. Use this function on systems @@ -50,9 +56,9 @@ static inline int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, * Returns: * 0 on success, or a negative errno code otherwise */ -static inline int aperture_remove_all_conflicting_devices(bool primary, const char *name) +static inline int aperture_remove_all_conflicting_devices(const char *name) { - return aperture_remove_conflicting_devices(0, (resource_size_t)-1, primary, name); + return aperture_remove_conflicting_devices(0, (resource_size_t)-1, name); } #endif diff --git a/sys/compat/linuxkpi/common/include/linux/bitfield.h b/sys/compat/linuxkpi/common/include/linux/bitfield.h index a2020d247489..8a91b0663f37 100644 --- a/sys/compat/linuxkpi/common/include/linux/bitfield.h +++ b/sys/compat/linuxkpi/common/include/linux/bitfield.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2020 The FreeBSD Foundation + * Copyright (c) 2020-2024 The FreeBSD Foundation * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -131,6 +131,10 @@ _uX_replace_bits(8) #define FIELD_PREP(_mask, _value) \ (((typeof(_mask))(_value) << __bf_shf(_mask)) & (_mask)) +/* Likely would need extra sanity checks compared to FIELD_PREP()? */ +#define FIELD_PREP_CONST(_mask, _value) \ + (((typeof(_mask))(_value) << __bf_shf(_mask)) & (_mask)) + #define FIELD_GET(_mask, _value) \ ((typeof(_mask))(((_value) & (_mask)) >> __bf_shf(_mask))) diff --git a/sys/compat/linuxkpi/common/include/linux/bitops.h b/sys/compat/linuxkpi/common/include/linux/bitops.h index 1415d5224084..bc776a0db9c4 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) (bitcount8((uint8_t)(x))) +#define HWEIGHT16(x) (bitcount16(x)) +#define HWEIGHT32(x) (bitcount32(x)) +#define HWEIGHT64(x) (bitcount64(x)) static inline int __ffs(int mask) @@ -288,6 +288,15 @@ find_next_zero_bit(const unsigned long *addr, unsigned long size, #define test_bit(i, a) \ !!(READ_ONCE(((volatile const unsigned long *)(a))[BIT_WORD(i)]) & BIT_MASK(i)) +static inline void +__assign_bit(long bit, volatile unsigned long *addr, bool value) +{ + if (value) + __set_bit(bit, addr); + else + __clear_bit(bit, addr); +} + static inline int test_and_clear_bit(long bit, volatile unsigned long *var) { diff --git a/sys/compat/linuxkpi/common/include/linux/cc_platform.h b/sys/compat/linuxkpi/common/include/linux/cc_platform.h index 01c0de17f7d2..1544c141614b 100644 --- a/sys/compat/linuxkpi/common/include/linux/cc_platform.h +++ b/sys/compat/linuxkpi/common/include/linux/cc_platform.h @@ -8,6 +8,7 @@ enum cc_attr { CC_ATTR_MEM_ENCRYPT, + CC_ATTR_GUEST_MEM_ENCRYPT, }; static inline bool diff --git a/sys/compat/linuxkpi/common/include/linux/cec.h b/sys/compat/linuxkpi/common/include/linux/cec.h new file mode 100644 index 000000000000..e0854d87d85c --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/cec.h @@ -0,0 +1,8 @@ +/* Public domain */ + +#ifndef _LINUXKPI_LINUX_CEC_H_ +#define _LINUXKPI_LINUX_CEC_H_ + +#define CEC_PHYS_ADDR_INVALID 0xffff + +#endif /* _LINUXKPI_LINUX_CEC_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/cgroup.h b/sys/compat/linuxkpi/common/include/linux/cgroup.h new file mode 100644 index 000000000000..a9dd22fd0f4c --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/cgroup.h @@ -0,0 +1,34 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * 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_CGROUP_H_ +#define _LINUXKPI_LINUX_CGROUP_H_ + +#include <linux/kernel_stat.h> + +#endif /* _LINUXKPI_LINUX_CGROUP_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/cleanup.h b/sys/compat/linuxkpi/common/include/linux/cleanup.h new file mode 100644 index 000000000000..01f234f0cbe7 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/cleanup.h @@ -0,0 +1,46 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 The FreeBSD Foundation + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + */ + +#ifndef _LINUXKPI_LINUX_CLEANUP_H +#define _LINUXKPI_LINUX_CLEANUP_H + +#define __cleanup(_f) __attribute__((__cleanup__(_f))) + +/* + * Note: "_T" are special as they are exposed into common code for + * statements. Extra care should be taken when changing the code. + */ +#define DEFINE_GUARD(_n, _dt, _lock, _unlock) \ + \ + typedef _dt guard_ ## _n ## _t; \ + \ + static inline _dt \ + guard_ ## _n ## _create( _dt _T) \ + { \ + _dt c; \ + \ + c = ({ _lock; _T; }); \ + return (c); \ + } \ + \ + static inline void \ + guard_ ## _n ## _destroy(_dt *t) \ + { \ + _dt _T; \ + \ + _T = *t; \ + if (_T) { _unlock; }; \ + } + +/* We need to keep these calls unique. */ +#define guard(_n) \ + guard_ ## _n ## _t guard_ ## _n ## _ ## __COUNTER__ \ + __cleanup(guard_ ## _n ## _destroy) = guard_ ## _n ## _create + +#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 d59e6faed12d..fb5ad3bf4fe4 100644 --- a/sys/compat/linuxkpi/common/include/linux/compiler.h +++ b/sys/compat/linuxkpi/common/include/linux/compiler.h @@ -48,7 +48,9 @@ #define __cond_lock(x,c) (c) #define __bitwise #define __devinitdata +#ifndef __deprecated #define __deprecated +#endif #define __init #define __initconst #define __devinit @@ -89,6 +91,10 @@ #define __printf(a,b) __printflike(a,b) +#define __diag_push() +#define __diag_pop() +#define __diag_ignore_all(...) + #define barrier() __asm__ __volatile__("": : :"memory") #define lower_32_bits(n) ((u32)(n)) 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/cpufeature.h b/sys/compat/linuxkpi/common/include/linux/cpufeature.h new file mode 100644 index 000000000000..746d1a7164a8 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/cpufeature.h @@ -0,0 +1,43 @@ +/*- + * 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_CPUFEATURE_H_ +#define _LINUXKPI_LINUX_CPUFEATURE_H_ + +/* + * Linux includes the following header. We don't have it on FreeBSD yet, so + * let's comment this include for now. It is still referenced here because + * sometimes, consumers of headers rely voluntarily or not on the namespace + * pollution. + */ +/* #include <linux/init.h> */ +#include <linux/mod_devicetable.h> +#include <asm/cpufeature.h> + +#endif /* _LINUXKPI_LINUX_CPUFEATURE_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/debugfs.h b/sys/compat/linuxkpi/common/include/linux/debugfs.h index 470b50cb730c..4d146e085a7b 100644 --- a/sys/compat/linuxkpi/common/include/linux/debugfs.h +++ b/sys/compat/linuxkpi/common/include/linux/debugfs.h @@ -82,12 +82,16 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent); struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, const char *dest); +struct dentry *debugfs_lookup(const char *name, struct dentry *parent); + void debugfs_remove(struct dentry *dentry); void debugfs_remove_recursive(struct dentry *dentry); #define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) +#define DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(__fops, __get, __set, __fmt) \ + DEFINE_SIMPLE_ATTRIBUTE_SIGNED(__fops, __get, __set, __fmt) void debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent, bool *value); @@ -111,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 668fe9cb8650..2556b0c45e49 100644 --- a/sys/compat/linuxkpi/common/include/linux/device.h +++ b/sys/compat/linuxkpi/common/include/linux/device.h @@ -57,7 +57,6 @@ struct device; struct class { const char *name; - struct module *owner; struct kobject kobj; devclass_t bsdclass; const struct dev_pm_ops *pm; @@ -91,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 { @@ -331,6 +332,13 @@ dev_name(const struct device *dev) return kobject_name(&dev->kobj); } +static inline bool +dev_is_removable(struct device *dev) +{ + + return (false); +} + #define dev_set_name(_dev, _fmt, ...) \ kobject_set_name(&(_dev)->kobj, (_fmt), ##__VA_ARGS__) @@ -342,7 +350,12 @@ put_device(struct device *dev) kobject_put(&dev->kobj); } -struct class *class_create(struct module *owner, const char *name); +struct class *lkpi_class_create(const char *name); +#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60400 +#define class_create(name) lkpi_class_create(name) +#else +#define class_create(owner, name) lkpi_class_create(name) +#endif static inline int class_register(struct class *class) @@ -698,4 +711,8 @@ int lkpi_devm_add_action_or_reset(struct device *dev, void (*action)(void *), vo #define devm_add_action_or_reset(dev, action, data) \ lkpi_devm_add_action_or_reset(dev, action, data) +int lkpi_devm_device_add_group(struct device *dev, const struct attribute_group *group); +#define devm_device_add_group(dev, group) \ + lkpi_devm_device_add_group(dev, group) + #endif /* _LINUXKPI_LINUX_DEVICE_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/device/driver.h b/sys/compat/linuxkpi/common/include/linux/device/driver.h new file mode 100644 index 000000000000..03b510c9c8b7 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/device/driver.h @@ -0,0 +1,33 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 Bjoern A. Zeeb + * Copyright (c) 2024 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + */ + +#ifndef LINUXKPI_LINUX_DEVICE_DRIVER_H +#define LINUXKPI_LINUX_DEVICE_DRIVER_H + +#include <sys/cdefs.h> +#include <linux/module.h> + +#define module_driver(_drv, _regf, _unregf) \ +static inline int \ +__CONCAT(__CONCAT(_, _drv), _init)(void) \ +{ \ + return (_regf(&(_drv))); \ +} \ + \ +static inline void \ +__CONCAT(__CONCAT(_, _drv), _exit)(void) \ +{ \ + _unregf(&(_drv)); \ +} \ + \ +module_init(__CONCAT(__CONCAT(_, _drv), _init)); \ +module_exit(__CONCAT(__CONCAT(_, _drv), _exit)) + +#endif /* LINUXKPI_LINUX_DEVICE_DRIVER_H */ 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/efi.h b/sys/compat/linuxkpi/common/include/linux/efi.h index a485b4b1fd94..aa33371bd0e8 100644 --- a/sys/compat/linuxkpi/common/include/linux/efi.h +++ b/sys/compat/linuxkpi/common/include/linux/efi.h @@ -41,9 +41,6 @@ static inline bool __efi_enabled(int feature) { -#if defined(MODINFOMD_EFI_MAP) && !defined(__amd64__) - caddr_t kmdp; -#endif bool enabled = false; switch (feature) { @@ -52,10 +49,7 @@ __efi_enabled(int feature) /* Use cached value on amd64 */ enabled = efi_boot; #elif defined(MODINFOMD_EFI_MAP) - kmdp = preload_search_by_type("elf kernel"); - if (kmdp == NULL) - kmdp = preload_search_by_type("elf64 kernel"); - enabled = preload_search_info(kmdp, + enabled = preload_search_info(preload_kmdp, MODINFO_METADATA | MODINFOMD_EFI_MAP) != NULL; #endif break; diff --git a/sys/compat/linuxkpi/common/include/linux/errno.h b/sys/compat/linuxkpi/common/include/linux/errno.h index ea258587c6f7..d634675d43d0 100644 --- a/sys/compat/linuxkpi/common/include/linux/errno.h +++ b/sys/compat/linuxkpi/common/include/linux/errno.h @@ -68,5 +68,6 @@ #define ENOMEDIUM 532 #define ENOSR 533 #define ELNRNG 534 +#define ENAVAIL 535 #endif /* _LINUXKPI_LINUX_ERRNO_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/etherdevice.h b/sys/compat/linuxkpi/common/include/linux/etherdevice.h index 89cd4c8e0ba0..1f2d6cf22d7e 100644 --- a/sys/compat/linuxkpi/common/include/linux/etherdevice.h +++ b/sys/compat/linuxkpi/common/include/linux/etherdevice.h @@ -53,19 +53,27 @@ struct ethtool_modinfo { static inline bool is_zero_ether_addr(const u8 * addr) { - return ((addr[0] + addr[1] + addr[2] + addr[3] + addr[4] + addr[5]) == 0x00); + return ((addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]) == + 0x00); +} + +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 is_broadcast_ether_addr(const u8 * addr) { - return ((addr[0] + addr[1] + addr[2] + addr[3] + addr[4] + addr[5]) == (6 * 0xff)); + return ((addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == + 0xff); } static inline bool diff --git a/sys/compat/linuxkpi/common/include/linux/file.h b/sys/compat/linuxkpi/common/include/linux/file.h index f94e3d89ced1..f6e988c2d88e 100644 --- a/sys/compat/linuxkpi/common/include/linux/file.h +++ b/sys/compat/linuxkpi/common/include/linux/file.h @@ -43,7 +43,7 @@ struct linux_file; #undef file -extern struct fileops linuxfileops; +extern const struct fileops linuxfileops; static inline struct linux_file * linux_fget(unsigned int fd) diff --git a/sys/compat/linuxkpi/common/include/linux/fs.h b/sys/compat/linuxkpi/common/include/linux/fs.h index a28d5bf97121..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); @@ -264,12 +269,18 @@ get_file(struct linux_file *f) return (f); } +struct linux_file * linux_get_file_rcu(struct linux_file **f); +struct linux_file * get_file_active(struct linux_file **f); +#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION < 60700 static inline bool get_file_rcu(struct linux_file *f) { return (refcount_acquire_if_not_zero( f->_file == NULL ? &f->f_count : &f->_file->f_count)); } +#else +#define get_file_rcu(f) linux_get_file_rcu(f) +#endif static inline struct inode * igrab(struct inode *inode) @@ -353,9 +364,8 @@ static inline ssize_t simple_read_from_buffer(void __user *dest, size_t read_size, loff_t *ppos, void *orig, size_t buf_size) { - void *read_pos = ((char *) orig) + *ppos; + void *p, *read_pos = ((char *) orig) + *ppos; size_t buf_remain = buf_size - *ppos; - ssize_t num_read; if (buf_remain < 0 || buf_remain > buf_size) return -EINVAL; @@ -363,18 +373,23 @@ simple_read_from_buffer(void __user *dest, size_t read_size, loff_t *ppos, if (read_size > buf_remain) read_size = buf_remain; - /* copy_to_user returns number of bytes NOT read */ - num_read = read_size - copy_to_user(dest, read_pos, read_size); - if (num_read == 0) - return -EFAULT; - *ppos += num_read; - - return (num_read); + /* + * XXX At time of commit only debugfs consumers could be + * identified. If others will use this function we may + * have to revise this: normally we would call copy_to_user() + * here but lindebugfs will return the result and the + * copyout is done elsewhere for us. + */ + p = memcpy(dest, read_pos, read_size); + if (p != NULL) + *ppos += read_size; + + return (read_size); } MALLOC_DECLARE(M_LSATTR); -#define DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \ +#define __DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt, __wrfunc)\ static inline int \ __fops ## _open(struct inode *inode, struct file *filp) \ { \ @@ -385,10 +400,15 @@ static const struct file_operations __fops = { \ .open = __fops ## _open, \ .release = simple_attr_release, \ .read = simple_attr_read, \ - .write = simple_attr_write, \ + .write = __wrfunc, \ .llseek = no_llseek \ } +#define DEFINE_SIMPLE_ATTRIBUTE(fops, get, set, fmt) \ + __DEFINE_SIMPLE_ATTRIBUTE(fops, get, set, fmt, simple_attr_write) +#define DEFINE_SIMPLE_ATTRIBUTE_SIGNED(fops, get, set, fmt) \ + __DEFINE_SIMPLE_ATTRIBUTE(fops, get, set, fmt, simple_attr_write_signed) + int simple_attr_open(struct inode *inode, struct file *filp, int (*get)(void *, uint64_t *), int (*set)(void *, uint64_t), const char *fmt); @@ -399,4 +419,7 @@ ssize_t simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t ssize_t simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos); +ssize_t simple_attr_write_signed(struct file *filp, const char *buf, + size_t write_size, loff_t *ppos); + #endif /* _LINUXKPI_LINUX_FS_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/gfp.h b/sys/compat/linuxkpi/common/include/linux/gfp.h index f6cce379924d..4c4caa621789 100644 --- a/sys/compat/linuxkpi/common/include/linux/gfp.h +++ b/sys/compat/linuxkpi/common/include/linux/gfp.h @@ -43,7 +43,6 @@ #define __GFP_NOWARN 0 #define __GFP_HIGHMEM 0 #define __GFP_ZERO M_ZERO -#define __GFP_NORETRY 0 #define __GFP_NOMEMALLOC 0 #define __GFP_RECLAIM 0 #define __GFP_RECLAIMABLE 0 @@ -57,7 +56,8 @@ #define __GFP_KSWAPD_RECLAIM 0 #define __GFP_WAIT M_WAITOK #define __GFP_DMA32 (1U << 24) /* LinuxKPI only */ -#define __GFP_BITS_SHIFT 25 +#define __GFP_NORETRY (1U << 25) /* LinuxKPI only */ +#define __GFP_BITS_SHIFT 26 #define __GFP_BITS_MASK ((1 << __GFP_BITS_SHIFT) - 1) #define __GFP_NOFAIL M_WAITOK @@ -85,19 +85,10 @@ struct page_frag_cache { }; /* - * Resolve a page into a virtual address: - * - * NOTE: This function only works for pages allocated by the kernel. - */ -extern void *linux_page_address(struct page *); - -#define page_address(page) linux_page_address(page) - -/* * Page management for unmapped pages: */ -extern struct page *linux_alloc_pages(gfp_t flags, unsigned int order); -extern void linux_free_pages(struct page *page, unsigned int order); +struct page *linux_alloc_pages(gfp_t flags, unsigned int order); +void linux_free_pages(struct page *page, unsigned int order); void *linuxkpi_page_frag_alloc(struct page_frag_cache *, size_t, gfp_t); void linuxkpi_page_frag_free(void *); void linuxkpi__page_frag_cache_drain(struct page *, size_t); @@ -143,11 +134,13 @@ 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: */ -extern vm_offset_t linux_alloc_kmem(gfp_t flags, unsigned int order); -extern void linux_free_kmem(vm_offset_t, unsigned int order); +vm_offset_t linux_alloc_kmem(gfp_t flags, unsigned int order); +void linux_free_kmem(vm_offset_t, unsigned int order); static inline vm_offset_t get_zeroed_page(gfp_t flags) diff --git a/sys/compat/linuxkpi/common/include/linux/gpf.h b/sys/compat/linuxkpi/common/include/linux/gpf.h new file mode 100644 index 000000000000..01e883a94728 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/gpf.h @@ -0,0 +1,33 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Serenity Cyber Security, LLC. + * + * 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_GPF_H_ +#define _LINUXKPI_LINUX_GPF_H_ + +#include <linux/mmzone.h> + +#endif /* _LINUXKPI_LINUX_GPF_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/hdmi.h b/sys/compat/linuxkpi/common/include/linux/hdmi.h index c8ec982ff498..e07578167d69 100644 --- a/sys/compat/linuxkpi/common/include/linux/hdmi.h +++ b/sys/compat/linuxkpi/common/include/linux/hdmi.h @@ -170,19 +170,19 @@ struct hdmi_avi_infoframe { enum hdmi_infoframe_type type; unsigned char version; unsigned char length; + bool itc; + unsigned char pixel_repeat; enum hdmi_colorspace colorspace; enum hdmi_scan_mode scan_mode; enum hdmi_colorimetry colorimetry; enum hdmi_picture_aspect picture_aspect; enum hdmi_active_aspect active_aspect; - bool itc; enum hdmi_extended_colorimetry extended_colorimetry; enum hdmi_quantization_range quantization_range; enum hdmi_nups nups; unsigned char video_code; enum hdmi_ycc_quantization_range ycc_quantization_range; enum hdmi_content_type content_type; - unsigned char pixel_repeat; unsigned short top_bar; unsigned short bottom_bar; unsigned short left_bar; @@ -336,7 +336,14 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, void *buffer, size_t size); ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame, void *buffer, size_t size); -int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame); +int hdmi_audio_infoframe_check(const struct hdmi_audio_infoframe *frame); + +#ifdef __linux__ +struct dp_sdp; +ssize_t +hdmi_audio_infoframe_pack_for_dp(const struct hdmi_audio_infoframe *frame, + struct dp_sdp *sdp, u8 dp_version); +#endif enum hdmi_3d_structure { HDMI_3D_STRUCTURE_INVALID = -1, diff --git a/sys/compat/linuxkpi/common/include/linux/highmem.h b/sys/compat/linuxkpi/common/include/linux/highmem.h index f770bef6b3b7..58a9cdcdf60f 100644 --- a/sys/compat/linuxkpi/common/include/linux/highmem.h +++ b/sys/compat/linuxkpi/common/include/linux/highmem.h @@ -43,6 +43,7 @@ #include <vm/vm_page.h> #include <vm/pmap.h> +#include <linux/mm.h> #include <linux/page.h> #define PageHighMem(p) (0) @@ -78,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)); @@ -94,6 +93,12 @@ kmap_atomic(struct page *page) } static inline void * +kmap_local_page(struct page *page) +{ + return (kmap(page)); +} + +static inline void * kmap_local_page_prot(struct page *page, pgprot_t prot) { @@ -132,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 ca3f8171ff44..535d8ce07fb4 100644 --- a/sys/compat/linuxkpi/common/include/linux/idr.h +++ b/sys/compat/linuxkpi/common/include/linux/idr.h @@ -34,6 +34,8 @@ #include <sys/limits.h> #include <sys/mutex.h> +#include <linux/radix-tree.h> +#include <linux/gpf.h> #include <linux/types.h> #define IDR_BITS 5 diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h index 2000e7480ff8..3644ef80861b 100644 --- a/sys/compat/linuxkpi/common/include/linux/ieee80211.h +++ b/sys/compat/linuxkpi/common/include/linux/ieee80211.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2020-2023 The FreeBSD Foundation + * Copyright (c) 2020-2025 The FreeBSD Foundation * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -33,9 +33,18 @@ #include <net80211/ieee80211.h> #include <asm/unaligned.h> +#include <linux/kernel.h> #include <linux/bitops.h> #include <linux/if_ether.h> +/* linux_80211.c */ +extern int linuxkpi_debug_80211; +#ifndef D80211_TODO +#define D80211_TODO 0x1 +#endif +#define TODO(fmt, ...) if (linuxkpi_debug_80211 & D80211_TODO) \ + printf("%s:%d: XXX LKPI80211 TODO " fmt "\n", __func__, __LINE__, ##__VA_ARGS__) + /* 9.4.2.55 Management MIC element (CMAC-256, GMAC-128, and GMAC-256). */ struct ieee80211_mmie_16 { @@ -55,6 +64,7 @@ struct ieee80211_mmie_16 { #define IEEE80211_GCMP_MIC_LEN 16 #define IEEE80211_GCMP_PN_LEN 6 #define IEEE80211_GMAC_PN_LEN 6 +#define IEEE80211_CMAC_PN_LEN 6 #define IEEE80211_MAX_PN_LEN 16 @@ -91,7 +101,9 @@ struct ieee80211_mmie_16 { #define IEEE80211_QOS_CTL_ACK_POLICY_NOACK 0x0020 #define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100 -#define IEEE80211_RATE_SHORT_PREAMBLE BIT(0) +enum ieee80211_rate_flags { + IEEE80211_RATE_SHORT_PREAMBLE = BIT(0), +}; enum ieee80211_rate_control_changed_flags { IEEE80211_RC_BW_CHANGED = BIT(0), @@ -106,7 +118,8 @@ enum ieee80211_rate_control_changed_flags { #define IEEE80211_TKIP_ICV_LEN 4 #define IEEE80211_TKIP_IV_LEN 8 /* WEP + KID + EXT */ -#define IEEE80211_VHT_EXT_NSS_BW_CAPABLE (1 << 13) /* assigned to tx_highest */ +/* 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 */ @@ -125,19 +138,23 @@ enum wlan_ht_cap_sm_ps { WLAN_HT_CAP_SM_PS_DISABLED, }; -#define WLAN_MAX_KEY_LEN 32 /* TODO FIXME brcmfmac */ -#define WLAN_PMKID_LEN 16 /* TODO FIXME brcmfmac */ - -#define WLAN_KEY_LEN_WEP40 5 -#define WLAN_KEY_LEN_WEP104 13 -#define WLAN_KEY_LEN_TKIP 32 -#define WLAN_KEY_LEN_CCMP 16 -#define WLAN_KEY_LEN_GCMP 16 -#define WLAN_KEY_LEN_AES_CMAC 16 -#define WLAN_KEY_LEN_GCMP_256 32 -#define WLAN_KEY_LEN_BIP_CMAC_256 32 -#define WLAN_KEY_LEN_BIP_GMAC_128 16 -#define WLAN_KEY_LEN_BIP_GMAC_256 32 +#define WLAN_MAX_KEY_LEN 32 +#define WLAN_PMKID_LEN 16 +#define WLAN_PMK_LEN_SUITE_B_192 48 + +enum ieee80211_key_len { + WLAN_KEY_LEN_WEP40 = 5, + WLAN_KEY_LEN_WEP104 = 13, + WLAN_KEY_LEN_TKIP = 32, + WLAN_KEY_LEN_CCMP = 16, + WLAN_KEY_LEN_CCMP_256 = 32, + WLAN_KEY_LEN_GCMP = 16, + WLAN_KEY_LEN_AES_CMAC = 16, + WLAN_KEY_LEN_GCMP_256 = 32, + WLAN_KEY_LEN_BIP_CMAC_256 = 32, + WLAN_KEY_LEN_BIP_GMAC_128 = 16, + WLAN_KEY_LEN_BIP_GMAC_256 = 32, +}; /* 802.11-2020, 9.4.2.55.3, Table 9-185 Subfields of the A-MPDU Parameters field */ enum ieee80211_min_mpdu_start_spacing { @@ -178,6 +195,7 @@ enum ieee80211_min_mpdu_start_spacing { #define IEEE80211_STYPE_CTS IEEE80211_FC0_SUBTYPE_CTS #define IEEE80211_STYPE_RTS IEEE80211_FC0_SUBTYPE_RTS #define IEEE80211_STYPE_ACTION IEEE80211_FC0_SUBTYPE_ACTION +#define IEEE80211_STYPE_DATA IEEE80211_FC0_SUBTYPE_DATA #define IEEE80211_STYPE_QOS_DATA IEEE80211_FC0_SUBTYPE_QOS_DATA #define IEEE80211_STYPE_QOS_NULLFUNC IEEE80211_FC0_SUBTYPE_QOS_NULL #define IEEE80211_STYPE_QOS_CFACK 0xd0 /* XXX-BZ reserved? */ @@ -254,20 +272,20 @@ enum ieee80211_ac_numbers { #define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0xf -/* XXX net80211 calls these IEEE80211_HTCAP_* */ -#define IEEE80211_HT_CAP_LDPC_CODING 0x0001 /* IEEE80211_HTCAP_LDPC */ -#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 /* IEEE80211_HTCAP_CHWIDTH40 */ -#define IEEE80211_HT_CAP_SM_PS 0x000c /* IEEE80211_HTCAP_SMPS */ +/* Define the LinuxKPI names directly to the net80211 ones. */ +#define IEEE80211_HT_CAP_LDPC_CODING IEEE80211_HTCAP_LDPC +#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 IEEE80211_HTCAP_CHWIDTH40 +#define IEEE80211_HT_CAP_SM_PS IEEE80211_HTCAP_SMPS #define IEEE80211_HT_CAP_SM_PS_SHIFT 2 -#define IEEE80211_HT_CAP_GRN_FLD 0x0010 /* IEEE80211_HTCAP_GREENFIELD */ -#define IEEE80211_HT_CAP_SGI_20 0x0020 /* IEEE80211_HTCAP_SHORTGI20 */ -#define IEEE80211_HT_CAP_SGI_40 0x0040 /* IEEE80211_HTCAP_SHORTGI40 */ -#define IEEE80211_HT_CAP_TX_STBC 0x0080 /* IEEE80211_HTCAP_TXSTBC */ -#define IEEE80211_HT_CAP_RX_STBC 0x0100 /* IEEE80211_HTCAP_RXSTBC */ -#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8 /* IEEE80211_HTCAP_RXSTBC_S */ -#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 /* IEEE80211_HTCAP_MAXAMSDU */ -#define IEEE80211_HT_CAP_DSSSCCK40 0x1000 /* IEEE80211_HTCAP_DSSSCCK40 */ -#define IEEE80211_HT_CAP_LSIG_TXOP_PROT 0x8000 /* IEEE80211_HTCAP_LSIGTXOPPROT */ +#define IEEE80211_HT_CAP_GRN_FLD IEEE80211_HTCAP_GREENFIELD +#define IEEE80211_HT_CAP_SGI_20 IEEE80211_HTCAP_SHORTGI20 +#define IEEE80211_HT_CAP_SGI_40 IEEE80211_HTCAP_SHORTGI40 +#define IEEE80211_HT_CAP_TX_STBC IEEE80211_HTCAP_TXSTBC +#define IEEE80211_HT_CAP_RX_STBC IEEE80211_HTCAP_RXSTBC +#define IEEE80211_HT_CAP_RX_STBC_SHIFT IEEE80211_HTCAP_RXSTBC_S +#define IEEE80211_HT_CAP_MAX_AMSDU IEEE80211_HTCAP_MAXAMSDU +#define IEEE80211_HT_CAP_DSSSCCK40 IEEE80211_HTCAP_DSSSCCK40 +#define IEEE80211_HT_CAP_LSIG_TXOP_PROT IEEE80211_HTCAP_LSIGTXOPPROT #define IEEE80211_HT_MCS_TX_DEFINED 0x0001 #define IEEE80211_HT_MCS_TX_RX_DIFF 0x0002 @@ -277,13 +295,16 @@ 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 struct ieee80211_mcs_info { uint8_t rx_mask[IEEE80211_HT_MCS_MASK_LEN]; 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 { @@ -293,7 +314,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 @@ -328,6 +349,8 @@ enum ieee80211_chanctx_change_flags { IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(2), 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 { @@ -371,14 +394,6 @@ enum ieee80211_sta_state { IEEE80211_STA_AUTHORIZED = 4, /* 802.1x */ }; -enum ieee80211_sta_rx_bw { - IEEE80211_STA_RX_BW_20, - 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), @@ -402,7 +417,7 @@ enum ieee80211_tx_info_flags { IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(18), IEEE80211_TX_CTL_LDPC = BIT(19), IEEE80211_TX_CTL_STBC = BIT(20), -}; +} __packed; enum ieee80211_tx_status_flags { IEEE80211_TX_STATUS_ACK_SIGNAL_VALID = BIT(0), @@ -413,6 +428,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. */ }; @@ -469,9 +485,14 @@ enum ieee80211_back { WLAN_ACTION_ADDBA_REQ = 0, }; +enum ieee80211_sa_query { + WLAN_ACTION_SA_QUERY_RESPONSE = 1, +}; + /* 802.11-2020, Table 9-51-Category values */ enum ieee80211_category { WLAN_CATEGORY_BACK = 3, + WLAN_CATEGORY_SA_QUERY = 8, /* net80211::IEEE80211_ACTION_CAT_SA_QUERY */ }; /* 80211-2020 9.3.3.2 Format of Management frames */ @@ -490,6 +511,12 @@ struct ieee80211_mgmt { uint16_t capab_info; uint8_t variable[0]; } beacon; + /* 9.3.3.5 Association Request frame format */ + struct { + uint16_t capab_info; + uint16_t listen_interval; + uint8_t variable[0]; + } assoc_req; /* 9.3.3.10 Probe Request frame format */ struct { uint8_t variable[0]; @@ -543,6 +570,7 @@ struct ieee80211_mgmt { } wnm_timing_msr; } u; } action; + DECLARE_FLEX_ARRAY(uint8_t, body); } u; }; @@ -567,6 +595,8 @@ struct ieee80211_rts { /* net80211::ieee80211_frame_rts */ #define IEEE80211_SEQ_TO_SN(_seqn) (((_seqn) & IEEE80211_SEQ_SEQ_MASK) >> \ IEEE80211_SEQ_SEQ_SHIFT) +#define IEEE80211_SN_TO_SEQ(_sn) (((_sn) << IEEE80211_SEQ_SEQ_SHIFT) & \ + IEEE80211_SEQ_SEQ_MASK) /* Time unit (TU) to .. See net80211: IEEE80211_DUR_TU */ #define TU_TO_JIFFIES(_tu) (usecs_to_jiffies(_tu) * 1024) @@ -584,6 +614,7 @@ enum ieee80211_eid { WLAN_EID_TIM = 5, WLAN_EID_COUNTRY = 7, /* IEEE80211_ELEMID_COUNTRY */ WLAN_EID_REQUEST = 10, + WLAN_EID_QBSS_LOAD = 11, /* IEEE80211_ELEMID_BSSLOAD */ WLAN_EID_CHANNEL_SWITCH = 37, WLAN_EID_MEASURE_REPORT = 39, WLAN_EID_HT_CAPABILITY = 45, /* IEEE80211_ELEMID_HTCAP */ @@ -595,6 +626,7 @@ enum ieee80211_eid { WLAN_EID_MULTI_BSSID_IDX = 85, WLAN_EID_EXT_CAPABILITY = 127, WLAN_EID_VHT_CAPABILITY = 191, /* IEEE80211_ELEMID_VHT_CAP */ + WLAN_EID_S1G_TWT = 216, WLAN_EID_VENDOR_SPECIFIC = 221, /* IEEE80211_ELEMID_VENDOR */ }; @@ -640,10 +672,10 @@ struct ieee80211_trigger { /* Table 9-29c-Trigger Type subfield encoding */ enum { IEEE80211_TRIGGER_TYPE_BASIC = 0x0, + IEEE80211_TRIGGER_TYPE_MU_BAR = 0x2, #if 0 /* Not seen yet. */ BFRP = 0x1, - MU-BAR = 0x2, MU-RTS = 0x3, BSRP = 0x4, GCR MU-BAR = 0x5, @@ -654,6 +686,12 @@ enum { IEEE80211_TRIGGER_TYPE_MASK = 0xf }; +#define IEEE80211_TRIGGER_ULBW_MASK 0xc0000 +#define IEEE80211_TRIGGER_ULBW_20MHZ 0x0 +#define IEEE80211_TRIGGER_ULBW_40MHZ 0x1 +#define IEEE80211_TRIGGER_ULBW_80MHZ 0x2 +#define IEEE80211_TRIGGER_ULBW_160_80P80MHZ 0x3 + /* 802.11-2020, Figure 9-687-Control field format; 802.11ax-2021 */ #define IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST BIT(3) #define IEEE80211_TWT_CONTROL_RX_DISABLED BIT(4) @@ -694,12 +732,74 @@ struct ieee80211_bssid_index { int bssid_index; }; -enum ieee80211_reg_ap_power { +enum ieee80211_ap_reg_power { + IEEE80211_REG_UNSET_AP, IEEE80211_REG_LPI_AP, IEEE80211_REG_SP_AP, IEEE80211_REG_VLP_AP, }; +/* + * 802.11ax-2021, Table 9-277-Meaning of Maximum Transmit Power Count subfield + * if Maximum Transmit Power Interpretation subfield is 1 or 3 + */ +#define IEEE80211_MAX_NUM_PWR_LEVEL 8 + +/* + * 802.11ax-2021, Table 9-275a-Maximum Transmit Power Interpretation subfield + * encoding (4) * Table E-12-Regulatory Info subfield encoding in the + * United States (2) + */ +#define IEEE80211_TPE_MAX_IE_NUM 8 + +/* 802.11ax-2021, 9.4.2.161 Transmit Power Envelope element */ +struct ieee80211_tx_pwr_env { + uint8_t tx_power_info; + uint8_t tx_power[IEEE80211_MAX_NUM_PWR_LEVEL]; +}; + +/* 802.11ax-2021, Figure 9-617-Transmit Power Information field format */ +/* These are field masks (3bit/3bit/2bit). */ +#define IEEE80211_TX_PWR_ENV_INFO_COUNT 0x07 +#define IEEE80211_TX_PWR_ENV_INFO_INTERPRET 0x38 +#define IEEE80211_TX_PWR_ENV_INFO_CATEGORY 0xc0 + +/* + * 802.11ax-2021, Table 9-275a-Maximum Transmit Power Interpretation subfield + * encoding + */ +enum ieee80211_tx_pwr_interpretation_subfield_enc { + IEEE80211_TPE_LOCAL_EIRP, + IEEE80211_TPE_LOCAL_EIRP_PSD, + IEEE80211_TPE_REG_CLIENT_EIRP, + IEEE80211_TPE_REG_CLIENT_EIRP_PSD, +}; + +enum ieee80211_tx_pwr_category_6ghz { + IEEE80211_TPE_CAT_6GHZ_DEFAULT, +}; + +/* 802.11-2020, 9.4.2.27 BSS Load element */ +struct ieee80211_bss_load_elem { + uint16_t sta_count; + uint8_t channel_util; + 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) @@ -800,4 +900,346 @@ ieee80211_is_trigger(__le16 fc) return (fc == v); } +static __inline bool +ieee80211_is_action(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_ACTION | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_probe_resp(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_RESP | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_auth(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_AUTH | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_assoc_req(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_REQ | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_assoc_resp(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_RESP | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_reassoc_req(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_REQ | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_reassoc_resp(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_RESP | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_disassoc(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_DISASSOC | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_data_present(__le16 fc) +{ + __le16 v; + + /* If it is a data frame and NODATA is not present. */ + fc &= htole16(IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_NODATA); + v = htole16(IEEE80211_FC0_TYPE_DATA); + + return (fc == v); +} + +static __inline bool +ieee80211_is_deauth(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_DEAUTH | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_beacon(__le16 fc) +{ + __le16 v; + + /* + * For as much as I get it this comes in LE and unlike FreeBSD + * where we get the entire frame header and u8[], here we get the + * 9.2.4.1 Frame Control field only. Mask and compare. + */ + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_BEACON | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + + +static __inline bool +ieee80211_is_probe_req(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_REQ | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_has_protected(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_PROTECTED << 8)); +} + +static __inline bool +ieee80211_is_back_req(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_BAR | IEEE80211_FC0_TYPE_CTL); + + return (fc == v); +} + +static __inline bool +ieee80211_is_bufferable_mmpdu(struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt; + __le16 fc; + + mgmt = (struct ieee80211_mgmt *)skb->data; + fc = mgmt->frame_control; + + /* 11.2.2 Bufferable MMPDUs, 80211-2020. */ + /* XXX we do not care about IBSS yet. */ + + if (!ieee80211_is_mgmt(fc)) + return (false); + if (ieee80211_is_action(fc)) /* XXX FTM? */ + return (true); /* XXX false? */ + if (ieee80211_is_disassoc(fc)) + return (true); + if (ieee80211_is_deauth(fc)) + return (true); + + TODO(); + + return (false); +} + +static __inline bool +ieee80211_is_nullfunc(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA); + + return (fc == v); +} + +static __inline bool +ieee80211_is_qos_nullfunc(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_QOS_NULL | IEEE80211_FC0_TYPE_DATA); + + return (fc == v); +} + +static __inline bool +ieee80211_is_any_nullfunc(__le16 fc) +{ + + return (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)); +} + +static inline bool +ieee80211_is_pspoll(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_PS_POLL | IEEE80211_FC0_TYPE_CTL); + + return (fc == v); +} + +static __inline bool +ieee80211_has_a4(__le16 fc) +{ + __le16 v; + + fc &= htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8); + v = htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8); + + return (fc == v); +} + +static __inline bool +ieee80211_has_order(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_ORDER << 8)); +} + +static __inline bool +ieee80211_has_retry(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_RETRY << 8)); +} + + +static __inline bool +ieee80211_has_fromds(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_DIR_FROMDS << 8)); +} + +static __inline bool +ieee80211_has_tods(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_DIR_TODS << 8)); +} + +static __inline uint8_t * +ieee80211_get_SA(struct ieee80211_hdr *hdr) +{ + + if (ieee80211_has_a4(hdr->frame_control)) + return (hdr->addr4); + if (ieee80211_has_fromds(hdr->frame_control)) + return (hdr->addr3); + return (hdr->addr2); +} + +static __inline uint8_t * +ieee80211_get_DA(struct ieee80211_hdr *hdr) +{ + + if (ieee80211_has_tods(hdr->frame_control)) + return (hdr->addr3); + return (hdr->addr1); +} + +static __inline bool +ieee80211_is_frag(struct ieee80211_hdr *hdr) +{ + TODO(); + return (false); +} + +static __inline bool +ieee80211_is_first_frag(__le16 fc) +{ + TODO(); + return (false); +} + +static __inline bool +ieee80211_is_robust_mgmt_frame(struct sk_buff *skb) +{ + TODO(); + return (false); +} + +static __inline bool +ieee80211_is_ftm(struct sk_buff *skb) +{ + TODO(); + return (false); +} + +static __inline bool +ieee80211_is_timing_measurement(struct sk_buff *skb) +{ + TODO(); + return (false); +} + +static __inline bool +ieee80211_has_pm(__le16 fc) +{ + TODO(); + return (false); +} + +static __inline bool +ieee80211_has_morefrags(__le16 fc) +{ + + fc &= htole16(IEEE80211_FC1_MORE_FRAG << 8); + return (fc != 0); +} + +static __inline u8 * +ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return (u8 *)hdr + 30; + else + return (u8 *)hdr + 24; +} + #endif /* _LINUXKPI_LINUX_IEEE80211_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/if_ether.h b/sys/compat/linuxkpi/common/include/linux/if_ether.h index 3735ad2f5527..6676e8fc142f 100644 --- a/sys/compat/linuxkpi/common/include/linux/if_ether.h +++ b/sys/compat/linuxkpi/common/include/linux/if_ether.h @@ -34,6 +34,7 @@ #define _LINUXKPI_LINUX_IF_ETHER_H_ #include <linux/types.h> +#include <linux/skbuff.h> #include <net/ethernet.h> @@ -69,4 +70,13 @@ struct ethhdr { uint16_t h_proto; } __packed; +static inline struct ethhdr * +eth_hdr(const struct sk_buff *skb) +{ + struct ethhdr *hdr; + + hdr = (struct ethhdr *)skb_mac_header(skb); + return (hdr); +} + #endif /* _LINUXKPI_LINUX_IF_ETHER_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/interrupt.h b/sys/compat/linuxkpi/common/include/linux/interrupt.h index d5f9a0ae7a47..dfd9816da8be 100644 --- a/sys/compat/linuxkpi/common/include/linux/interrupt.h +++ b/sys/compat/linuxkpi/common/include/linux/interrupt.h @@ -133,7 +133,7 @@ irq_set_affinity_hint(int vector, const cpumask_t *mask) int error; if (mask != NULL) - error = intr_setaffinity(vector, CPU_WHICH_IRQ, __DECONST(cpumask_t *, mask)); + error = intr_setaffinity(vector, CPU_WHICH_IRQ, mask); else error = intr_setaffinity(vector, CPU_WHICH_IRQ, cpuset_root); diff --git a/sys/compat/linuxkpi/common/include/linux/io.h b/sys/compat/linuxkpi/common/include/linux/io.h index bce70ed0cb8d..2d6fef4e7c52 100644 --- a/sys/compat/linuxkpi/common/include/linux/io.h +++ b/sys/compat/linuxkpi/common/include/linux/io.h @@ -36,6 +36,7 @@ #include <linux/compiler.h> #include <linux/err.h> +#include <asm-generic/io.h> #include <linux/types.h> #if !defined(__arm__) #include <asm/set_memory.h> @@ -395,11 +396,7 @@ iowrite32be(uint32_t v, volatile void *addr) #define iowrite32be(v, addr) iowrite32be(v, addr) #if defined(__i386__) || defined(__amd64__) -static inline void -_outb(u_char data, u_int port) -{ - __asm __volatile("outb %0, %w1" : : "a" (data), "Nd" (port)); -} +#define _outb(data, port) outb((data), (port)) #endif #if defined(__i386__) || defined(__amd64__) || defined(__powerpc__) || defined(__aarch64__) || defined(__riscv) @@ -541,30 +538,29 @@ void lkpi_arch_phys_wc_del(int); #define arch_phys_wc_index(x) \ (((x) < __MTRR_ID_BASE) ? -1 : ((x) - __MTRR_ID_BASE)) -#if defined(__amd64__) || defined(__i386__) || defined(__aarch64__) || defined(__powerpc__) || defined(__riscv) static inline int arch_io_reserve_memtype_wc(resource_size_t start, resource_size_t size) { +#if defined(__amd64__) vm_offset_t va; va = PHYS_TO_DMAP(start); - -#ifdef VM_MEMATTR_WRITE_COMBINING return (-pmap_change_attr(va, size, VM_MEMATTR_WRITE_COMBINING)); #else - return (-pmap_change_attr(va, size, VM_MEMATTR_UNCACHEABLE)); + return (0); #endif } static inline void arch_io_free_memtype_wc(resource_size_t start, resource_size_t size) { +#if defined(__amd64__) vm_offset_t va; va = PHYS_TO_DMAP(start); pmap_change_attr(va, size, VM_MEMATTR_WRITE_BACK); -} #endif +} #endif /* _LINUXKPI_LINUX_IO_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/jiffies.h b/sys/compat/linuxkpi/common/include/linux/jiffies.h index d5cb6e14860e..c2409726e874 100644 --- a/sys/compat/linuxkpi/common/include/linux/jiffies.h +++ b/sys/compat/linuxkpi/common/include/linux/jiffies.h @@ -32,26 +32,27 @@ #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)) #define time_is_after_eq_jiffies(a) time_after_eq(a, jiffies) #define time_is_after_jiffies(a) time_after(a, jiffies) +#define time_is_before_jiffies(a) time_before(a, jiffies) #define HZ hz @@ -67,20 +68,7 @@ extern uint64_t lkpi_msec2hz_rem; extern uint64_t lkpi_msec2hz_div; extern uint64_t lkpi_msec2hz_max; -static inline int -timespec_to_jiffies(const struct timespec *ts) -{ - u64 result; - - result = ((u64)hz * ts->tv_sec) + - (((u64)hz * ts->tv_nsec + NSEC_PER_SEC - 1) / NSEC_PER_SEC); - if (result > MAX_JIFFY_OFFSET) - result = MAX_JIFFY_OFFSET; - - return ((int)result); -} - -static inline int +static inline unsigned long msecs_to_jiffies(uint64_t msec) { uint64_t result; @@ -91,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; @@ -105,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 @@ -132,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/kernel.h b/sys/compat/linuxkpi/common/include/linux/kernel.h index ed4320e80fa7..11a13cbd49b4 100644 --- a/sys/compat/linuxkpi/common/include/linux/kernel.h +++ b/sys/compat/linuxkpi/common/include/linux/kernel.h @@ -44,10 +44,12 @@ #include <linux/build_bug.h> #include <linux/compiler.h> #include <linux/container_of.h> +#include <linux/kstrtox.h> #include <linux/limits.h> +#include <linux/math.h> +#include <linux/minmax.h> #include <linux/stringify.h> #include <linux/errno.h> -#include <linux/sched.h> #include <linux/types.h> #include <linux/typecheck.h> #include <linux/jiffies.h> @@ -123,10 +125,7 @@ extern int linuxkpi_warn_dump_stack; #undef PTR_ALIGN #define PTR_ALIGN(p, a) ((__typeof(p))ALIGN((uintptr_t)(p), (a))) #define IS_ALIGNED(x, a) (((x) & ((__typeof(x))(a) - 1)) == 0) -#define DIV_ROUND_UP(x, n) howmany(x, n) #define __KERNEL_DIV_ROUND_UP(x, n) howmany(x, n) -#define DIV_ROUND_UP_ULL(x, n) DIV_ROUND_UP((unsigned long long)(x), (n)) -#define DIV_ROUND_DOWN_ULL(x, n) (((unsigned long long)(x) / (n)) * (n)) #define FIELD_SIZEOF(t, f) sizeof(((t *)0)->f) #define printk(...) printf(__VA_ARGS__) @@ -270,286 +269,9 @@ extern int linuxkpi_debug; #define _RET_IP_ __builtin_return_address(0) -static inline unsigned long long -simple_strtoull(const char *cp, char **endp, unsigned int base) -{ - return (strtouq(cp, endp, base)); -} - -static inline long long -simple_strtoll(const char *cp, char **endp, unsigned int base) -{ - return (strtoq(cp, endp, base)); -} - -static inline unsigned long -simple_strtoul(const char *cp, char **endp, unsigned int base) -{ - return (strtoul(cp, endp, base)); -} - -static inline long -simple_strtol(const char *cp, char **endp, unsigned int base) -{ - return (strtol(cp, endp, base)); -} - -static inline int -kstrtoul(const char *cp, unsigned int base, unsigned long *res) -{ - char *end; - - *res = strtoul(cp, &end, base); - - /* skip newline character, if any */ - if (*end == '\n') - end++; - if (*cp == 0 || *end != 0) - return (-EINVAL); - return (0); -} - -static inline int -kstrtol(const char *cp, unsigned int base, long *res) -{ - char *end; - - *res = strtol(cp, &end, base); - - /* skip newline character, if any */ - if (*end == '\n') - end++; - if (*cp == 0 || *end != 0) - return (-EINVAL); - return (0); -} - -static inline int -kstrtoint(const char *cp, unsigned int base, int *res) -{ - char *end; - long temp; - - *res = temp = strtol(cp, &end, base); - - /* skip newline character, if any */ - if (*end == '\n') - end++; - if (*cp == 0 || *end != 0) - return (-EINVAL); - if (temp != (int)temp) - return (-ERANGE); - return (0); -} - -static inline int -kstrtouint(const char *cp, unsigned int base, unsigned int *res) -{ - char *end; - unsigned long temp; - - *res = temp = strtoul(cp, &end, base); - - /* skip newline character, if any */ - if (*end == '\n') - end++; - if (*cp == 0 || *end != 0) - return (-EINVAL); - if (temp != (unsigned int)temp) - return (-ERANGE); - return (0); -} - -static inline int -kstrtou8(const char *cp, unsigned int base, u8 *res) -{ - char *end; - unsigned long temp; - - *res = temp = strtoul(cp, &end, base); - - /* skip newline character, if any */ - if (*end == '\n') - end++; - if (*cp == 0 || *end != 0) - return (-EINVAL); - if (temp != (u8)temp) - return (-ERANGE); - return (0); -} - -static inline int -kstrtou16(const char *cp, unsigned int base, u16 *res) -{ - char *end; - unsigned long temp; - - *res = temp = strtoul(cp, &end, base); - - /* skip newline character, if any */ - if (*end == '\n') - end++; - if (*cp == 0 || *end != 0) - return (-EINVAL); - if (temp != (u16)temp) - return (-ERANGE); - return (0); -} - -static inline int -kstrtou32(const char *cp, unsigned int base, u32 *res) -{ - - return (kstrtouint(cp, base, res)); -} - -static inline int -kstrtou64(const char *cp, unsigned int base, u64 *res) -{ - char *end; - - *res = strtouq(cp, &end, base); - - /* skip newline character, if any */ - if (*end == '\n') - end++; - if (*cp == 0 || *end != 0) - return (-EINVAL); - return (0); -} - -static inline int -kstrtoull(const char *cp, unsigned int base, unsigned long long *res) -{ - return (kstrtou64(cp, base, (u64 *)res)); -} - -static inline int -kstrtobool(const char *s, bool *res) -{ - int len; - - if (s == NULL || (len = strlen(s)) == 0 || res == NULL) - return (-EINVAL); - - /* skip newline character, if any */ - if (s[len - 1] == '\n') - len--; - - if (len == 1 && strchr("yY1", s[0]) != NULL) - *res = true; - else if (len == 1 && strchr("nN0", s[0]) != NULL) - *res = false; - else if (strncasecmp("on", s, len) == 0) - *res = true; - else if (strncasecmp("off", s, len) == 0) - *res = false; - else - return (-EINVAL); - - return (0); -} - -static inline int -kstrtobool_from_user(const char __user *s, size_t count, bool *res) -{ - char buf[8] = {}; - - if (count > (sizeof(buf) - 1)) - count = (sizeof(buf) - 1); - - if (copy_from_user(buf, s, count)) - return (-EFAULT); - - return (kstrtobool(buf, res)); -} - -static inline int -kstrtoint_from_user(const char __user *s, size_t count, unsigned int base, - int *p) -{ - char buf[36] = {}; - - if (count > (sizeof(buf) - 1)) - count = (sizeof(buf) - 1); - - if (copy_from_user(buf, s, count)) - return (-EFAULT); - - return (kstrtoint(buf, base, p)); -} - -static inline int -kstrtouint_from_user(const char __user *s, size_t count, unsigned int base, - unsigned int *p) -{ - char buf[36] = {}; - - if (count > (sizeof(buf) - 1)) - count = (sizeof(buf) - 1); - - if (copy_from_user(buf, s, count)) - return (-EFAULT); - - return (kstrtouint(buf, base, p)); -} - -static inline int -kstrtou32_from_user(const char __user *s, size_t count, unsigned int base, - unsigned int *p) -{ - - return (kstrtouint_from_user(s, count, base, p)); -} - -static inline int -kstrtou8_from_user(const char __user *s, size_t count, unsigned int base, - u8 *p) -{ - char buf[8] = {}; - - if (count > (sizeof(buf) - 1)) - count = (sizeof(buf) - 1); - - if (copy_from_user(buf, s, count)) - return (-EFAULT); - - return (kstrtou8(buf, base, p)); -} - -#define min(x, y) ((x) < (y) ? (x) : (y)) -#define max(x, y) ((x) > (y) ? (x) : (y)) - -#define min3(a, b, c) min(a, min(b,c)) -#define max3(a, b, c) max(a, max(b,c)) - -#define min_t(type, x, y) ({ \ - type __min1 = (x); \ - type __min2 = (y); \ - __min1 < __min2 ? __min1 : __min2; }) - -#define max_t(type, x, y) ({ \ - type __max1 = (x); \ - type __max2 = (y); \ - __max1 > __max2 ? __max1 : __max2; }) - #define offsetofend(t, m) \ (offsetof(t, m) + sizeof((((t *)0)->m))) -#define clamp_t(type, _x, min, max) min_t(type, max_t(type, _x, min), max) -#define clamp(x, lo, hi) min( max(x,lo), hi) -#define clamp_val(val, lo, hi) clamp_t(typeof(val), val, lo, hi) - -/* - * This looks more complex than it should be. But we need to - * get the type for the ~ right in round_down (it needs to be - * as wide as the result!), and we want to evaluate the macro - * arguments just once each. - */ -#define __round_mask(x, y) ((__typeof__(x))((y)-1)) -#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) -#define round_down(x, y) ((x) & ~__round_mask(x, y)) - #define smp_processor_id() PCPU_GET(cpuid) #define num_possible_cpus() mp_ncpus #define num_online_cpus() mp_ncpus @@ -559,31 +281,6 @@ extern bool linux_cpu_has_clflush; #define cpu_has_clflush linux_cpu_has_clflush #endif -/* Swap values of a and b */ -#define swap(a, b) do { \ - typeof(a) _swap_tmp = a; \ - a = b; \ - b = _swap_tmp; \ -} while (0) - -#define DIV_ROUND_CLOSEST(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) - -#define DIV_ROUND_CLOSEST_ULL(x, divisor) ({ \ - __typeof(divisor) __d = (divisor); \ - unsigned long long __ret = (x) + (__d) / 2; \ - __ret /= __d; \ - __ret; \ -}) - -static inline uintmax_t -mult_frac(uintmax_t x, uintmax_t multiplier, uintmax_t divisor) -{ - uintmax_t q = (x / divisor); - uintmax_t r = (x % divisor); - - return ((q * multiplier) + ((r * multiplier) / divisor)); -} - typedef struct linux_ratelimit { struct timeval lasttime; int counter; @@ -644,7 +341,7 @@ static inline bool mac_pton(const char *macin, uint8_t *macout) { const char *s, *d; - uint8_t mac[6], hx, lx;; + uint8_t mac[6], hx, lx; int i; if (strlen(macin) < (3 * 6 - 1)) diff --git a/sys/compat/linuxkpi/common/include/linux/kernel_stat.h b/sys/compat/linuxkpi/common/include/linux/kernel_stat.h new file mode 100644 index 000000000000..c960b4ad2cff --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/kernel_stat.h @@ -0,0 +1,34 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * 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_KERNEL_STAT_H_ +#define _LINUXKPI_LINUX_KERNEL_STAT_H_ + +#include <linux/interrupt.h> + +#endif /* _LINUXKPI_LINUX_KERNEL_STAT_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/kref.h b/sys/compat/linuxkpi/common/include/linux/kref.h index 47d61c9ee316..b2fba468f7df 100644 --- a/sys/compat/linuxkpi/common/include/linux/kref.h +++ b/sys/compat/linuxkpi/common/include/linux/kref.h @@ -41,8 +41,7 @@ #include <asm/atomic.h> struct kref { - /* XXX In Linux this is a refcount_t */ - atomic_t refcount; + refcount_t refcount; }; static inline void diff --git a/sys/compat/linuxkpi/common/include/linux/kstrtox.h b/sys/compat/linuxkpi/common/include/linux/kstrtox.h new file mode 100644 index 000000000000..5da99de24197 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/kstrtox.h @@ -0,0 +1,324 @@ +/*- + * Copyright (c) 2010 Isilon Systems, Inc. + * Copyright (c) 2010 iX Systems, Inc. + * Copyright (c) 2010 Panasas, Inc. + * Copyright (c) 2017-2018 Mellanox Technologies, Ltd. + * All rights reserved. + * Copyright (c) 2018 Johannes Lundberg <johalun0@gmail.com> + * Copyright (c) 2020-2022 The FreeBSD Foundation + * Copyright (c) 2021 Vladimir Kondratyev <wulf@FreeBSD.org> + * Copyright (c) 2023 Serenity Cyber Security, LLC + * + * Portions of this software were developed by Bjoern A. Zeeb and + * Emmanuel Vadot 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 unmodified, 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 ``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 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_KSTRTOX_H_ +#define _LINUXKPI_LINUX_KSTRTOX_H_ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/libkern.h> + +#include <linux/compiler.h> +#include <linux/types.h> + +#include <asm/uaccess.h> + +static inline unsigned long long +simple_strtoull(const char *cp, char **endp, unsigned int base) +{ + return (strtouq(cp, endp, base)); +} + +static inline long long +simple_strtoll(const char *cp, char **endp, unsigned int base) +{ + return (strtoq(cp, endp, base)); +} + +static inline unsigned long +simple_strtoul(const char *cp, char **endp, unsigned int base) +{ + return (strtoul(cp, endp, base)); +} + +static inline long +simple_strtol(const char *cp, char **endp, unsigned int base) +{ + return (strtol(cp, endp, base)); +} + +static inline int +kstrtoul(const char *cp, unsigned int base, unsigned long *res) +{ + char *end; + + *res = strtoul(cp, &end, base); + + /* skip newline character, if any */ + if (*end == '\n') + end++; + if (*cp == 0 || *end != 0) + return (-EINVAL); + return (0); +} + +static inline int +kstrtol(const char *cp, unsigned int base, long *res) +{ + char *end; + + *res = strtol(cp, &end, base); + + /* skip newline character, if any */ + if (*end == '\n') + end++; + if (*cp == 0 || *end != 0) + return (-EINVAL); + return (0); +} + +static inline int +kstrtoint(const char *cp, unsigned int base, int *res) +{ + char *end; + long temp; + + *res = temp = strtol(cp, &end, base); + + /* skip newline character, if any */ + if (*end == '\n') + end++; + if (*cp == 0 || *end != 0) + return (-EINVAL); + if (temp != (int)temp) + return (-ERANGE); + return (0); +} + +static inline int +kstrtouint(const char *cp, unsigned int base, unsigned int *res) +{ + char *end; + unsigned long temp; + + *res = temp = strtoul(cp, &end, base); + + /* skip newline character, if any */ + if (*end == '\n') + end++; + if (*cp == 0 || *end != 0) + return (-EINVAL); + if (temp != (unsigned int)temp) + return (-ERANGE); + return (0); +} + +static inline int +kstrtou8(const char *cp, unsigned int base, uint8_t *res) +{ + char *end; + unsigned long temp; + + *res = temp = strtoul(cp, &end, base); + + /* skip newline character, if any */ + if (*end == '\n') + end++; + if (*cp == 0 || *end != 0) + return (-EINVAL); + if (temp != (uint8_t)temp) + return (-ERANGE); + return (0); +} + +static inline int +kstrtou16(const char *cp, unsigned int base, uint16_t *res) +{ + char *end; + unsigned long temp; + + *res = temp = strtoul(cp, &end, base); + + /* skip newline character, if any */ + if (*end == '\n') + end++; + if (*cp == 0 || *end != 0) + return (-EINVAL); + if (temp != (uint16_t)temp) + return (-ERANGE); + return (0); +} + +static inline int +kstrtou32(const char *cp, unsigned int base, uint32_t *res) +{ + + return (kstrtouint(cp, base, res)); +} + +static inline int +kstrtos32(const char *cp, unsigned int base, int32_t *res) +{ + + return (kstrtoint(cp, base, res)); +} + +static inline int +kstrtos64(const char *cp, unsigned int base, int64_t *res) +{ + char *end; + + *res = strtoq(cp, &end, base); + + /* skip newline character, if any */ + if (*end == '\n') + end++; + if (*cp == 0 || *end != 0) + return (-EINVAL); + return (0); +} + +static inline int +kstrtoll(const char *cp, unsigned int base, long long *res) +{ + return (kstrtos64(cp, base, (int64_t *)res)); +} + +static inline int +kstrtou64(const char *cp, unsigned int base, u64 *res) +{ + char *end; + + *res = strtouq(cp, &end, base); + + /* skip newline character, if any */ + if (*end == '\n') + end++; + if (*cp == 0 || *end != 0) + return (-EINVAL); + return (0); +} + +static inline int +kstrtoull(const char *cp, unsigned int base, unsigned long long *res) +{ + return (kstrtou64(cp, base, (uint64_t *)res)); +} + +static inline int +kstrtobool(const char *s, bool *res) +{ + int len; + + if (s == NULL || (len = strlen(s)) == 0 || res == NULL) + return (-EINVAL); + + /* skip newline character, if any */ + if (s[len - 1] == '\n') + len--; + + if (len == 1 && strchr("yY1", s[0]) != NULL) + *res = true; + else if (len == 1 && strchr("nN0", s[0]) != NULL) + *res = false; + else if (strncasecmp("on", s, len) == 0) + *res = true; + else if (strncasecmp("off", s, len) == 0) + *res = false; + else + return (-EINVAL); + + return (0); +} + +static inline int +kstrtobool_from_user(const char __user *s, size_t count, bool *res) +{ + char buf[8] = {}; + + if (count > (sizeof(buf) - 1)) + count = (sizeof(buf) - 1); + + if (copy_from_user(buf, s, count)) + return (-EFAULT); + + return (kstrtobool(buf, res)); +} + +static inline int +kstrtoint_from_user(const char __user *s, size_t count, unsigned int base, + int *p) +{ + char buf[36] = {}; + + if (count > (sizeof(buf) - 1)) + count = (sizeof(buf) - 1); + + if (copy_from_user(buf, s, count)) + return (-EFAULT); + + return (kstrtoint(buf, base, p)); +} + +static inline int +kstrtouint_from_user(const char __user *s, size_t count, unsigned int base, + unsigned int *p) +{ + char buf[36] = {}; + + if (count > (sizeof(buf) - 1)) + count = (sizeof(buf) - 1); + + if (copy_from_user(buf, s, count)) + return (-EFAULT); + + return (kstrtouint(buf, base, p)); +} + +static inline int +kstrtou32_from_user(const char __user *s, size_t count, unsigned int base, + unsigned int *p) +{ + + return (kstrtouint_from_user(s, count, base, p)); +} + +static inline int +kstrtou8_from_user(const char __user *s, size_t count, unsigned int base, + uint8_t *p) +{ + char buf[8] = {}; + + if (count > (sizeof(buf) - 1)) + count = (sizeof(buf) - 1); + + if (copy_from_user(buf, s, count)) + return (-EFAULT); + + return (kstrtou8(buf, base, p)); +} + +#endif /* _LINUXKPI_LINUX_KSTRTOX_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/list.h b/sys/compat/linuxkpi/common/include/linux/list.h index eecb517d780e..a6c74a324dac 100644 --- a/sys/compat/linuxkpi/common/include/linux/list.h +++ b/sys/compat/linuxkpi/common/include/linux/list.h @@ -234,6 +234,11 @@ list_del_init(struct list_head *entry) for (; &p->field != (h); \ p = list_prev_entry(p, field)) +#define list_for_each_rcu(p, head) \ + for (p = rcu_dereference((head)->next); \ + p != (head); \ + p = rcu_dereference((p)->next)) + static inline void list_add(struct list_head *new, struct list_head *head) { diff --git a/sys/compat/linuxkpi/common/include/linux/lockdep.h b/sys/compat/linuxkpi/common/include/linux/lockdep.h index a379e191aad7..93fe445f7057 100644 --- a/sys/compat/linuxkpi/common/include/linux/lockdep.h +++ b/sys/compat/linuxkpi/common/include/linux/lockdep.h @@ -30,6 +30,7 @@ #ifndef _LINUXKPI_LINUX_LOCKDEP_H_ #define _LINUXKPI_LINUX_LOCKDEP_H_ +#include <sys/systm.h> #include <sys/types.h> #include <sys/lock.h> @@ -50,8 +51,8 @@ struct pin_cookie { #define lockdep_unregister_key(key) do { } while(0) #ifdef INVARIANTS -#define lockdep_assert(cond) do { WARN_ON(!cond); } while (0) -#define lockdep_assert_once(cond) do { WARN_ON_ONCE(!cond); } while (0) +#define lockdep_assert(cond) do { WARN_ON(!(cond)); } while (0) +#define lockdep_assert_once(cond) do { WARN_ON_ONCE(!(cond)); } while (0) #define lockdep_assert_not_held(m) do { \ struct lock_object *__lock = (struct lock_object *)(m); \ @@ -70,17 +71,6 @@ struct pin_cookie { #define lockdep_assert_none_held_once() do { } while (0) -static __inline bool -lockdep_is_held(void *__m) -{ - struct lock_object *__lock; - struct thread *__td; - - __lock = __m; - return (LOCK_CLASS(__lock)->lc_owner(__lock, &__td) != 0); -} -#define lockdep_is_held_type(_m, _t) lockdep_is_held(_m) - #else #define lockdep_assert(cond) do { } while (0) #define lockdep_assert_once(cond) do { } while (0) @@ -91,10 +81,23 @@ lockdep_is_held(void *__m) #define lockdep_assert_held_once(m) do { (void)(m); } while (0) -#define lockdep_is_held(m) 1 -#define lockdep_is_held_type(_m, _t) 1 #endif +static __inline bool +lockdep_is_held(void *__m __diagused) +{ +#ifdef INVARIANTS + struct lock_object *__lock; + struct thread *__td; + + __lock = __m; + return (LOCK_CLASS(__lock)->lc_owner(__lock, &__td) != 0); +#else + return (true); +#endif +} +#define lockdep_is_held_type(_m, _t) lockdep_is_held(_m) + #define might_lock(m) do { } while (0) #define might_lock_read(m) do { } while (0) #define might_lock_nested(m, n) do { } while (0) diff --git a/sys/compat/linuxkpi/common/include/linux/log2.h b/sys/compat/linuxkpi/common/include/linux/log2.h index 27e91a8bdbe0..660e9adb6fa9 100644 --- a/sys/compat/linuxkpi/common/include/linux/log2.h +++ b/sys/compat/linuxkpi/common/include/linux/log2.h @@ -33,97 +33,9 @@ #include <sys/libkern.h> -static inline unsigned long -roundup_pow_of_two(unsigned long x) -{ - return (1UL << flsl(x - 1)); -} - -static inline int -is_power_of_2(unsigned long n) -{ - return (n == roundup_pow_of_two(n)); -} - -static inline unsigned long -rounddown_pow_of_two(unsigned long x) -{ - return (1UL << (flsl(x) - 1)); -} - -#define ilog2(n) \ -( \ - __builtin_constant_p(n) ? ( \ - (n) < 1 ? -1 : \ - (n) & (1ULL << 63) ? 63 : \ - (n) & (1ULL << 62) ? 62 : \ - (n) & (1ULL << 61) ? 61 : \ - (n) & (1ULL << 60) ? 60 : \ - (n) & (1ULL << 59) ? 59 : \ - (n) & (1ULL << 58) ? 58 : \ - (n) & (1ULL << 57) ? 57 : \ - (n) & (1ULL << 56) ? 56 : \ - (n) & (1ULL << 55) ? 55 : \ - (n) & (1ULL << 54) ? 54 : \ - (n) & (1ULL << 53) ? 53 : \ - (n) & (1ULL << 52) ? 52 : \ - (n) & (1ULL << 51) ? 51 : \ - (n) & (1ULL << 50) ? 50 : \ - (n) & (1ULL << 49) ? 49 : \ - (n) & (1ULL << 48) ? 48 : \ - (n) & (1ULL << 47) ? 47 : \ - (n) & (1ULL << 46) ? 46 : \ - (n) & (1ULL << 45) ? 45 : \ - (n) & (1ULL << 44) ? 44 : \ - (n) & (1ULL << 43) ? 43 : \ - (n) & (1ULL << 42) ? 42 : \ - (n) & (1ULL << 41) ? 41 : \ - (n) & (1ULL << 40) ? 40 : \ - (n) & (1ULL << 39) ? 39 : \ - (n) & (1ULL << 38) ? 38 : \ - (n) & (1ULL << 37) ? 37 : \ - (n) & (1ULL << 36) ? 36 : \ - (n) & (1ULL << 35) ? 35 : \ - (n) & (1ULL << 34) ? 34 : \ - (n) & (1ULL << 33) ? 33 : \ - (n) & (1ULL << 32) ? 32 : \ - (n) & (1ULL << 31) ? 31 : \ - (n) & (1ULL << 30) ? 30 : \ - (n) & (1ULL << 29) ? 29 : \ - (n) & (1ULL << 28) ? 28 : \ - (n) & (1ULL << 27) ? 27 : \ - (n) & (1ULL << 26) ? 26 : \ - (n) & (1ULL << 25) ? 25 : \ - (n) & (1ULL << 24) ? 24 : \ - (n) & (1ULL << 23) ? 23 : \ - (n) & (1ULL << 22) ? 22 : \ - (n) & (1ULL << 21) ? 21 : \ - (n) & (1ULL << 20) ? 20 : \ - (n) & (1ULL << 19) ? 19 : \ - (n) & (1ULL << 18) ? 18 : \ - (n) & (1ULL << 17) ? 17 : \ - (n) & (1ULL << 16) ? 16 : \ - (n) & (1ULL << 15) ? 15 : \ - (n) & (1ULL << 14) ? 14 : \ - (n) & (1ULL << 13) ? 13 : \ - (n) & (1ULL << 12) ? 12 : \ - (n) & (1ULL << 11) ? 11 : \ - (n) & (1ULL << 10) ? 10 : \ - (n) & (1ULL << 9) ? 9 : \ - (n) & (1ULL << 8) ? 8 : \ - (n) & (1ULL << 7) ? 7 : \ - (n) & (1ULL << 6) ? 6 : \ - (n) & (1ULL << 5) ? 5 : \ - (n) & (1ULL << 4) ? 4 : \ - (n) & (1ULL << 3) ? 3 : \ - (n) & (1ULL << 2) ? 2 : \ - (n) & (1ULL << 1) ? 1 : \ - (n) & (1ULL << 0) ? 0 : \ - -1) : \ - (sizeof(n) <= 4) ? \ - fls((u32)(n)) - 1 : flsll((u64)(n)) - 1 \ -) - -#define order_base_2(x) ilog2(roundup_pow_of_two(x)) +#define is_power_of_2(n) ({ \ + __typeof(n) _n = (n); \ + _n != 0 && (_n & (_n - 1)) == 0; \ +}) #endif /* _LINUXKPI_LINUX_LOG2_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/math.h b/sys/compat/linuxkpi/common/include/linux/math.h new file mode 100644 index 000000000000..5a348a57747b --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/math.h @@ -0,0 +1,76 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2013-2015 Mellanox Technologies, Ltd. + * Copyright (c) 2014-2015 François Tigeot + * Copyright (c) 2016 Matt Macy <mmacy@FreeBSD.org> + * Copyright (c) 2019 Johannes Lundberg <johalun@FreeBSD.org> + * Copyright (c) 2023 Serenity Cyber Security, LLC. + * + * 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_MATH_H_ +#define _LINUXKPI_LINUX_MATH_H_ + +#include <linux/types.h> + +/* + * This looks more complex than it should be. But we need to + * get the type for the ~ right in round_down (it needs to be + * as wide as the result!), and we want to evaluate the macro + * arguments just once each. + */ +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) +#define round_down(x, y) ((x) & ~__round_mask(x, y)) + +#define DIV_ROUND_UP(x, n) howmany(x, n) +#define DIV_ROUND_UP_ULL(x, n) DIV_ROUND_UP((unsigned long long)(x), (n)) +#define DIV_ROUND_DOWN_ULL(x, n) ((unsigned long long)(x) / (n)) + +#define DIV_ROUND_CLOSEST(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) +#define DIV_ROUND_CLOSEST_ULL(x, divisor) ({ \ + __typeof(divisor) __d = (divisor); \ + unsigned long long __ret = (x) + (__d) / 2; \ + __ret /= __d; \ + __ret; \ +}) + +#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60600 +#define abs_diff(x, y) ({ \ + __typeof(x) _x = (x); \ + __typeof(y) _y = (y); \ + _x > _y ? _x - _y : _y - _x; \ +}) +#endif + +static inline uintmax_t +mult_frac(uintmax_t x, uintmax_t multiplier, uintmax_t divisor) +{ + uintmax_t q = (x / divisor); + uintmax_t r = (x % divisor); + + return ((q * multiplier) + ((r * multiplier) / divisor)); +} + +#endif /* _LINUXKPI_LINUX_MATH_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/math64.h b/sys/compat/linuxkpi/common/include/linux/math64.h index cae5e30b08df..a216d350570f 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) { diff --git a/sys/compat/linuxkpi/common/include/linux/mhi.h b/sys/compat/linuxkpi/common/include/linux/mhi.h index 2e0b74f29def..24b3205d6f5a 100644 --- a/sys/compat/linuxkpi/common/include/linux/mhi.h +++ b/sys/compat/linuxkpi/common/include/linux/mhi.h @@ -75,6 +75,8 @@ struct mhi_controller { void *regs; int *irq; const char *fw_image; + const u8 *fw_data; + size_t fw_sz; bool fbc_download; size_t rddm_size; diff --git a/sys/compat/linuxkpi/common/include/linux/minmax.h b/sys/compat/linuxkpi/common/include/linux/minmax.h new file mode 100644 index 000000000000..d48958f0899f --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/minmax.h @@ -0,0 +1,74 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2010 iX Systems, Inc. + * Copyright (c) 2010 Panasas, Inc. + * Copyright (c) 2013-2015 Mellanox Technologies, Ltd. + * Copyright (c) 2014-2015 François Tigeot + * Copyright (c) 2015 Hans Petter Selasky <hselasky@FreeBSD.org> + * Copyright (c) 2016 Matt Macy <mmacy@FreeBSD.org> + * Copyright (c) 2023 Serenity Cyber Security, LLC. + * + * 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_MINMAX_H_ +#define _LINUXKPI_LINUX_MINMAX_H_ + +#include <linux/build_bug.h> +#include <linux/compiler.h> +#include <linux/types.h> + +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define max(x, y) ((x) > (y) ? (x) : (y)) + +#define min3(a, b, c) min(a, min(b, c)) +#define max3(a, b, c) max(a, max(b, c)) + +#define min_not_zero(x, y) ({ \ + __typeof(x) __min1 = (x); \ + __typeof(y) __min2 = (y); \ + __min1 == 0 ? __min2 : ((__min2 == 0) ? __min1 : min(__min1, __min2));\ +}) + +#define min_t(type, x, y) ({ \ + type __min1 = (x); \ + type __min2 = (y); \ + __min1 < __min2 ? __min1 : __min2; }) + +#define max_t(type, x, y) ({ \ + type __max1 = (x); \ + type __max2 = (y); \ + __max1 > __max2 ? __max1 : __max2; }) + +#define clamp_t(type, _x, min, max) min_t(type, max_t(type, _x, min), max) +#define clamp(x, lo, hi) min(max(x, lo), hi) +#define clamp_val(val, lo, hi) clamp_t(typeof(val), val, lo, hi) + +/* Swap values of a and b */ +#define swap(a, b) do { \ + __typeof(a) _swap_tmp = a; \ + a = b; \ + b = _swap_tmp; \ +} while (0) + +#endif /* _LINUXKPI_LINUX_MINMAX_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/mm.h b/sys/compat/linuxkpi/common/include/linux/mm.h index 109bfffe7d6a..156b00a0c0f0 100644 --- a/sys/compat/linuxkpi/common/include/linux/mm.h +++ b/sys/compat/linuxkpi/common/include/linux/mm.h @@ -35,6 +35,7 @@ #include <linux/gfp.h> #include <linux/kernel.h> #include <linux/mm_types.h> +#include <linux/mmzone.h> #include <linux/pfn.h> #include <linux/list.h> #include <linux/mmap_lock.h> @@ -160,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. @@ -178,6 +187,14 @@ get_order(unsigned long size) return (order); } +/* + * Resolve a page into a virtual address: + * + * NOTE: This function only works for pages allocated by the kernel. + */ +void *linux_page_address(const struct page *); +#define page_address(page) linux_page_address(page) + static inline void * lowmem_page_address(struct page *page) { @@ -266,18 +283,65 @@ get_page(struct page *page) vm_page_wire(page); } -extern long -get_user_pages(unsigned long start, unsigned long nr_pages, - unsigned int gup_flags, struct page **, - struct vm_area_struct **); +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 **); +#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60500 +#define get_user_pages(start, nr_pages, gup_flags, pages) \ + lkpi_get_user_pages(start, nr_pages, gup_flags, pages) +#else +#define get_user_pages(start, nr_pages, gup_flags, pages, vmas) \ + lkpi_get_user_pages(start, nr_pages, gup_flags, pages) +#endif + +#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60500 +static inline long +pin_user_pages(unsigned long start, unsigned long nr_pages, + unsigned int gup_flags, struct page **pages) +{ + return (get_user_pages(start, nr_pages, gup_flags, pages)); +} +#else static inline long pin_user_pages(unsigned long start, unsigned long nr_pages, unsigned int gup_flags, struct page **pages, struct vm_area_struct **vmas) { - return get_user_pages(start, nr_pages, gup_flags, pages, vmas); + return (get_user_pages(start, nr_pages, gup_flags, pages, vmas)); } +#endif extern int __get_user_pages_fast(unsigned long start, int nr_pages, int write, @@ -307,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) @@ -348,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); @@ -388,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/mmzone.h b/sys/compat/linuxkpi/common/include/linux/mmzone.h index 49cc218c6fce..57d3dcac9597 100644 --- a/sys/compat/linuxkpi/common/include/linux/mmzone.h +++ b/sys/compat/linuxkpi/common/include/linux/mmzone.h @@ -4,8 +4,12 @@ #define _LINUX_MMZONE_H #include <linux/mm_types.h> +#include <linux/numa.h> #include <linux/page-flags.h> #define MAX_ORDER 11 +#define MAX_PAGE_ORDER 10 +#define NR_PAGE_ORDERS (MAX_PAGE_ORDER + 1) + #endif diff --git a/sys/compat/linuxkpi/common/include/linux/module.h b/sys/compat/linuxkpi/common/include/linux/module.h index 9d98ef7cf3db..079dacf8df6c 100644 --- a/sys/compat/linuxkpi/common/include/linux/module.h +++ b/sys/compat/linuxkpi/common/include/linux/module.h @@ -32,6 +32,8 @@ #include <sys/types.h> #include <sys/param.h> #include <sys/module.h> +#include <sys/queue.h> +#include <sys/linker.h> #include <linux/list.h> #include <linux/compiler.h> @@ -51,7 +53,26 @@ #define MODULE_SUPPORTED_DEVICE(name) #define MODULE_IMPORT_NS(_name) +/* + * THIS_MODULE is used to differentiate modules on Linux. We currently + * completely stub out any Linux struct module usage, but THIS_MODULE is still + * used to populate the "owner" fields of various drivers. Even though we + * don't actually dereference these "owner" fields they are still used by + * drivers to check if devices/dmabufs/etc come from different modules. For + * example, during DRM GEM import some drivers check if the dmabuf's owner + * matches the dev's owner. If they match because they are both NULL drivers + * may incorrectly think two resources come from the same module. + * + * To handle this we specify an undefined symbol __this_linker_file, which + * will get special treatment from the linker when resolving. This will + * populate the usages of __this_linker_file with the linker_file_t of the + * module. + */ +#ifdef KLD_MODULE +#define THIS_MODULE ((struct module *)&__this_linker_file) +#else #define THIS_MODULE ((struct module *)0) +#endif #define __MODULE_STRING(x) __stringify(x) 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 3d2b309909b4..cd7d23077a62 100644 --- a/sys/compat/linuxkpi/common/include/linux/netdevice.h +++ b/sys/compat/linuxkpi/common/include/linux/netdevice.h @@ -75,6 +75,10 @@ struct wireless_dev; /* net/cfg80211.h */ #define NET_NAME_UNKNOWN 0 +enum net_addr_assign_type { + NET_ADDR_RANDOM, +}; + enum netdev_tx { NETDEV_TX_OK = 0, }; @@ -95,6 +99,10 @@ enum net_device_reg_state { NETREG_REGISTERED, }; +enum tc_setup_type { + TC_SETUP_MAX_DUMMY, +}; + struct net_device_ops { int (*ndo_open)(struct net_device *); int (*ndo_stop)(struct net_device *); @@ -122,6 +130,7 @@ struct net_device { unsigned long tx_errors; unsigned long tx_packets; } stats; + enum net_addr_assign_type addr_assign_type; enum net_device_reg_state reg_state; const struct ethtool_ops *ethtool_ops; const struct net_device_ops *netdev_ops; @@ -457,6 +466,8 @@ void linuxkpi_free_netdev(struct net_device *); #define alloc_netdev(_l, _n, _f, _func) \ linuxkpi_alloc_netdev(_l, _n, _f, _func) +#define alloc_netdev_dummy(_l) \ + linuxkpi_alloc_netdev(_l, "dummy", NET_NAME_UNKNOWN, NULL) #define free_netdev(_n) \ linuxkpi_free_netdev(_n) diff --git a/sys/compat/linuxkpi/common/include/linux/nl80211.h b/sys/compat/linuxkpi/common/include/linux/nl80211.h index 5b43ff675e19..f3979d3a2abc 100644 --- a/sys/compat/linuxkpi/common/include/linux/nl80211.h +++ b/sys/compat/linuxkpi/common/include/linux/nl80211.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2020-2023 The FreeBSD Foundation + * Copyright (c) 2020-2024 The FreeBSD Foundation * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -80,6 +80,11 @@ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = BIT(10), NL80211_RRF_NO_320MHZ = BIT(11), NL80211_RRF_NO_EHT = BIT(12), + NL80211_RRF_DFS_CONCURRENT = BIT(13), + NL80211_RRF_NO_6GHZ_VLP_CLIENT = BIT(14), + NL80211_RRF_NO_6GHZ_AFC_CLIENT = BIT(15), + NL80211_RRF_PSD = BIT(16), + NL80211_RRF_ALLOW_6GHZ_VLP_AP = BIT(17), }; #define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS|NL80211_RRF_NO_HT40PLUS) @@ -128,11 +133,13 @@ enum nl80211_band { /* Keep this last. */ NUM_NL80211_BANDS -}; +} __packed; -enum nl80211_chan_flags { - /* XXX TODO */ +enum nl80211_channel_type { NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40PLUS, + NL80211_CHAN_HT40MINUS, }; enum nl80211_chan_width { @@ -183,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 { @@ -230,17 +235,26 @@ enum nl80211_ext_feature { NL80211_EXT_FEATURE_BEACON_PROTECTION, NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT, NL80211_EXT_FEATURE_PUNCT, + NL80211_EXT_FEATURE_DFS_CONCURRENT, + NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS, + NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT, + NL80211_EXT_FEATURE_SECURE_LTF, + NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK, + NL80211_EXT_FEATURE_SAE_OFFLOAD_AP, /* Keep this last. */ NUM_NL80211_EXT_FEATURES }; +/* Keep in order with lkpi_nl80211_sta_info_to_str() */ enum nl80211_sta_info { /* XXX TODO */ NL80211_STA_INFO_BEACON_RX, NL80211_STA_INFO_BEACON_SIGNAL_AVG, NL80211_STA_INFO_BSS_PARAM, NL80211_STA_INFO_CHAIN_SIGNAL, + NL80211_STA_INFO_CHAIN_SIGNAL_AVG, NL80211_STA_INFO_CONNECTED_TIME, NL80211_STA_INFO_INACTIVE_TIME, NL80211_STA_INFO_SIGNAL, @@ -416,6 +430,13 @@ enum nl80211_user_reg_hint_type { NL80211_USER_REG_HINT_USER, }; +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/nodemask.h b/sys/compat/linuxkpi/common/include/linux/nodemask.h new file mode 100644 index 000000000000..7a245cc6f256 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/nodemask.h @@ -0,0 +1,46 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Serenity Cyber Security, LLC. + * + * 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_NODEMASK_H_ +#define _LINUXKPI_LINUX_NODEMASK_H_ + +#include <linux/kernel.h> /* pr_debug */ + +static inline int +num_online_nodes(void) +{ + return (1); +} + +static inline int +num_possible_nodes(void) +{ + pr_debug("%s: TODO\n", __func__); + return (1); +} + +#endif /* _LINUXKPI_LINUX_NODEMASK_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/page.h b/sys/compat/linuxkpi/common/include/linux/page.h index aa5acac4f5fe..37ab593a64e9 100644 --- a/sys/compat/linuxkpi/common/include/linux/page.h +++ b/sys/compat/linuxkpi/common/include/linux/page.h @@ -72,6 +72,7 @@ pgprot2cachemode(pgprot_t prot) return (VM_MEMATTR_DEFAULT); } +#define page_to_virt(page) linux_page_address(page) #define virt_to_page(x) PHYS_TO_VM_PAGE(vtophys(x)) #define page_to_pfn(pp) (VM_PAGE_TO_PHYS(pp) >> PAGE_SHIFT) #define pfn_to_page(pfn) (PHYS_TO_VM_PAGE((pfn) << PAGE_SHIFT)) 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 be3b13f07e53..af19829f1cbb 100644 --- a/sys/compat/linuxkpi/common/include/linux/pci.h +++ b/sys/compat/linuxkpi/common/include/linux/pci.h @@ -36,6 +36,7 @@ #define CONFIG_PCI_MSI #include <linux/types.h> +#include <linux/device/driver.h> #include <sys/param.h> #include <sys/bus.h> @@ -59,6 +60,8 @@ #include <linux/pci_ids.h> #include <linux/pm.h> +#include <linux/kernel.h> /* pr_debug */ + struct pci_device_id { uint32_t vendor; uint32_t device; @@ -69,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) \ \ @@ -82,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 @@ -113,6 +119,10 @@ MODULE_PNP_INFO("U32:vendor;U32:device;V32:subvendor;V32:subdevice", \ #define PCI_COMMAND PCIR_COMMAND #define PCI_COMMAND_INTX_DISABLE PCIM_CMD_INTxDIS #define PCI_COMMAND_MEMORY PCIM_CMD_MEMEN +#define PCI_PRIMARY_BUS PCIR_PRIBUS_1 +#define PCI_SECONDARY_BUS PCIR_SECBUS_1 +#define PCI_SUBORDINATE_BUS PCIR_SUBBUS_1 +#define PCI_SEC_LATENCY_TIMER PCIR_SECLAT_1 #define PCI_EXP_DEVCTL PCIER_DEVICE_CTL /* Device Control */ #define PCI_EXP_LNKCTL PCIER_LINK_CTL /* Link Control */ #define PCI_EXP_LNKCTL_ASPM_L0S PCIEM_LINK_CTL_ASPMC_L0S @@ -223,6 +233,8 @@ typedef int pci_power_t; extern const char *pci_power_names[6]; +#define PCI_ERR_UNCOR_STATUS PCIR_AER_UC_STATUS +#define PCI_ERR_COR_STATUS PCIR_AER_COR_STATUS #define PCI_ERR_ROOT_COMMAND PCIR_AER_ROOTERR_CMD #define PCI_ERR_ROOT_ERR_SRC PCIR_AER_COR_SOURCE_ID @@ -232,10 +244,14 @@ extern const char *pci_power_names[6]; #define PCI_L1SS_CTL1 0x8 #define PCI_L1SS_CTL1_L1SS_MASK 0xf -#define PCI_IRQ_LEGACY 0x01 +#define PCI_IRQ_INTX 0x01 #define PCI_IRQ_MSI 0x02 #define PCI_IRQ_MSIX 0x04 -#define PCI_IRQ_ALL_TYPES (PCI_IRQ_MSIX|PCI_IRQ_MSI|PCI_IRQ_LEGACY) +#define PCI_IRQ_ALL_TYPES (PCI_IRQ_MSIX|PCI_IRQ_MSI|PCI_IRQ_INTX) + +#if defined(LINUXKPI_VERSION) && (LINUXKPI_VERSION <= 61000) +#define PCI_IRQ_LEGACY PCI_IRQ_INTX +#endif struct pci_dev; @@ -274,24 +290,8 @@ extern spinlock_t pci_lock; #define __devexit_p(x) x -#define module_pci_driver(_driver) \ - \ -static inline int \ -_pci_init(void) \ -{ \ - \ - return (linux_pci_register_driver(&_driver)); \ -} \ - \ -static inline void \ -_pci_exit(void) \ -{ \ - \ - linux_pci_unregister_driver(&_driver); \ -} \ - \ -module_init(_pci_init); \ -module_exit(_pci_exit) +#define module_pci_driver(_drv) \ + module_driver(_drv, linux_pci_register_driver, linux_pci_unregister_driver) struct msi_msg { uint32_t data; @@ -362,6 +362,8 @@ 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_iounmap(struct pci_dev *pdev, void *res); int linuxkpi_pcim_iomap_regions(struct pci_dev *pdev, uint32_t mask, @@ -380,6 +382,11 @@ 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__) +#define pci_info(pdev, fmt, ...) \ + dev_info(&(pdev)->dev, fmt, ##__VA_ARGS__) + static inline bool dev_is_pci(struct device *dev) { @@ -521,7 +528,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); @@ -774,6 +794,8 @@ 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) @@ -1268,6 +1290,29 @@ pci_dev_present(const struct pci_device_id *cur) return (0); } +static inline const struct pci_device_id * +pci_match_id(const struct pci_device_id *ids, struct pci_dev *pdev) +{ + if (ids == NULL) + return (NULL); + + for (; + ids->vendor != 0 || ids->subvendor != 0 || ids->class_mask != 0; + ids++) + if ((ids->vendor == PCI_ANY_ID || + ids->vendor == pdev->vendor) && + (ids->device == PCI_ANY_ID || + ids->device == pdev->device) && + (ids->subvendor == PCI_ANY_ID || + ids->subvendor == pdev->subsystem_vendor) && + (ids->subdevice == PCI_ANY_ID || + ids->subdevice == pdev->subsystem_device) && + ((ids->class ^ pdev->class) & ids->class_mask) == 0) + return (ids); + + return (NULL); +} + struct pci_dev *lkpi_pci_get_domain_bus_and_slot(int domain, unsigned int bus, unsigned int devfn); #define pci_get_domain_bus_and_slot(domain, bus, devfn) \ @@ -1336,6 +1381,9 @@ pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn, int pos, struct pci_dev *lkpi_pci_get_class(unsigned int class, struct pci_dev *from); #define pci_get_class(class, from) lkpi_pci_get_class(class, from) +struct pci_dev *lkpi_pci_get_base_class(unsigned int class, + struct pci_dev *from); +#define pci_get_base_class(class, from) lkpi_pci_get_base_class(class, from) /* -------------------------------------------------------------------------- */ @@ -1478,4 +1526,12 @@ pci_irq_vector(struct pci_dev *pdev, unsigned int vector) return (-ENXIO); } +static inline int +pci_wake_from_d3(struct pci_dev *pdev, bool enable) +{ + + pr_debug("%s: TODO\n", __func__); + return (0); +} + #endif /* _LINUXKPI_LINUX_PCI_H_ */ 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/perf_event.h b/sys/compat/linuxkpi/common/include/linux/perf_event.h new file mode 100644 index 000000000000..86b0d06cdc1f --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/perf_event.h @@ -0,0 +1,34 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Jean-Sébastien Pédron <dumbbell@FreeBSD.org> + * + * 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_PERF_EVENT_H_ +#define _LINUXKPI_LINUX_PERF_EVENT_H_ + +#include <linux/cgroup.h> + +#endif /* _LINUXKPI_LINUX_PERF_EVENT_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/pm.h b/sys/compat/linuxkpi/common/include/linux/pm.h index 871c7b587864..c8d943027909 100644 --- a/sys/compat/linuxkpi/common/include/linux/pm.h +++ b/sys/compat/linuxkpi/common/include/linux/pm.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2020 The FreeBSD Foundation + * Copyright (c) 2020-2024 The FreeBSD Foundation * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -58,25 +58,26 @@ struct dev_pm_info { IS_ENABLED(CONFIG_PM_SLEEP) ? (_p) : NULL #ifdef CONFIG_PM_SLEEP +#define __SET_PM_OPS(_suspendfunc, _resumefunc) \ + .suspend = _suspendfunc, \ + .resume = _resumefunc, \ + .freeze = _suspendfunc, \ + .thaw = _resumefunc, \ + .poweroff = _suspendfunc, \ + .restore = _resumefunc, \ + #define SIMPLE_DEV_PM_OPS(_name, _suspendfunc, _resumefunc) \ const struct dev_pm_ops _name = { \ - .suspend = _suspendfunc, \ - .resume = _resumefunc, \ - .freeze = _suspendfunc, \ - .thaw = _resumefunc, \ - .poweroff = _suspendfunc, \ - .restore = _resumefunc, \ + __SET_PM_OPS(_suspendfunc, _resumefunc) \ } #define DEFINE_SIMPLE_DEV_PM_OPS(_name, _suspendfunc, _resumefunc) \ const struct dev_pm_ops _name = { \ - .suspend = _suspendfunc, \ - .resume = _resumefunc, \ - .freeze = _suspendfunc, \ - .thaw = _resumefunc, \ - .poweroff = _suspendfunc, \ - .restore = _resumefunc, \ + __SET_PM_OPS(_suspendfunc, _resumefunc) \ } + +#define SET_SYSTEM_SLEEP_PM_OPS(_suspendfunc, _resumefunc) \ + __SET_PM_OPS(_suspendfunc, _resumefunc) #else #define SIMPLE_DEV_PM_OPS(_name, _suspendfunc, _resumefunc) \ const struct dev_pm_ops _name = { \ @@ -86,6 +87,9 @@ const struct dev_pm_ops _name = { \ } #endif +bool linuxkpi_device_can_wakeup(struct device *); +#define device_can_wakeup(_dev) linuxkpi_device_can_wakeup(_dev) + static inline void pm_wakeup_event(struct device *dev __unused, unsigned int x __unused) { 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 933d5aa6f94a..da9d45122d4d 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 @@ -125,4 +87,11 @@ print_hex_dump_bytes(const char *prefix_str, const int prefix_type, #define pr_info_ratelimited(fmt, ...) \ printk_ratelimited(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) +#define no_printk(fmt, ...) \ +({ \ + if (0) \ + printk(pr_fmt(fmt), ##__VA_ARGS__); \ + 0; \ +}) + #endif /* _LINUXKPI_LINUX_PRINTK_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/pwm.h b/sys/compat/linuxkpi/common/include/linux/pwm.h index 59a17f525c91..c0740db675e8 100644 --- a/sys/compat/linuxkpi/common/include/linux/pwm.h +++ b/sys/compat/linuxkpi/common/include/linux/pwm.h @@ -91,4 +91,10 @@ pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) return (-ENOTSUPP); } +static inline int +pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state) +{ + return (0); +} + #endif /* _LINUXKPI_LINUX_PWM_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/radix-tree.h b/sys/compat/linuxkpi/common/include/linux/radix-tree.h index a1204cb20a79..ea75836c26fb 100644 --- a/sys/compat/linuxkpi/common/include/linux/radix-tree.h +++ b/sys/compat/linuxkpi/common/include/linux/radix-tree.h @@ -29,6 +29,7 @@ #ifndef _LINUXKPI_LINUX_RADIX_TREE_H_ #define _LINUXKPI_LINUX_RADIX_TREE_H_ +#include <linux/rcupdate.h> #include <linux/types.h> #define RADIX_TREE_MAP_SHIFT 6 diff --git a/sys/compat/linuxkpi/common/include/linux/random.h b/sys/compat/linuxkpi/common/include/linux/random.h index 808c5bc55974..893ee2b7b728 100644 --- a/sys/compat/linuxkpi/common/include/linux/random.h +++ b/sys/compat/linuxkpi/common/include/linux/random.h @@ -54,6 +54,15 @@ get_random_int(void) return (val); } +static inline uint8_t +get_random_u8(void) +{ + uint8_t val; + + get_random_bytes(&val, sizeof(val)); + return (val); +} + #define get_random_u32() get_random_int() /* diff --git a/sys/compat/linuxkpi/common/include/linux/rbtree.h b/sys/compat/linuxkpi/common/include/linux/rbtree.h index 78bf938eb000..e6033cfd760d 100644 --- a/sys/compat/linuxkpi/common/include/linux/rbtree.h +++ b/sys/compat/linuxkpi/common/include/linux/rbtree.h @@ -175,6 +175,30 @@ rb_replace_node_cached(struct rb_node *old, struct rb_node *new, root->rb_leftmost = new; } +static inline struct rb_node * +rb_add_cached(struct rb_node *node, struct rb_root_cached *tree, + bool (*less)(struct rb_node *, const struct rb_node *)) +{ + struct rb_node **link = &tree->rb_root.rb_node; + struct rb_node *parent = NULL; + bool leftmost = true; + + while (*link != NULL) { + parent = *link; + if (less(node, parent)) { + link = &RB_LEFT(parent, __entry); + } else { + link = &RB_RIGHT(parent, __entry); + leftmost = false; + } + } + + rb_link_node(node, parent, link); + rb_insert_color_cached(node, tree, leftmost); + + return (leftmost ? node : NULL); +} + #undef RB_ROOT #define RB_ROOT (struct rb_root) { NULL } #define RB_ROOT_CACHED (struct rb_root_cached) { RB_ROOT, NULL } diff --git a/sys/compat/linuxkpi/common/include/linux/rcupdate.h b/sys/compat/linuxkpi/common/include/linux/rcupdate.h index 95332217f8f5..85d766c8dbc9 100644 --- a/sys/compat/linuxkpi/common/include/linux/rcupdate.h +++ b/sys/compat/linuxkpi/common/include/linux/rcupdate.h @@ -1,6 +1,10 @@ /*- * Copyright (c) 2016-2017 Mellanox Technologies, Ltd. * All rights reserved. + * Copyright (c) 2024 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 @@ -26,11 +30,20 @@ #ifndef _LINUXKPI_LINUX_RCUPDATE_H_ #define _LINUXKPI_LINUX_RCUPDATE_H_ +#include <sys/cdefs.h> + #include <linux/compiler.h> #include <linux/types.h> +#include <linux/kernel.h> #include <machine/atomic.h> +extern int linuxkpi_rcu_debug; +#define RCU_WARN_ONCE(c, ...) do { \ + if (unlikely(linuxkpi_rcu_debug > 0)) \ + WARN_ONCE((c), ##__VA_ARGS__); \ +} while(0) + #define LINUX_KFREE_RCU_OFFSET_MAX 4096 /* exclusive */ /* BSD specific defines */ @@ -61,6 +74,9 @@ linux_rcu_read_unlock(RCU_TYPE_REGULAR);\ } while (0) +#define rcu_read_lock_held(void) \ + linux_rcu_read_lock_held(RCU_TYPE_REGULAR) + #define synchronize_rcu(void) do { \ linux_synchronize_rcu(RCU_TYPE_REGULAR); \ } while (0) @@ -79,14 +95,34 @@ #define rcu_access_pointer(p) \ ((__typeof(*p) *)READ_ONCE(p)) -#define rcu_dereference_protected(p, c) \ +#define rcu_dereference(p) \ ((__typeof(*p) *)READ_ONCE(p)) -#define rcu_dereference(p) \ - rcu_dereference_protected(p, 0) +#define __rcu_var_name(n, f, l) \ + __CONCAT(__CONCAT(__CONCAT(rcu_, n), _), __COUNTER__) + +#define __rcu_dereference_protected(p, c, n) \ +({ \ + RCU_WARN_ONCE(!(c), "%s:%d: condition for %s failed\n", \ + __func__, __LINE__, __XSTRING(n)); \ + rcu_dereference(p); \ +}) + +#define rcu_dereference_protected(p, c) \ + __rcu_dereference_protected((p), (c), \ + __rcu_var_name(protected, __func__, __LINE__)) + +#define __rcu_dereference_check(p, c, n) \ +({ \ + __typeof(*p) *n = rcu_dereference(p); \ + RCU_WARN_ONCE(!(c), "%s:%d: condition for %s failed\n", \ + __func__, __LINE__, __XSTRING(n)); \ + n; \ +}) -#define rcu_dereference_check(p, c) \ - rcu_dereference_protected(p, c) +#define rcu_dereference_check(p, c) \ + __rcu_dereference_check((p), (c) || rcu_read_lock_held(), \ + __rcu_var_name(check, __func__, __LINE__)) #define rcu_dereference_raw(p) \ ((__typeof(*p) *)READ_ONCE(p)) @@ -113,11 +149,12 @@ /* prototypes */ -extern void linux_call_rcu(unsigned type, struct rcu_head *ptr, rcu_callback_t func); -extern void linux_rcu_barrier(unsigned type); -extern void linux_rcu_read_lock(unsigned type); -extern void linux_rcu_read_unlock(unsigned type); -extern void linux_synchronize_rcu(unsigned type); +void linux_call_rcu(unsigned type, struct rcu_head *ptr, rcu_callback_t func); +void linux_rcu_barrier(unsigned type); +void linux_rcu_read_lock(unsigned type); +void linux_rcu_read_unlock(unsigned type); +bool linux_rcu_read_lock_held(unsigned); +void linux_synchronize_rcu(unsigned type); /* Empty implementation for !DEBUG */ #define init_rcu_head(...) 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 61947485945d..02a7eda3f4a9 100644 --- a/sys/compat/linuxkpi/common/include/linux/refcount.h +++ b/sys/compat/linuxkpi/common/include/linux/refcount.h @@ -31,58 +31,51 @@ #include <linux/atomic.h> -struct refcount_linux { - atomic_t value; -}; -typedef struct refcount_linux refcount_t; +typedef atomic_t refcount_t; static inline void refcount_set(refcount_t *ref, unsigned int i) { - atomic_set(&ref->value, i); + atomic_set(ref, i); } static inline void refcount_inc(refcount_t *ref) { - atomic_inc(&ref->value); + atomic_inc(ref); } static inline bool refcount_inc_not_zero(refcount_t *ref) { - return (atomic_inc_not_zero(&ref->value)); + return (atomic_inc_not_zero(ref)); } static inline void refcount_dec(refcount_t *ref) { - atomic_dec(&ref->value); + atomic_dec(ref); } static inline unsigned int refcount_read(refcount_t *ref) { - return atomic_read(&ref->value); + return atomic_read(ref); } static inline bool refcount_dec_and_lock_irqsave(refcount_t *ref, spinlock_t *lock, unsigned long *flags) { - if (atomic_dec_and_test(&ref->value) == true) { + if (atomic_dec_and_test(ref) == true) { spin_lock_irqsave(lock, flags); return (true); } return (false); } -/* - * struct kref uses atomic_t and not refcount_t so - * we differ from Linux here. - */ static inline bool -refcount_dec_and_test(atomic_t *r) +refcount_dec_and_test(refcount_t *r) { return (atomic_dec_and_test(r)); diff --git a/sys/compat/linuxkpi/common/include/linux/rwlock.h b/sys/compat/linuxkpi/common/include/linux/rwlock.h index 8c1ee36ac4de..3030ec89ff1e 100644 --- a/sys/compat/linuxkpi/common/include/linux/rwlock.h +++ b/sys/compat/linuxkpi/common/include/linux/rwlock.h @@ -34,14 +34,12 @@ #include <sys/rwlock.h> #include <sys/libkern.h> -typedef struct { - struct rwlock rw; -} rwlock_t; +typedef struct rwlock rwlock_t; -#define read_lock(_l) rw_rlock(&(_l)->rw) -#define write_lock(_l) rw_wlock(&(_l)->rw) -#define read_unlock(_l) rw_runlock(&(_l)->rw) -#define write_unlock(_l) rw_wunlock(&(_l)->rw) +#define read_lock(_l) rw_rlock(_l) +#define write_lock(_l) rw_wlock(_l) +#define read_unlock(_l) rw_runlock(_l) +#define write_unlock(_l) rw_wunlock(_l) #define read_lock_irq(lock) read_lock((lock)) #define read_unlock_irq(lock) read_unlock((lock)) #define write_lock_irq(lock) write_lock((lock)) @@ -54,13 +52,6 @@ typedef struct { do { read_unlock(lock); } while (0) #define write_unlock_irqrestore(lock, flags) \ do { write_unlock(lock); } while (0) - -static inline void -rwlock_init(rwlock_t *lock) -{ - - memset(&lock->rw, 0, sizeof(lock->rw)); - rw_init_flags(&lock->rw, "lnxrw", RW_NOWITNESS); -} +#define rwlock_init(_l) rw_init_flags(_l, "lnxrw", RW_NOWITNESS | RW_NEW) #endif /* _LINUXKPI_LINUX_RWLOCK_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/scatterlist.h b/sys/compat/linuxkpi/common/include/linux/scatterlist.h index e462d5c649f1..537f5bebc5aa 100644 --- a/sys/compat/linuxkpi/common/include/linux/scatterlist.h +++ b/sys/compat/linuxkpi/common/include/linux/scatterlist.h @@ -32,8 +32,11 @@ #define _LINUXKPI_LINUX_SCATTERLIST_H_ #include <sys/types.h> +#include <sys/proc.h> +#include <sys/sched.h> #include <sys/sf_buf.h> +#include <linux/err.h> #include <linux/page.h> #include <linux/slab.h> #include <linux/mm.h> @@ -671,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 8cb6b12100d5..3ad2f8e4ce8b 100644 --- a/sys/compat/linuxkpi/common/include/linux/sched.h +++ b/sys/compat/linuxkpi/common/include/linux/sched.h @@ -42,6 +42,7 @@ #include <linux/completion.h> #include <linux/hrtimer.h> #include <linux/mm_types.h> +#include <linux/nodemask.h> #include <linux/pid.h> #include <linux/slab.h> #include <linux/string.h> @@ -52,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 @@ -159,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..876ef9e8dfe5 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 @@ -89,6 +93,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/seqlock.h b/sys/compat/linuxkpi/common/include/linux/seqlock.h index 48e42efc10fe..554fdfd6e202 100644 --- a/sys/compat/linuxkpi/common/include/linux/seqlock.h +++ b/sys/compat/linuxkpi/common/include/linux/seqlock.h @@ -99,7 +99,7 @@ lkpi_write_seqcount_invalidate(seqc_t *seqcp) static inline seqc_t lkpi_seqprop_sequence(const seqc_t *seqcp) { - return (atomic_load_int(__DECONST(seqc_t *, seqcp))); + return (atomic_load_int(seqcp)); } #define seqprop_sequence(s) lkpi_seqprop_sequence(&(s)->seqc) 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/shrinker.h b/sys/compat/linuxkpi/common/include/linux/shrinker.h index a865241cc7cb..eb95dafb83ce 100644 --- a/sys/compat/linuxkpi/common/include/linux/shrinker.h +++ b/sys/compat/linuxkpi/common/include/linux/shrinker.h @@ -27,6 +27,8 @@ #define _LINUXKPI_LINUX_SHRINKER_H_ #include <sys/queue.h> + +#include <linux/bitops.h> #include <linux/gfp.h> struct shrink_control { @@ -39,6 +41,8 @@ struct shrinker { unsigned long (*count_objects)(struct shrinker *, struct shrink_control *); unsigned long (*scan_objects)(struct shrinker *, struct shrink_control *); int seeks; + unsigned int flags; + void * private_data; long batch; TAILQ_ENTRY(shrinker) next; }; @@ -47,10 +51,23 @@ struct shrinker { #define DEFAULT_SEEKS 2 +#define SHRINKER_REGISTERED BIT(0) +#define SHRINKER_ALLOCATED BIT(1) + +struct shrinker *linuxkpi_shrinker_alloc( + unsigned int flags, const char *fmt, ...); int linuxkpi_register_shrinker(struct shrinker *s); void linuxkpi_unregister_shrinker(struct shrinker *s); +void linuxkpi_shrinker_free(struct shrinker *shrinker); void linuxkpi_synchronize_shrinkers(void); +#define shrinker_alloc(flags, fmt, ...) \ + linuxkpi_shrinker_alloc(flags, fmt __VA_OPT__(,) __VA_ARGS__) +#define shrinker_register(shrinker) \ + linuxkpi_register_shrinker(shrinker) +#define shrinker_free(shrinker) \ + linuxkpi_shrinker_free(shrinker) + #if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60000 #define register_shrinker(s, ...) linuxkpi_register_shrinker(s) #else diff --git a/sys/compat/linuxkpi/common/include/linux/skbuff.h b/sys/compat/linuxkpi/common/include/linux/skbuff.h index ee3f427aa6e9..c8ad90281e34 100644 --- a/sys/compat/linuxkpi/common/include/linux/skbuff.h +++ b/sys/compat/linuxkpi/common/include/linux/skbuff.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2020-2023 The FreeBSD Foundation + * Copyright (c) 2020-2025 The FreeBSD Foundation * Copyright (c) 2021-2023 Bjoern A. Zeeb * * This software was developed by Björn Zeeb under sponsorship from @@ -45,8 +45,17 @@ #include <linux/compiler.h> #include <linux/spinlock.h> #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 /* #define SKB_DEBUG */ + #ifdef SKB_DEBUG #define DSKB_TODO 0x01 #define DSKB_IMPROVE 0x02 @@ -145,37 +154,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; + /* 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 */ - - /* "Scratch" area for layers to store metadata. */ - /* ??? I see sizeof() operations so probably an array. */ - uint8_t cb[64] __aligned(CACHE_LINE_SIZE); + uint16_t mac_len; /* Link-layer header length. */ + enum sk_buff_pkt_type pkt_type; + refcount_t refcnt; 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 *); @@ -191,7 +209,7 @@ struct sk_buff *linuxkpi_dev_alloc_skb(size_t, gfp_t); struct sk_buff *linuxkpi_build_skb(void *, size_t); void linuxkpi_kfree_skb(struct sk_buff *); -struct sk_buff *linuxkpi_skb_copy(struct sk_buff *, gfp_t); +struct sk_buff *linuxkpi_skb_copy(const struct sk_buff *, gfp_t); /* -------------------------------------------------------------------------- */ @@ -235,6 +253,13 @@ kfree_skb(struct sk_buff *skb) } static inline void +consume_skb(struct sk_buff *skb) +{ + SKB_TRACE(skb); + kfree_skb(skb); +} + +static inline void dev_kfree_skb(struct sk_buff *skb) { SKB_TRACE(skb); @@ -268,9 +293,12 @@ build_skb(void *data, unsigned int fragsz) /* -------------------------------------------------------------------------- */ -/* XXX BZ review this one for terminal condition as Linux "queues" are special. */ -#define skb_list_walk_safe(_q, skb, tmp) \ - for ((skb) = (_q)->next; (skb) != NULL && ((tmp) = (skb)->next); (skb) = (tmp)) +static inline bool +skb_is_nonlinear(struct sk_buff *skb) +{ + SKB_TRACE(skb); + return ((skb->data_len > 0) ? true : false); +} /* Add headroom; cannot do once there is data in there. */ static inline void @@ -342,12 +370,14 @@ skb_tailroom(struct sk_buff *skb) SKB_TRACE(skb); KASSERT((skb->end - skb->tail) >= 0, ("%s: skb %p tailroom < 0, " "end %p tail %p\n", __func__, skb, skb->end, skb->tail)); + if (unlikely(skb_is_nonlinear(skb))) + return (0); return (skb->end - skb->tail); } -/* Return numer of bytes available at the beginning of buffer. */ +/* Return number of bytes available at the beginning of buffer. */ static inline unsigned int -skb_headroom(struct sk_buff *skb) +skb_headroom(const struct sk_buff *skb) { SKB_TRACE(skb); KASSERT((skb->data - skb->head) >= 0, ("%s: skb %p headroom < 0, " @@ -494,13 +524,10 @@ skb_add_rx_frag(struct sk_buff *skb, int fragno, struct page *page, skb->len += size; skb->data_len += size; skb->truesize += truesize; - - /* XXX TODO EXTEND truesize? */ } /* -------------------------------------------------------------------------- */ -/* XXX BZ review this one for terminal condition as Linux "queues" are special. */ #define skb_queue_walk(_q, skb) \ for ((skb) = (_q)->next; (skb) != (struct sk_buff *)(_q); \ (skb) = (skb)->next) @@ -509,12 +536,23 @@ skb_add_rx_frag(struct sk_buff *skb, int fragno, struct page *page, for ((skb) = (_q)->next, (tmp) = (skb)->next; \ (skb) != (struct sk_buff *)(_q); (skb) = (tmp), (tmp) = (skb)->next) +#define skb_list_walk_safe(_q, skb, tmp) \ + for ((skb) = (_q), (tmp) = ((skb) != NULL) ? (skb)->next ? NULL; \ + ((skb) != NULL); \ + (skb) = (tmp), (tmp) = ((skb) != NULL) ? (skb)->next ? NULL) + static inline bool -skb_queue_empty(struct sk_buff_head *q) +skb_queue_empty(const struct sk_buff_head *q) { + SKB_TRACE(q); + return (q->next == (const struct sk_buff *)q); +} +static inline bool +skb_queue_empty_lockless(const struct sk_buff_head *q) +{ SKB_TRACE(q); - return (q->qlen == 0); + return (READ_ONCE(q->next) == (const struct sk_buff *)q); } static inline void @@ -529,7 +567,8 @@ static inline void skb_queue_head_init(struct sk_buff_head *q) { SKB_TRACE(q); - return (__skb_queue_head_init(q)); + __skb_queue_head_init(q); + spin_lock_init(&q->lock); } static inline void @@ -538,11 +577,11 @@ __skb_insert(struct sk_buff *new, struct sk_buff *prev, struct sk_buff *next, { SKB_TRACE_FMT(new, "prev %p next %p q %p", prev, next, q); - new->prev = prev; - new->next = next; - ((struct sk_buff_head_l *)next)->prev = new; - ((struct sk_buff_head_l *)prev)->next = new; - q->qlen++; + WRITE_ONCE(new->prev, prev); + WRITE_ONCE(new->next, next); + WRITE_ONCE(((struct sk_buff_head_l *)next)->prev, new); + WRITE_ONCE(((struct sk_buff_head_l *)prev)->next, new); + WRITE_ONCE(q->qlen, q->qlen + 1); } static inline void @@ -574,53 +613,62 @@ __skb_queue_tail(struct sk_buff_head *q, struct sk_buff *new) static inline void skb_queue_tail(struct sk_buff_head *q, struct sk_buff *new) { + unsigned long flags; + SKB_TRACE2(q, new); - return (__skb_queue_tail(q, new)); + spin_lock_irqsave(&q->lock, flags); + __skb_queue_tail(q, new); + spin_unlock_irqrestore(&q->lock, flags); } static inline struct sk_buff * -skb_peek(struct sk_buff_head *q) +skb_peek(const struct sk_buff_head *q) { struct sk_buff *skb; skb = q->next; SKB_TRACE2(q, skb); - if (skb == (struct sk_buff *)q) + if (skb == (const struct sk_buff *)q) return (NULL); return (skb); } static inline struct sk_buff * -skb_peek_tail(struct sk_buff_head *q) +skb_peek_tail(const struct sk_buff_head *q) { struct sk_buff *skb; - skb = q->prev; + skb = READ_ONCE(q->prev); SKB_TRACE2(q, skb); - if (skb == (struct sk_buff *)q) + if (skb == (const struct sk_buff *)q) return (NULL); return (skb); } static inline void -__skb_unlink(struct sk_buff *skb, struct sk_buff_head *head) +__skb_unlink(struct sk_buff *skb, struct sk_buff_head *q) { - SKB_TRACE2(skb, head); - struct sk_buff *p, *n;; + struct sk_buff *p, *n; + + SKB_TRACE2(skb, q); - head->qlen--; + WRITE_ONCE(q->qlen, q->qlen - 1); p = skb->prev; n = skb->next; - p->next = n; - n->prev = p; + WRITE_ONCE(n->prev, p); + WRITE_ONCE(p->next, n); skb->prev = skb->next = NULL; } static inline void -skb_unlink(struct sk_buff *skb, struct sk_buff_head *head) +skb_unlink(struct sk_buff *skb, struct sk_buff_head *q) { - SKB_TRACE2(skb, head); - return (__skb_unlink(skb, head)); + unsigned long flags; + + SKB_TRACE2(skb, q); + spin_lock_irqsave(&q->lock, flags); + __skb_unlink(skb, q); + spin_unlock_irqrestore(&q->lock, flags); } static inline struct sk_buff * @@ -628,32 +676,47 @@ __skb_dequeue(struct sk_buff_head *q) { struct sk_buff *skb; - SKB_TRACE(q); - skb = q->next; - if (skb == (struct sk_buff *)q) - return (NULL); + skb = skb_peek(q); if (skb != NULL) __skb_unlink(skb, q); - SKB_TRACE(skb); + SKB_TRACE2(q, skb); return (skb); } static inline struct sk_buff * skb_dequeue(struct sk_buff_head *q) { - SKB_TRACE(q); - return (__skb_dequeue(q)); + unsigned long flags; + struct sk_buff *skb; + + spin_lock_irqsave(&q->lock, flags); + skb = __skb_dequeue(q); + spin_unlock_irqrestore(&q->lock, flags); + SKB_TRACE2(q, skb); + return (skb); } static inline struct sk_buff * -skb_dequeue_tail(struct sk_buff_head *q) +__skb_dequeue_tail(struct sk_buff_head *q) { struct sk_buff *skb; skb = skb_peek_tail(q); if (skb != NULL) __skb_unlink(skb, q); + SKB_TRACE2(q, skb); + return (skb); +} +static inline struct sk_buff * +skb_dequeue_tail(struct sk_buff_head *q) +{ + unsigned long flags; + struct sk_buff *skb; + + spin_lock_irqsave(&q->lock, flags); + skb = __skb_dequeue_tail(q); + spin_unlock_irqrestore(&q->lock, flags); SKB_TRACE2(q, skb); return (skb); } @@ -669,28 +732,75 @@ __skb_queue_head(struct sk_buff_head *q, struct sk_buff *skb) static inline void skb_queue_head(struct sk_buff_head *q, struct sk_buff *skb) { + unsigned long flags; SKB_TRACE2(q, skb); - __skb_queue_after(q, (struct sk_buff *)q, skb); + spin_lock_irqsave(&q->lock, flags); + __skb_queue_head(q, skb); + spin_unlock_irqrestore(&q->lock, flags); } static inline uint32_t -skb_queue_len(struct sk_buff_head *head) +skb_queue_len(const struct sk_buff_head *q) { - SKB_TRACE(head); - return (head->qlen); + SKB_TRACE(q); + return (q->qlen); } static inline uint32_t -skb_queue_len_lockless(const struct sk_buff_head *head) +skb_queue_len_lockless(const struct sk_buff_head *q) { - SKB_TRACE(head); - return (READ_ONCE(head->qlen)); + SKB_TRACE(q); + return (READ_ONCE(q->qlen)); } static inline void +___skb_queue_splice(const struct sk_buff_head *from, + struct sk_buff *p, struct sk_buff *n) +{ + struct sk_buff *b, *e; + + b = from->next; + e = from->prev; + + WRITE_ONCE(b->prev, p); + WRITE_ONCE(((struct sk_buff_head_l *)p)->next, b); + WRITE_ONCE(e->next, n); + WRITE_ONCE(((struct sk_buff_head_l *)n)->prev, e); +} + +static inline void +skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to) +{ + + SKB_TRACE2(from, to); + + if (skb_queue_empty(from)) + return; + + ___skb_queue_splice(from, (struct sk_buff *)to, to->next); + to->qlen += from->qlen; + __skb_queue_head_init(from); +} + +static inline void +skb_queue_splice_tail_init(struct sk_buff_head *from, struct sk_buff_head *to) +{ + + SKB_TRACE2(from, to); + + if (skb_queue_empty(from)) + return; + + ___skb_queue_splice(from, to->prev, (struct sk_buff *)to); + to->qlen += from->qlen; + __skb_queue_head_init(from); +} + + +static inline void __skb_queue_purge(struct sk_buff_head *q) { struct sk_buff *skb; @@ -698,13 +808,26 @@ __skb_queue_purge(struct sk_buff_head *q) SKB_TRACE(q); while ((skb = __skb_dequeue(q)) != NULL) kfree_skb(skb); + WARN_ONCE(skb_queue_len(q) != 0, "%s: queue %p not empty: %u", + __func__, q, skb_queue_len(q)); } static inline void skb_queue_purge(struct sk_buff_head *q) { + struct sk_buff_head _q; + unsigned long flags; + SKB_TRACE(q); - return (__skb_queue_purge(q)); + + if (skb_queue_empty_lockless(q)) + return; + + __skb_queue_head_init(&_q); + spin_lock_irqsave(&q->lock, flags); + skb_queue_splice_init(q, &_q); + spin_unlock_irqrestore(&q->lock, flags); + __skb_queue_purge(&_q); } static inline struct sk_buff * @@ -719,7 +842,7 @@ skb_queue_prev(struct sk_buff_head *q, struct sk_buff *skb) /* -------------------------------------------------------------------------- */ static inline struct sk_buff * -skb_copy(struct sk_buff *skb, gfp_t gfp) +skb_copy(const struct sk_buff *skb, gfp_t gfp) { struct sk_buff *new; @@ -728,13 +851,6 @@ skb_copy(struct sk_buff *skb, gfp_t gfp) return (new); } -static inline void -consume_skb(struct sk_buff *skb) -{ - SKB_TRACE(skb); - SKB_TODO(); -} - static inline uint16_t skb_checksum(struct sk_buff *skb, int offs, size_t len, int x) { @@ -764,8 +880,7 @@ static inline size_t skb_frag_size(const skb_frag_t *frag) { SKB_TRACE(frag); - SKB_TODO(); - return (-1); + return (frag->size); } #define skb_walk_frags(_skb, _frag) \ @@ -790,8 +905,7 @@ static inline void * skb_frag_address(const skb_frag_t *frag) { SKB_TRACE(frag); - SKB_TODO(); - return (NULL); + return (page_address(frag->page + frag->offset)); } static inline void @@ -821,50 +935,7 @@ static inline void skb_mark_not_on_list(struct sk_buff *skb) { SKB_TRACE(skb); - SKB_TODO(); -} - -static inline void -___skb_queue_splice(const struct sk_buff_head *from, - struct sk_buff *p, struct sk_buff *n) -{ - struct sk_buff *b, *e; - - b = from->next; - e = from->prev; - - b->prev = p; - ((struct sk_buff_head_l *)p)->next = b; - e->next = n; - ((struct sk_buff_head_l *)n)->prev = e; -} - -static inline void -skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to) -{ - - SKB_TRACE2(from, to); - - if (skb_queue_empty(from)) - return; - - ___skb_queue_splice(from, (struct sk_buff *)to, to->next); - to->qlen += from->qlen; - __skb_queue_head_init(from); -} - -static inline void -skb_queue_splice_tail_init(struct sk_buff_head *from, struct sk_buff_head *to) -{ - - SKB_TRACE2(from, to); - - if (skb_queue_empty(from)) - return; - - ___skb_queue_splice(from, to->prev, (struct sk_buff *)to); - to->qlen += from->qlen; - __skb_queue_head_init(from); + skb->next = NULL; } static inline void @@ -891,25 +962,17 @@ skb_network_header(struct sk_buff *skb) return (skb->head + skb->l3hdroff); } -static inline bool -skb_is_nonlinear(struct sk_buff *skb) -{ - SKB_TRACE(skb); - return ((skb->data_len > 0) ? true : false); -} - static inline int __skb_linearize(struct sk_buff *skb) { SKB_TRACE(skb); SKB_TODO(); - return (ENXIO); + return (-ENXIO); } static inline int skb_linearize(struct sk_buff *skb) { - return (skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0); } @@ -938,12 +1001,19 @@ 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) { SKB_TRACE(skb); SKB_TODO(); - return (false); + return (true); } static inline uint8_t * @@ -983,10 +1053,9 @@ skb_orphan(struct sk_buff *skb) SKB_TODO(); } -static inline __sum16 +static inline __wsum csum_unfold(__sum16 sum) { - SKB_TODO(); return (sum); } @@ -1012,7 +1081,8 @@ static inline struct sk_buff * skb_get(struct sk_buff *skb) { - SKB_TODO(); /* XXX refcnt? as in get/put_device? */ + SKB_TRACE(skb); + refcount_inc(&skb->refcnt); return (skb); } @@ -1047,7 +1117,8 @@ skb_list_del_init(struct sk_buff *skb) { SKB_TRACE(skb); - SKB_TODO(); + __list_del_entry(&skb->list); + skb_mark_not_on_list(skb); } static inline void @@ -1078,6 +1149,7 @@ static inline void skb_mark_for_recycle(struct sk_buff *skb) { SKB_TRACE(skb); + /* page_pool */ SKB_TODO(); } diff --git a/sys/compat/linuxkpi/common/include/linux/slab.h b/sys/compat/linuxkpi/common/include/linux/slab.h index 298306b6ea05..f3a840d9bf4b 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 @@ -41,13 +45,12 @@ 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 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,12 +88,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) +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___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) { @@ -106,35 +119,89 @@ linux_check_m_flags(gfp_t flags) return (flags & GFP_NATIVE_MASK); } +/* + * Base functions with a native implementation. + */ +static inline void * +kmalloc(size_t size, gfp_t flags) +{ + return (lkpi_kmalloc(size, flags)); +} + static inline void * __kmalloc(size_t size, gfp_t flags) { - return (malloc(MAX(size, sizeof(struct llist_node)), M_KMALLOC, - linux_check_m_flags(flags))); + return (lkpi___kmalloc(size, flags)); } static inline void * kmalloc_node(size_t size, gfp_t flags, int node) { - return (malloc_domainset(size, M_KMALLOC, - linux_get_vm_domain_set(node), linux_check_m_flags(flags))); + return (lkpi___kmalloc_node(size, flags, node)); +} + +static inline void * +krealloc(void *ptr, size_t size, gfp_t flags) +{ + return (lkpi_krealloc(ptr, size, flags)); +} + +static inline void +kfree(const void *ptr) +{ + lkpi_kfree(ptr); +} + +/* + * 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) +{ + if (WOULD_OVERFLOW(n, size)) + panic("%s: %zu * %zu overflowed", __func__, n, size); + + return (kmalloc(size * n, flags)); } static inline void * kcalloc(size_t n, size_t size, gfp_t flags) { flags |= __GFP_ZERO; - return (mallocarray(n, size, M_KMALLOC, linux_check_m_flags(flags))); + return (kmalloc_array(n, size, flags)); +} + +/* kmalloc_node */ +static inline void * +kmalloc_array_node(size_t n, size_t size, gfp_t flags, int node) +{ + if (WOULD_OVERFLOW(n, size)) + panic("%s: %zu * %zu overflowed", __func__, n, size); + + return (kmalloc_node(size * n, flags, node)); } static inline void * kcalloc_node(size_t n, 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 (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)) + return NULL; + + return (krealloc(ptr, n * size, flags)); +} + +/* + * vmalloc/kvalloc functions. + */ static inline void * __vmalloc(size_t size, gfp_t flags, int other) { @@ -154,55 +221,43 @@ vmalloc_32(size_t size) return (contigmalloc(size, M_KMALLOC, M_WAITOK, 0, UINT_MAX, 1, 1)); } +/* May return non-contiguous memory. */ static inline void * -kmalloc_array(size_t n, size_t size, gfp_t flags) +kvmalloc(size_t size, gfp_t flags) { - return (mallocarray(n, size, M_KMALLOC, linux_check_m_flags(flags))); -} - -static inline void * -kmalloc_array_node(size_t n, size_t size, gfp_t flags, int node) -{ - return (mallocarray_domainset(n, size, M_KMALLOC, - linux_get_vm_domain_set(node), linux_check_m_flags(flags))); + return (malloc(size, M_KMALLOC, linux_check_m_flags(flags))); } static inline void * kvmalloc_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); -static inline void * -krealloc(void *ptr, size_t size, gfp_t flags) -{ - return (realloc(ptr, size, M_KMALLOC, linux_check_m_flags(flags))); + return (kvmalloc(size * n, flags)); } static inline void * -krealloc_array(void *ptr, size_t n, size_t size, gfp_t flags) +kvrealloc(const void *ptr, size_t oldsize, size_t newsize, gfp_t flags) { - if (WOULD_OVERFLOW(n, size)) { - return NULL; - } - - return (realloc(ptr, n * size, M_KMALLOC, linux_check_m_flags(flags))); -} + void *newptr; -extern void linux_kfree_async(void *); + if (newsize <= oldsize) + return (__DECONST(void *, ptr)); -static inline void -kfree(const void *ptr) -{ - if (ZERO_OR_NULL_PTR(ptr)) - return; + newptr = kvmalloc(newsize, flags); + if (newptr != NULL) { + memcpy(newptr, ptr, oldsize); + kvfree(ptr); + } - if (curthread->td_critnest != 0) - linux_kfree_async(__DECONST(void *, ptr)); - else - free(__DECONST(void *, ptr), M_KMALLOC); + return (newptr); } +/* + * Misc. + */ + static __inline void kfree_sensitive(const void *ptr) { @@ -218,12 +273,12 @@ ksize(const void *ptr) return (malloc_usable_size(ptr)); } -extern void *lkpi_kmalloc(size_t size, gfp_t flags); -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 *); +static inline size_t +kmalloc_size_roundup(size_t size) +{ + if (unlikely(size == 0 || size == SIZE_MAX)) + return (size); + return (malloc_size(size)); +} #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 b0aec2d4afbd..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 @@ -42,6 +42,8 @@ struct mtk_wed_device { #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _entry) \ 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 4bc1e2a2d431..dc10b0457153 100644 --- a/sys/compat/linuxkpi/common/include/linux/spinlock.h +++ b/sys/compat/linuxkpi/common/include/linux/spinlock.h @@ -41,9 +41,7 @@ #include <linux/bottom_half.h> #include <linux/lockdep.h> -typedef struct { - struct mtx m; -} spinlock_t; +typedef struct mtx spinlock_t; /* * By defining CONFIG_SPIN_SKIP LinuxKPI spinlocks and asserts will be @@ -59,7 +57,7 @@ typedef struct { #define spin_lock(_l) do { \ if (SPIN_SKIP()) \ break; \ - mtx_lock(&(_l)->m); \ + mtx_lock(_l); \ local_bh_disable(); \ } while (0) @@ -76,7 +74,7 @@ typedef struct { if (SPIN_SKIP()) \ break; \ local_bh_enable(); \ - mtx_unlock(&(_l)->m); \ + mtx_unlock(_l); \ } while (0) #define spin_unlock_bh(_l) do { \ @@ -93,7 +91,7 @@ typedef struct { if (SPIN_SKIP()) { \ __ret = 1; \ } else { \ - __ret = mtx_trylock(&(_l)->m); \ + __ret = mtx_trylock(_l); \ if (likely(__ret != 0)) \ local_bh_disable(); \ } \ @@ -111,7 +109,7 @@ typedef struct { #define spin_lock_nested(_l, _n) do { \ if (SPIN_SKIP()) \ break; \ - mtx_lock_flags(&(_l)->m, MTX_DUPOK); \ + mtx_lock_flags(_l, MTX_DUPOK); \ local_bh_disable(); \ } while (0) @@ -141,31 +139,27 @@ typedef struct { #define _spin_lock_name(...) __spin_lock_name(__VA_ARGS__) #define spin_lock_name(name) _spin_lock_name(name, __FILE__, __LINE__) -#define spin_lock_init(lock) linux_spin_lock_init(lock, spin_lock_name("lnxspin")) +#define spin_lock_init(lock) mtx_init(lock, spin_lock_name("lnxspin"), \ + NULL, MTX_DEF | MTX_NOWITNESS | MTX_NEW) -static inline void -linux_spin_lock_init(spinlock_t *lock, const char *name) -{ - - memset(lock, 0, sizeof(*lock)); - mtx_init(&lock->m, name, NULL, MTX_DEF | MTX_NOWITNESS); -} - -static inline void -spin_lock_destroy(spinlock_t *lock) -{ - - mtx_destroy(&lock->m); -} +#define spin_lock_destroy(_l) mtx_destroy(_l) #define DEFINE_SPINLOCK(lock) \ spinlock_t lock; \ - MTX_SYSINIT(lock, &(lock).m, spin_lock_name("lnxspin"), MTX_DEF) + MTX_SYSINIT(lock, &lock, spin_lock_name("lnxspin"), MTX_DEF) #define assert_spin_locked(_l) do { \ if (SPIN_SKIP()) \ break; \ - mtx_assert(&(_l)->m, MA_OWNED); \ + 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) \ 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.h b/sys/compat/linuxkpi/common/include/linux/string.h index 9e1818a38594..f7b64560d254 100644 --- a/sys/compat/linuxkpi/common/include/linux/string.h +++ b/sys/compat/linuxkpi/common/include/linux/string.h @@ -98,6 +98,18 @@ kmemdup(const void *src, size_t len, gfp_t gfp) return (dst); } +/* See slab.h for kvmalloc/kvfree(). */ +static inline void * +kvmemdup(const void *src, size_t len, gfp_t gfp) +{ + void *dst; + + dst = kvmalloc(len, gfp); + if (dst != NULL) + memcpy(dst, src, len); + return (dst); +} + static inline char * strndup_user(const char __user *ustr, long n) { @@ -149,6 +161,24 @@ skip_spaces(const char *str) return (__DECONST(char *, str)); } +/* + * This function trims whitespaces at the end of a string and returns a pointer + * to the first non-whitespace character. + */ +static inline char * +strim(char *str) +{ + char *end; + + end = str + strlen(str); + while (end >= str && (*end == '\0' || isspace(*end))) { + *end = '\0'; + end--; + } + + return (skip_spaces(str)); +} + static inline void * memchr_inv(const void *start, int c, size_t length) { 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..65e023031bb2 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: @@ -156,6 +198,80 @@ sysfs_remove_file(struct kobject *kobj, const struct attribute *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 +464,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..a635f0faea59 100644 --- a/sys/compat/linuxkpi/common/include/linux/timer.h +++ b/sys/compat/linuxkpi/common/include/linux/timer.h @@ -42,7 +42,7 @@ struct timer_list { void (*function_415) (struct timer_list *); }; unsigned long data; - int expires; + unsigned long expires; }; extern unsigned long linux_timer_hz_mask; @@ -76,7 +76,7 @@ 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 *); @@ -86,7 +86,7 @@ extern int timer_shutdown_sync(struct timer_list *); #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/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/units.h b/sys/compat/linuxkpi/common/include/linux/units.h new file mode 100644 index 000000000000..304b5c27d87f --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/units.h @@ -0,0 +1,40 @@ +/*- + * 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_UNITS_H_ +#define _LINUXKPI_LINUX_UNITS_H_ + +#define NANOHZ_PER_HZ 1000000000UL +#define MICROHZ_PER_HZ 1000000UL +#define MILLIHZ_PER_HZ 1000UL +#define HZ_PER_KHZ 1000UL +#define KHZ_PER_MHZ 1000UL +#define HZ_PER_MHZ 1000000UL + +#endif /* _LINUXKPI_LINUX_UNITS_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/wait.h b/sys/compat/linuxkpi/common/include/linux/wait.h index d50003d44955..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 = { \ @@ -110,10 +118,10 @@ extern wait_queue_func_t default_wake_function; wait_queue_head_t name = { \ .task_list = LINUX_LIST_HEAD_INIT(name.task_list), \ }; \ - MTX_SYSINIT(name, &(name).lock.m, spin_lock_name("wqhead"), MTX_DEF) + MTX_SYSINIT(name, &(name).lock, spin_lock_name("wqhead"), MTX_DEF) #define init_waitqueue_head(wqh) do { \ - mtx_init(&(wqh)->lock.m, spin_lock_name("wqhead"), \ + mtx_init(&(wqh)->lock, spin_lock_name("wqhead"), \ NULL, MTX_DEF | MTX_NEW | MTX_NOWITNESS); \ INIT_LIST_HEAD(&(wqh)->task_list); \ } while (0) @@ -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/linux/xarray.h b/sys/compat/linuxkpi/common/include/linux/xarray.h index d293a8f7c2a3..fba36eea0ab5 100644 --- a/sys/compat/linuxkpi/common/include/linux/xarray.h +++ b/sys/compat/linuxkpi/common/include/linux/xarray.h @@ -49,14 +49,14 @@ #define xa_limit_32b XA_LIMIT(0, 0xFFFFFFFF) -#define XA_ASSERT_LOCKED(xa) mtx_assert(&(xa)->mtx, MA_OWNED) -#define xa_lock(xa) mtx_lock(&(xa)->mtx) -#define xa_unlock(xa) mtx_unlock(&(xa)->mtx) +#define XA_ASSERT_LOCKED(xa) mtx_assert(&(xa)->xa_lock, MA_OWNED) +#define xa_lock(xa) mtx_lock(&(xa)->xa_lock) +#define xa_unlock(xa) mtx_unlock(&(xa)->xa_lock) struct xarray { - struct radix_tree_root root; - struct mtx mtx; /* internal mutex */ - uint32_t flags; /* see XA_FLAGS_XXX */ + struct radix_tree_root xa_head; + struct mtx xa_lock; /* internal mutex */ + uint32_t xa_flags; /* see XA_FLAGS_XXX */ }; /* @@ -67,6 +67,7 @@ void *xa_erase(struct xarray *, uint32_t); void *xa_load(struct xarray *, uint32_t); int xa_alloc(struct xarray *, uint32_t *, void *, uint32_t, gfp_t); int xa_alloc_cyclic(struct xarray *, uint32_t *, void *, uint32_t, uint32_t *, gfp_t); +int xa_alloc_cyclic_irq(struct xarray *, uint32_t *, void *, uint32_t, uint32_t *, gfp_t); int xa_insert(struct xarray *, uint32_t, void *, gfp_t); void *xa_store(struct xarray *, uint32_t, void *, gfp_t); void xa_init_flags(struct xarray *, uint32_t); diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h index 7e57ce67cc26..18b34f0e90ec 100644 --- a/sys/compat/linuxkpi/common/include/net/cfg80211.h +++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2020-2023 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 @@ -33,14 +33,19 @@ #include <linux/types.h> #include <linux/nl80211.h> #include <linux/ieee80211.h> +#include <linux/mutex.h> #include <linux/if_ether.h> #include <linux/ethtool.h> #include <linux/device.h> #include <linux/netdevice.h> #include <linux/random.h> #include <linux/skbuff.h> +#include <linux/timer.h> +#include <linux/workqueue.h> #include <net/regulatory.h> +#include <net80211/ieee80211.h> + /* linux_80211.c */ extern int linuxkpi_debug_80211; #ifndef D80211_TODO @@ -49,8 +54,8 @@ extern int linuxkpi_debug_80211; #ifndef D80211_IMPROVE #define D80211_IMPROVE 0x2 #endif -#define TODO(...) if (linuxkpi_debug_80211 & D80211_TODO) \ - printf("%s:%d: XXX LKPI80211 TODO\n", __func__, __LINE__) +#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__) @@ -78,6 +83,9 @@ enum cfg80211_rate_info_flags { /* Max 8 bits as used in struct rate_info. */ }; +#define CFG80211_RATE_INFO_FLAGS_BITS \ + "\20\1MCS\2VHT_MCS\3SGI\5HE_MCS\10EHT_MCS" + extern const uint8_t rfc1042_header[6]; extern const uint8_t bridge_tunnel_header[6]; @@ -106,6 +114,11 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_NO_80MHZ = BIT(7), IEEE80211_CHAN_NO_160MHZ = BIT(8), IEEE80211_CHAN_NO_OFDM = BIT(9), + IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT = BIT(10), + IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT = BIT(11), + IEEE80211_CHAN_PSD = BIT(12), + IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP = BIT(13), + IEEE80211_CHAN_CAN_MONITOR = BIT(14), }; #define IEEE80211_CHAN_NO_HT40 (IEEE80211_CHAN_NO_HT40MINUS|IEEE80211_CHAN_NO_HT40PLUS) @@ -156,7 +169,7 @@ enum rate_info_bw { struct rate_info { uint8_t flags; /* enum cfg80211_rate_info_flags */ - uint8_t bw; + uint8_t bw; /* enum rate_info_bw */ uint16_t legacy; uint8_t mcs; uint8_t nss; @@ -167,11 +180,10 @@ struct rate_info { }; struct ieee80211_rate { - /* TODO FIXME */ - uint32_t bitrate; - uint32_t hw_value; - uint32_t hw_value_short; - uint32_t flags; + uint32_t flags; /* enum ieee80211_rate_flags */ + uint16_t bitrate; + uint16_t hw_value; + uint16_t hw_value_short; }; struct ieee80211_sta_ht_cap { @@ -190,7 +202,7 @@ struct ieee80211_sta_ht_cap { #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ << IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK_S) #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80P80MHZ << IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK_S) -#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK 0x0000000c /* IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK */ +#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK #define IEEE80211_VHT_CAP_RXLDPC 0x00000010 /* IEEE80211_VHTCAP_RXLDPC */ @@ -228,7 +240,8 @@ struct ieee80211_sta_ht_cap { #define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK \ (7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT) /* IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK */ -#define IEEE80211_VHT_CAP_EXT_NSS_BW_MASK 0xc0000000 +#define IEEE80211_VHT_CAP_EXT_NSS_BW_MASK IEEE80211_VHTCAP_EXT_NSS_BW +#define IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT IEEE80211_VHTCAP_EXT_NSS_BW_S struct ieee80211_sta_vht_cap { /* TODO FIXME */ @@ -275,6 +288,9 @@ struct cfg80211_bss_ies { struct cfg80211_bss { /* XXX TODO */ struct cfg80211_bss_ies *ies; + struct cfg80211_bss_ies *beacon_ies; + + int32_t signal; }; struct cfg80211_chan_def { @@ -283,6 +299,7 @@ struct cfg80211_chan_def { enum nl80211_chan_width width; uint32_t center_freq1; uint32_t center_freq2; + uint16_t punctured; }; struct cfg80211_ftm_responder_stats { @@ -378,11 +395,16 @@ struct cfg80211_match_set { struct cfg80211_scan_request { /* XXX TODO */ - int duration, duration_mandatory, flags; bool no_cck; bool scan_6ghz; + bool duration_mandatory; + int8_t tsf_report_link_id; + uint16_t duration; + uint32_t flags; struct wireless_dev *wdev; struct wiphy *wiphy; + uint64_t scan_start; + uint32_t rates[NUM_NL80211_BANDS]; int ie_len; uint8_t *ie; uint8_t mac_addr[ETH_ALEN], mac_addr_mask[ETH_ALEN]; @@ -529,18 +551,39 @@ struct station_del_parameters { }; struct station_info { - /* TODO FIXME */ - int assoc_req_ies_len, connected_time; - int generation, inactive_time, rx_bytes, rx_dropped_misc, rx_packets, signal, tx_bytes, tx_packets; - int filled, rx_beacon, rx_beacon_signal_avg, signal_avg; - int rx_duration, tx_duration, tx_failed, tx_retries; - int ack_signal, avg_ack_signal; + uint64_t filled; /* enum nl80211_sta_info */ + uint32_t connected_time; + uint32_t inactive_time; + + uint64_t rx_bytes; + uint32_t rx_packets; + uint32_t rx_dropped_misc; + + uint64_t rx_duration; + uint32_t rx_beacon; + uint8_t rx_beacon_signal_avg; + + int8_t signal; + int8_t signal_avg; + int8_t ack_signal; + int8_t avg_ack_signal; + + /* gap */ + int generation; + + uint64_t tx_bytes; + uint32_t tx_packets; + uint32_t tx_failed; + uint64_t tx_duration; + uint32_t tx_retries; int chains; uint8_t chain_signal[IEEE80211_MAX_CHAINS]; uint8_t chain_signal_avg[IEEE80211_MAX_CHAINS]; uint8_t *assoc_req_ies; + size_t assoc_req_ies_len; + struct rate_info rxrate; struct rate_info txrate; struct cfg80211_ibss_params bss_param; @@ -599,174 +642,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 @@ -774,6 +649,7 @@ struct linuxkpi_ieee80211_regdomain { #define IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 0x05 #define IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2 0x06 #define IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_7991 0x07 +#define IEEE80211_EHT_MAC_CAP0_SCS_TRAFFIC_DESC 0x08 #define IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK 0x01 @@ -851,28 +727,12 @@ struct linuxkpi_ieee80211_regdomain { #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 { @@ -889,22 +749,27 @@ 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 { - uint8_t rx_tx_mcs7_max_nss; - uint8_t rx_tx_mcs9_max_nss; - uint8_t rx_tx_mcs11_max_nss; - uint8_t rx_tx_mcs13_max_nss; + union { + struct { + uint8_t rx_tx_mcs7_max_nss; + uint8_t rx_tx_mcs9_max_nss; + uint8_t rx_tx_mcs11_max_nss; + uint8_t rx_tx_mcs13_max_nss; + }; + uint8_t rx_tx_max_nss[4]; + }; }; struct ieee80211_eht_mcs_nss_supp_bw { - uint8_t rx_tx_mcs9_max_nss; - uint8_t rx_tx_mcs11_max_nss; - uint8_t rx_tx_mcs13_max_nss; + union { + struct { + uint8_t rx_tx_mcs9_max_nss; + uint8_t rx_tx_mcs11_max_nss; + uint8_t rx_tx_mcs13_max_nss; + }; + uint8_t rx_tx_max_nss[3]; + }; }; struct ieee80211_eht_cap_elem_fixed { @@ -937,7 +802,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; @@ -981,9 +846,12 @@ struct cfg80211_wowlan_nd_info { enum wiphy_wowlan_support_flags { WIPHY_WOWLAN_DISCONNECT, - WIPHY_WOWLAN_GTK_REKEY_FAILURE, WIPHY_WOWLAN_MAGIC_PKT, WIPHY_WOWLAN_SUPPORTS_GTK_REKEY, + WIPHY_WOWLAN_GTK_REKEY_FAILURE, + WIPHY_WOWLAN_EAP_IDENTITY_REQ, + WIPHY_WOWLAN_4WAY_HANDSHAKE, + WIPHY_WOWLAN_RFKILL_RELEASE, WIPHY_WOWLAN_NET_DETECT, }; @@ -997,6 +865,7 @@ struct cfg80211_wowlan_wakeup { /* XXX TODO */ uint16_t pattern_idx; bool disconnect; + bool unprot_deauth_disassoc; bool eap_identity_req; bool four_way_handshake; bool gtk_rekey_failure; @@ -1014,11 +883,22 @@ struct cfg80211_wowlan_wakeup { struct cfg80211_wowlan { /* XXX TODO */ - int disconnect, gtk_rekey_failure, magic_pkt; - int eap_identity_req, four_way_handshake, rfkill_release, tcp, any; + bool any; + bool disconnect; + bool magic_pkt; + bool gtk_rekey_failure; + bool eap_identity_req; + bool four_way_handshake; + bool rfkill_release; + + /* Magic packet patterns. */ int n_patterns; - struct cfg80211_sched_scan_request *nd_config; struct cfg80211_pkt_pattern *patterns; + + /* netdetect? if not assoc? */ + struct cfg80211_sched_scan_request *nd_config; + + void *tcp; /* XXX ? */ }; struct cfg80211_gtk_rekey_data { @@ -1096,7 +976,7 @@ struct wiphy_iftype_ext_capab { const uint8_t *extended_capabilities_mask; uint8_t extended_capabilities_len; uint16_t eml_capabilities; - + uint16_t mld_capa_and_ops; }; struct tid_config_support { @@ -1115,6 +995,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), @@ -1133,10 +1025,23 @@ enum wiphy_flags { WIPHY_FLAG_4ADDR_AP = BIT(14), WIPHY_FLAG_4ADDR_STATION = BIT(15), WIPHY_FLAG_SUPPORTS_MLO = BIT(16), + WIPHY_FLAG_DISABLE_WEXT = BIT(17), }; -struct wiphy { +struct wiphy_work; +typedef void (*wiphy_work_fn)(struct wiphy *, struct wiphy_work *); +struct wiphy_work { + struct list_head entry; + wiphy_work_fn fn; +}; +struct wiphy_delayed_work { + struct wiphy_work work; + struct wiphy *wiphy; + struct timer_list timer; +}; +struct wiphy { + struct mutex mtx; struct device *dev; struct mac_address *addresses; int n_addresses; @@ -1166,6 +1071,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; @@ -1179,16 +1087,22 @@ struct wiphy { unsigned long ext_features[BITS_TO_LONGS(NUM_NL80211_EXT_FEATURES)]; struct dentry *debugfsdir; - struct cfg80211_wowlan_support *wowlan; + + const struct wiphy_wowlan_support *wowlan; + struct cfg80211_wowlan *wowlan_config; /* Lower layer (driver/mac80211) specific data. */ /* Must stay last. */ uint8_t priv[0] __aligned(CACHE_LINE_SIZE); }; +#define lockdep_assert_wiphy(wiphy) \ + lockdep_assert_held(&(wiphy)->mtx) + 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; }; @@ -1249,8 +1163,18 @@ struct cfg80211_ops { struct wiphy *linuxkpi_wiphy_new(const struct cfg80211_ops *, size_t); void linuxkpi_wiphy_free(struct wiphy *wiphy); +void linuxkpi_wiphy_work_queue(struct wiphy *, struct wiphy_work *); +void linuxkpi_wiphy_work_cancel(struct wiphy *, struct wiphy_work *); +void linuxkpi_wiphy_work_flush(struct wiphy *, struct wiphy_work *); +void lkpi_wiphy_delayed_work_timer(struct timer_list *); +void linuxkpi_wiphy_delayed_work_queue(struct wiphy *, + struct wiphy_delayed_work *, unsigned long); +void linuxkpi_wiphy_delayed_work_cancel(struct wiphy *, + struct wiphy_delayed_work *); + int linuxkpi_regulatory_set_wiphy_regd_sync(struct wiphy *wiphy, struct linuxkpi_ieee80211_regdomain *regd); +uint32_t linuxkpi_cfg80211_calculate_bitrate(struct rate_info *); uint32_t linuxkpi_ieee80211_channel_to_frequency(uint32_t, enum nl80211_band); uint32_t linuxkpi_ieee80211_frequency_to_channel(uint32_t, uint32_t); struct linuxkpi_ieee80211_channel * @@ -1300,28 +1224,11 @@ wiphy_dev(struct wiphy *wiphy) return (wiphy->dev); } -#define wiphy_err(_wiphy, _fmt, ...) \ - dev_err((_wiphy)->dev, _fmt, __VA_ARGS__) +#define wiphy_dereference(_w, p) \ + rcu_dereference_check(p, lockdep_is_held(&(_w)->mtx)) -static __inline const struct linuxkpi_ieee80211_regdomain * -wiphy_dereference(struct wiphy *wiphy, - const struct linuxkpi_ieee80211_regdomain *regd) -{ - TODO(); - return (NULL); -} - -static __inline void -wiphy_lock(struct wiphy *wiphy) -{ - TODO(); -} - -static __inline void -wiphy_unlock(struct wiphy *wiphy) -{ - TODO(); -} +#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, @@ -1372,6 +1279,18 @@ 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) @@ -1413,40 +1332,74 @@ cfg80211_pmsr_report(struct wireless_dev *wdev, TODO(); } -static __inline void +static inline void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, - struct linuxkpi_ieee80211_channel *chan, enum nl80211_chan_flags chan_flag) + struct linuxkpi_ieee80211_channel *chan, enum nl80211_channel_type chan_type) { KASSERT(chandef != NULL, ("%s: chandef is NULL\n", __func__)); KASSERT(chan != NULL, ("%s: chan is NULL\n", __func__)); - memset(chandef, 0, sizeof(*chandef)); + /* memset(chandef, 0, sizeof(*chandef)); */ chandef->chan = chan; - chandef->center_freq2 = 0; /* Set here and only overwrite if needed. */ + chandef->center_freq1 = chan->center_freq; + /* chandef->width, center_freq2, punctured */ - switch (chan_flag) { + switch (chan_type) { case NL80211_CHAN_NO_HT: chandef->width = NL80211_CHAN_WIDTH_20_NOHT; - chandef->center_freq1 = chan->center_freq; break; - default: - IMPROVE("Also depends on our manual settings"); - if (chan->flags & IEEE80211_CHAN_NO_HT40) - chandef->width = NL80211_CHAN_WIDTH_20; - else if (chan->flags & IEEE80211_CHAN_NO_80MHZ) - chandef->width = NL80211_CHAN_WIDTH_40; - else if (chan->flags & IEEE80211_CHAN_NO_160MHZ) - chandef->width = NL80211_CHAN_WIDTH_80; - else { - chandef->width = NL80211_CHAN_WIDTH_160; - IMPROVE("80P80 and 320 ..."); - } - chandef->center_freq1 = chan->center_freq; + case NL80211_CHAN_HT20: + chandef->width = NL80211_CHAN_WIDTH_20; + break; + case NL80211_CHAN_HT40MINUS: + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 -= 10; + break; + case NL80211_CHAN_HT40PLUS: + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 += 10; break; }; } +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) @@ -1529,11 +1482,10 @@ cfg80211_find_vendor_ie(unsigned int oui, int oui_type, return (__DECONST(uint8_t *, elem)); } -static __inline uint32_t +static inline uint32_t cfg80211_calculate_bitrate(struct rate_info *rate) { - TODO(); - return (-1); + return (linuxkpi_cfg80211_calculate_bitrate(rate)); } static __inline uint32_t @@ -1661,6 +1613,12 @@ wiphy_ext_feature_set(struct wiphy *wiphy, enum nl80211_ext_feature ef) set_bit(ef, wiphy->ext_features); } +static inline bool +wiphy_ext_feature_isset(struct wiphy *wiphy, enum nl80211_ext_feature ef) +{ + return (test_bit(ef, wiphy->ext_features)); +} + static __inline void * wiphy_net(struct wiphy *wiphy) { @@ -1906,7 +1864,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; @@ -1977,23 +1935,200 @@ 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) +static inline void +_ieee80211_set_sband_iftype_data(struct ieee80211_supported_band *band, + struct ieee80211_sband_iftype_data *iftype_data, size_t nitems) { - TODO(); - return (false); + band->iftype_data = iftype_data; + band->n_iftype_data = nitems; +} + +static inline const struct ieee80211_sband_iftype_data * +ieee80211_get_sband_iftype_data(const struct ieee80211_supported_band *band, + enum nl80211_iftype iftype) +{ + const struct ieee80211_sband_iftype_data *iftype_data; + int i; + + for (i = 0; i < band->n_iftype_data; i++) { + iftype_data = (const void *)&band->iftype_data[i]; + if (iftype_data->types_mask & BIT(iftype)) + return (iftype_data); + } + + return (NULL); } -static __inline const struct ieee80211_sta_eht_cap * +static inline const struct ieee80211_sta_he_cap * +ieee80211_get_he_iftype_cap(const struct ieee80211_supported_band *band, + enum nl80211_iftype iftype) +{ + const struct ieee80211_sband_iftype_data *iftype_data; + const struct ieee80211_sta_he_cap *he_cap; + + iftype_data = ieee80211_get_sband_iftype_data(band, iftype); + if (iftype_data == NULL) + return (NULL); + + he_cap = NULL; + if (iftype_data->he_cap.has_he) + he_cap = &iftype_data->he_cap; + + return (he_cap); +} + +static inline const struct ieee80211_sta_eht_cap * ieee80211_get_eht_iftype_cap(const struct ieee80211_supported_band *band, enum nl80211_iftype iftype) { + const struct ieee80211_sband_iftype_data *iftype_data; + const struct ieee80211_sta_eht_cap *eht_cap; + + iftype_data = ieee80211_get_sband_iftype_data(band, iftype); + if (iftype_data == NULL) + return (NULL); + + eht_cap = NULL; + if (iftype_data->eht_cap.has_eht) + eht_cap = &iftype_data->eht_cap; + + return (eht_cap); +} + +static inline bool +cfg80211_ssid_eq(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2) +{ + int error; + + if (ssid1 == NULL || ssid2 == NULL) /* Can we KASSERT this? */ + return (false); + + if (ssid1->ssid_len != ssid2->ssid_len) + return (false); + error = memcmp(ssid1->ssid, ssid2->ssid, ssid2->ssid_len); + if (error != 0) + return (false); + return (true); +} + +static inline void +cfg80211_rx_unprot_mlme_mgmt(struct net_device *ndev, const uint8_t *hdr, + uint32_t len) +{ + TODO(); +} + +static inline const struct wiphy_iftype_ext_capab * +cfg80211_get_iftype_ext_capa(struct wiphy *wiphy, enum nl80211_iftype iftype) +{ + TODO(); 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) +{ + switch (width) { + case NL80211_CHAN_WIDTH_5: + return (5); + break; + case NL80211_CHAN_WIDTH_10: + return (10); + break; + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return (20); + break; + case NL80211_CHAN_WIDTH_40: + return (40); + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + return (80); + break; + case NL80211_CHAN_WIDTH_160: + return (160); + break; + case NL80211_CHAN_WIDTH_320: + return (320); + break; + } +} + +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 +wiphy_work_init(struct wiphy_work *wwk, wiphy_work_fn fn) +{ + INIT_LIST_HEAD(&wwk->entry); + wwk->fn = fn; +} + +static inline void +wiphy_work_queue(struct wiphy *wiphy, struct wiphy_work *wwk) +{ + linuxkpi_wiphy_work_queue(wiphy, wwk); +} + +static inline void +wiphy_work_cancel(struct wiphy *wiphy, struct wiphy_work *wwk) +{ + linuxkpi_wiphy_work_cancel(wiphy, wwk); +} + +static inline void +wiphy_work_flush(struct wiphy *wiphy, struct wiphy_work *wwk) +{ + linuxkpi_wiphy_work_flush(wiphy, wwk); +} + +static inline void +wiphy_delayed_work_init(struct wiphy_delayed_work *wdwk, wiphy_work_fn fn) +{ + wiphy_work_init(&wdwk->work, fn); + timer_setup(&wdwk->timer, lkpi_wiphy_delayed_work_timer, 0); +} + +static inline void +wiphy_delayed_work_queue(struct wiphy *wiphy, struct wiphy_delayed_work *wdwk, + unsigned long delay) +{ + linuxkpi_wiphy_delayed_work_queue(wiphy, wdwk, delay); +} + +static inline void +wiphy_delayed_work_cancel(struct wiphy *wiphy, struct wiphy_delayed_work *wdwk) +{ + linuxkpi_wiphy_delayed_work_cancel(wiphy, wdwk); +} + +/* -------------------------------------------------------------------------- */ + +#define wiphy_err(_wiphy, _fmt, ...) \ + dev_err((_wiphy)->dev, _fmt, __VA_ARGS__) #define wiphy_info(wiphy, fmt, ...) \ - printf("%s:%d XXX TODO " fmt, __func__, __LINE__, __VA_ARGS__) + dev_info((wiphy)->dev, fmt, ##__VA_ARGS__) +#define wiphy_info_once(wiphy, fmt, ...) \ + 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 fc9d7829dae3..af3199c38939 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-2023 The FreeBSD Foundation - * Copyright (c) 2020-2022 Bjoern A. Zeeb + * Copyright (c) 2020-2025 The FreeBSD Foundation + * Copyright (c) 2020-2025 Bjoern A. Zeeb * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -34,13 +34,16 @@ #include <asm/atomic64.h> #include <linux/bitops.h> +#include <linux/bitfield.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> #include <linux/netdevice.h> #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> #define ARPHRD_IEEE80211_RADIOTAP __LINE__ /* XXX TODO brcmfmac */ @@ -83,6 +86,7 @@ enum mcast_filter_flags { FIF_OTHER_BSS = BIT(4), FIF_PSPOLL = BIT(5), FIF_CONTROL = BIT(6), + FIF_MCAST_ACTION = BIT(7), }; enum ieee80211_bss_changed { @@ -117,6 +121,9 @@ enum ieee80211_bss_changed { BSS_CHANGED_TWT = BIT(28), BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = BIT(30), BSS_CHANGED_EHT_PUNCTURING = BIT(31), + BSS_CHANGED_MLD_VALID_LINKS = BIT_ULL(32), + BSS_CHANGED_MLD_TTLM = BIT_ULL(33), + BSS_CHANGED_TPE = BIT_ULL(34), }; /* 802.11 Figure 9-256 Suite selector format. [OUI(3), SUITE TYPE(1)] */ @@ -167,13 +174,24 @@ enum ieee80211_bss_changed { #define TKIP_PN_TO_IV16(_x) ((uint16_t)(_x & 0xffff)) #define TKIP_PN_TO_IV32(_x) ((uint32_t)((_x >> 16) & 0xffffffff)) -struct ieee80211_sta; +enum ieee80211_neg_ttlm_res { + NEG_TTLM_RES_ACCEPT, + NEG_TTLM_RES_REJECT, +}; + +#define IEEE80211_TTLM_NUM_TIDS 8 +struct ieee80211_neg_ttlm { + uint16_t downlink[IEEE80211_TTLM_NUM_TIDS]; + uint16_t uplink[IEEE80211_TTLM_NUM_TIDS]; +}; /* 802.11-2020 9.4.2.55.3 A-MPDU Parameters field */ #define IEEE80211_HT_AMPDU_PARM_FACTOR 0x3 #define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2 #define IEEE80211_HT_AMPDU_PARM_DENSITY (0x7 << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) +struct ieee80211_sta; + struct ieee80211_ampdu_params { struct ieee80211_sta *sta; enum ieee80211_ampdu_mlme_action action; @@ -191,19 +209,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; @@ -217,11 +222,13 @@ struct mac80211_fils_discovery { }; struct ieee80211_chanctx_conf { - /* TODO FIXME */ - int rx_chains_dynamic, rx_chains_static; - bool radar_enabled; struct cfg80211_chan_def def; struct cfg80211_chan_def min_def; + struct cfg80211_chan_def ap; + + uint8_t rx_chains_dynamic; + uint8_t rx_chains_static; + bool radar_enabled; /* Must stay last. */ uint8_t drv_priv[0] __aligned(CACHE_LINE_SIZE); @@ -240,12 +247,39 @@ struct ieee80211_ema_beacons { } bcn[0]; }; +struct ieee80211_chanreq { + struct cfg80211_chan_def oper; +}; + #define WLAN_MEMBERSHIP_LEN (8) #define WLAN_USER_POSITION_LEN (16) +/* + * 802.11ac-2013, 8.4.2.164 VHT Transmit Power Envelope element + * 802.11-???? ? + */ +struct ieee80211_parsed_tpe_eirp { + int8_t power[5]; + uint8_t count; + bool valid; +}; +struct ieee80211_parsed_tpe_psd { + int8_t power[16]; + uint8_t count; + bool valid; +}; +struct ieee80211_parsed_tpe { + /* We see access to [0] so assume at least 2. */ + struct ieee80211_parsed_tpe_eirp max_local[2]; + struct ieee80211_parsed_tpe_eirp max_reg_client[2]; + struct ieee80211_parsed_tpe_psd psd_local[2]; + struct ieee80211_parsed_tpe_psd psd_reg_client[2]; +}; + struct ieee80211_bss_conf { /* TODO FIXME */ struct ieee80211_vif *vif; + struct cfg80211_bss *bss; const uint8_t *bssid; uint8_t addr[ETH_ALEN]; uint8_t link_id; @@ -253,7 +287,7 @@ struct ieee80211_bss_conf { uint8_t transmitter_bssid[ETH_ALEN]; struct ieee80211_ftm_responder_params *ftmr_params; struct ieee80211_p2p_noa_attr p2p_noa_attr; - struct cfg80211_chan_def chandef; + struct ieee80211_chanreq chanreq; __be32 arp_addr_list[1]; /* XXX TODO */ struct ieee80211_rate *beacon_rate; struct { @@ -288,6 +322,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; @@ -297,17 +332,19 @@ 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; int16_t txpower; uint32_t basic_rates; int mcast_rate[NUM_NL80211_BANDS]; - enum ieee80211_reg_ap_power power_type; + enum ieee80211_ap_reg_power power_type; struct cfg80211_bitrate_mask beacon_tx_rate; struct mac80211_fils_discovery fils_discovery; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_vif *mbssid_tx_vif; + struct ieee80211_parsed_tpe tpe; int ack_enabled, bssid_index, bssid_indicator, cqm_rssi_hyst, cqm_rssi_thold, ema_ap, frame_time_rts_th, ftm_responder; int htc_trig_based_pkt_ext; @@ -316,27 +353,15 @@ 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 { /* TODO FIXME */ int block_tx, count, delay, device_timestamp, timestamp; + uint8_t link_id; 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, @@ -388,11 +413,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; @@ -444,6 +464,11 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD, IEEE80211_HW_SUPPORTS_RC_TABLE, IEEE80211_HW_DETECTS_COLOR_COLLISION, + IEEE80211_HW_DISALLOW_PUNCTURING, + 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 @@ -456,8 +481,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. */ @@ -500,9 +523,18 @@ enum ieee802111_key_flag { IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(6), IEEE80211_KEY_FLAG_GENERATE_MMIE = BIT(7), IEEE80211_KEY_FLAG_RESERVE_TAILROOM = BIT(8), + 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 */ +#endif atomic64_t tx_pn; uint32_t cipher; uint8_t icv_len; /* __unused nowadays? */ @@ -526,10 +558,13 @@ struct ieee80211_key_seq { uint8_t pn[IEEE80211_CCMP_PN_LEN]; } ccmp; struct { - uint8_t pn[IEEE80211_CCMP_PN_LEN]; + uint8_t pn[IEEE80211_GCMP_PN_LEN]; + } gcmp; + struct { + uint8_t pn[IEEE80211_CMAC_PN_LEN]; } aes_cmac; struct { - uint8_t pn[IEEE80211_CCMP_PN_LEN]; + uint8_t pn[IEEE80211_GMAC_PN_LEN]; } aes_gmac; struct { uint32_t iv32; @@ -548,8 +583,10 @@ enum ieee80211_rx_status_flags { RX_FLAG_DUP_VALIDATED = BIT(5), RX_FLAG_FAILED_FCS_CRC = BIT(6), RX_FLAG_ICV_STRIPPED = BIT(7), - RX_FLAG_MACTIME_PLCP_START = BIT(8), - RX_FLAG_MACTIME_START = BIT(9), + RX_FLAG_MACTIME = BIT(8) | BIT(9), + RX_FLAG_MACTIME_PLCP_START = 1 << 8, + RX_FLAG_MACTIME_START = 2 << 8, + RX_FLAG_MACTIME_END = 3 << 8, RX_FLAG_MIC_STRIPPED = BIT(10), RX_FLAG_MMIC_ERROR = BIT(11), RX_FLAG_MMIC_STRIPPED = BIT(12), @@ -564,13 +601,27 @@ enum ieee80211_rx_status_flags { RX_FLAG_AMPDU_IS_LAST = BIT(21), RX_FLAG_AMPDU_LAST_KNOWN = BIT(22), RX_FLAG_AMSDU_MORE = BIT(23), - RX_FLAG_MACTIME_END = BIT(24), + /* = BIT(24), */ RX_FLAG_ONLY_MONITOR = BIT(25), RX_FLAG_SKIP_MONITOR = BIT(26), RX_FLAG_8023 = BIT(27), RX_FLAG_RADIOTAP_TLV_AT_END = BIT(28), + /* = BIT(29), */ + RX_FLAG_MACTIME_IS_RTAP_TS64 = BIT(30), + RX_FLAG_FAILED_PLCP_CRC = BIT(31), }; +#define IEEE80211_RX_STATUS_FLAGS_BITS \ + "\20\1ALLOW_SAME_PN\2AMPDU_DETAILS\3AMPDU_EOF_BIT\4AMPDU_EOF_BIT_KNOWN" \ + "\5DECRYPTED\6DUP_VALIDATED\7FAILED_FCS_CRC\10ICV_STRIPPED" \ + "\11MACTIME_PLCP_START\12MACTIME_START\13MIC_STRIPPED" \ + "\14MMIC_ERROR\15MMIC_STRIPPED\16NO_PSDU\17PN_VALIDATED" \ + "\20RADIOTAP_HE\21RADIOTAP_HE_MU\22RADIOTAP_LSIG\23RADIOTAP_VENDOR_DATA" \ + "\24NO_SIGNAL_VAL\25IV_STRIPPED\26AMPDU_IS_LAST\27AMPDU_LAST_KNOWN" \ + "\30AMSDU_MORE\31MACTIME_END\32ONLY_MONITOR\33SKIP_MONITOR" \ + "\348023\35RADIOTAP_TLV_AT_END\36MACTIME\37MACTIME_IS_RTAP_TS64" \ + "\40FAILED_PLCP_CRC" + enum mac80211_rx_encoding { RX_ENC_LEGACY = 0, RX_ENC_HT, @@ -653,9 +704,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 { @@ -672,13 +724,14 @@ struct ieee80211_sta_agg { }; struct ieee80211_link_sta { + struct ieee80211_sta *sta; uint8_t addr[ETH_ALEN]; uint8_t link_id; uint32_t supp_rates[NUM_NL80211_BANDS]; 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; @@ -696,6 +749,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; @@ -742,7 +796,12 @@ enum ieee80211_vif_driver_flags { IEEE80211_VIF_BEACON_FILTER = BIT(0), IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1), IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2), - IEEE80211_VIF_DISABLE_SMPS_OVERRIDE = BIT(3), +#if defined(LINUXKPI_VERSION) && (LINUXKPI_VERSION < 60600) /* v6.6 */ + IEEE80211_VIF_DISABLE_SMPS_OVERRIDE = BIT(3), /* Renamed to IEEE80211_VIF_EML_ACTIVE. */ +#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 @@ -765,15 +824,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? */ @@ -799,20 +856,23 @@ struct ieee80211_vif_chanctx_switch { struct ieee80211_prep_tx_info { u16 duration; bool success; + bool was_assoc; + int link_id; }; /* XXX-BZ too big, over-reduce size to u8, and array sizes to minuimum to fit in skb->cb. */ /* Also warning: some sizes change by pointer size! This is 64bit only. */ struct ieee80211_tx_info { - enum ieee80211_tx_info_flags flags; + enum ieee80211_tx_info_flags flags; /* 32 bits */ /* TODO FIXME */ - u8 band; - u8 hw_queue; - bool tx_time_est; + enum nl80211_band band; /* 3 bits */ + uint16_t hw_queue:4, /* 4 bits */ + tx_time_est:10; /* 10 bits */ union { struct { struct ieee80211_tx_rate rates[4]; bool use_rts; + uint8_t antennas:2; struct ieee80211_vif *vif; struct ieee80211_key_conf *hw_key; enum ieee80211_tx_control_flags flags; @@ -911,11 +971,12 @@ enum ieee80211_offload_flags { struct ieee80211_ops { /* TODO FIXME */ int (*start)(struct ieee80211_hw *); - void (*stop)(struct ieee80211_hw *); + void (*stop)(struct ieee80211_hw *, bool); 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); @@ -934,7 +995,7 @@ struct ieee80211_ops { void (*mgd_prepare_tx)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_prep_tx_info *); void (*mgd_complete_tx)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_prep_tx_info *); - void (*mgd_protect_tdls_discover)(struct ieee80211_hw *, struct ieee80211_vif *); + void (*mgd_protect_tdls_discover)(struct ieee80211_hw *, struct ieee80211_vif *, unsigned int); void (*flush)(struct ieee80211_hw *, struct ieee80211_vif *, u32, bool); void (*flush_sta)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *); @@ -954,6 +1015,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); @@ -965,10 +1027,10 @@ struct ieee80211_ops { bool (*can_aggregate_in_amsdu)(struct ieee80211_hw *, struct sk_buff *, struct sk_buff *); int (*pre_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel_switch *); - int (*post_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *); + int (*post_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_bss_conf *); void (*channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel_switch *); void (*channel_switch_beacon)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_chan_def *); - void (*abort_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *); + void (*abort_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_bss_conf *); void (*channel_switch_rx_beacon)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel_switch *); int (*tdls_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, u8, struct cfg80211_chan_def *, struct sk_buff *, u32); void (*tdls_cancel_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *); @@ -1007,9 +1069,7 @@ struct ieee80211_ops { int (*set_tim)(struct ieee80211_hw *, struct ieee80211_sta *, bool); int (*set_key)(struct ieee80211_hw *, enum set_key_cmd, struct ieee80211_vif *, struct ieee80211_sta *, struct ieee80211_key_conf *); - void (*set_default_unicast_key)(struct ieee80211_hw *, struct ieee80211_vif *, int); void (*update_tkip_key)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_key_conf *, struct ieee80211_sta *, u32, u16 *); - void (*set_rekey_data)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_gtk_rekey_data *); int (*start_pmsr)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_pmsr_request *); void (*abort_pmsr)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_pmsr_request *); @@ -1030,7 +1090,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 *); @@ -1044,13 +1104,29 @@ struct ieee80211_ops { int (*change_vif_links)(struct ieee80211_hw *, struct ieee80211_vif *, u16, u16, struct ieee80211_bss_conf *[IEEE80211_MLD_MAX_NUM_LINKS]); int (*change_sta_links)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, u16, u16); + 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 *); + void (*link_sta_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_link_sta *, struct dentry *); + void (*link_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_bss_conf *, struct dentry *); /* #endif */ +/* #ifdef CONFIG_PM_SLEEP */ /* Do not change depending on compile-time option. */ + int (*suspend)(struct ieee80211_hw *, struct cfg80211_wowlan *); + int (*resume)(struct ieee80211_hw *); + void (*set_wakeup)(struct ieee80211_hw *, bool); + void (*set_rekey_data)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_gtk_rekey_data *); + void (*set_default_unicast_key)(struct ieee80211_hw *, struct ieee80211_vif *, int); +/* #if IS_ENABLED(CONFIG_IPV6) */ + void (*ipv6_addr_change)(struct ieee80211_hw *, struct ieee80211_vif *, struct inet6_dev *); +/* #endif */ +/* #endif CONFIG_PM_SLEEP */ }; - /* -------------------------------------------------------------------------- */ /* linux_80211.c */ @@ -1073,7 +1149,7 @@ void linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *, struct ieee80211_vif *, void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct ieee80211_key_conf *, void *), - void *); + void *, bool); void linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *, void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_chanctx_conf *, void *), @@ -1253,7 +1329,7 @@ ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif) (_link = rcu_dereference((_vif)->link_conf[_linkid])) ) #define for_each_sta_active_link(_vif, _sta, _linksta, _linkid) \ - for (_linkid = 0; _linkid < nitems((_vif)->link_conf); _linkid++) \ + for (_linkid = 0; _linkid < nitems((_sta)->link); _linkid++) \ if ( ((_vif)->active_links == 0 /* no MLO */ || \ ((_vif)->active_links & BIT(_linkid)) != 0) && \ (_linksta = link_sta_dereference_protected((_sta), (_linkid))) ) @@ -1261,353 +1337,12 @@ ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif) /* -------------------------------------------------------------------------- */ static __inline bool -ieee80211_is_action(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_ACTION | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_is_probe_resp(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_RESP | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_is_auth(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_AUTH | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_is_assoc_req(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_REQ | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_is_assoc_resp(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_RESP | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_is_reassoc_req(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_REQ | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_is_reassoc_resp(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_RESP | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_is_disassoc(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_DISASSOC | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_is_data_present(__le16 fc) -{ - __le16 v; - - /* If it is a data frame and NODATA is not present. */ - fc &= htole16(IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_NODATA); - v = htole16(IEEE80211_FC0_TYPE_DATA); - - return (fc == v); -} - -static __inline bool -ieee80211_is_deauth(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_DEAUTH | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_is_beacon(__le16 fc) -{ - __le16 v; - - /* - * For as much as I get it this comes in LE and unlike FreeBSD - * where we get the entire frame header and u8[], here we get the - * 9.2.4.1 Frame Control field only. Mask and compare. - */ - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_BEACON | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - - -static __inline bool -ieee80211_is_probe_req(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_REQ | IEEE80211_FC0_TYPE_MGT); - - return (fc == v); -} - -static __inline bool -ieee80211_has_protected(__le16 fc) -{ - - return (fc & htole16(IEEE80211_FC1_PROTECTED << 8)); -} - -static __inline bool -ieee80211_is_back_req(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_BAR | IEEE80211_FC0_TYPE_CTL); - - return (fc == v); -} - -static __inline bool -ieee80211_is_bufferable_mmpdu(struct sk_buff *skb) -{ - struct ieee80211_mgmt *mgmt; - __le16 fc; - - mgmt = (struct ieee80211_mgmt *)skb->data; - fc = mgmt->frame_control; - - /* 11.2.2 Bufferable MMPDUs, 80211-2020. */ - /* XXX we do not care about IBSS yet. */ - - if (!ieee80211_is_mgmt(fc)) - return (false); - if (ieee80211_is_action(fc)) /* XXX FTM? */ - return (true); /* XXX false? */ - if (ieee80211_is_disassoc(fc)) - return (true); - if (ieee80211_is_deauth(fc)) - return (true); - - TODO(); - - return (false); -} - -static __inline bool -ieee80211_is_nullfunc(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA); - - return (fc == v); -} - -static __inline bool -ieee80211_is_qos_nullfunc(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_QOS_NULL | IEEE80211_FC0_TYPE_DATA); - - return (fc == v); -} - -static __inline bool -ieee80211_is_any_nullfunc(__le16 fc) -{ - - return (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)); -} - -static inline bool -ieee80211_is_pspoll(__le16 fc) -{ - __le16 v; - - fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); - v = htole16(IEEE80211_FC0_SUBTYPE_PS_POLL | IEEE80211_FC0_TYPE_CTL); - - return (fc == v); -} - -static __inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) { TODO(); return (false); } -static __inline bool -ieee80211_is_frag(struct ieee80211_hdr *hdr) -{ - TODO(); - return (false); -} - -static __inline bool -ieee80211_is_first_frag(__le16 fc) -{ - TODO(); - return (false); -} - -static __inline bool -ieee80211_is_robust_mgmt_frame(struct sk_buff *skb) -{ - TODO(); - return (false); -} - -static __inline bool -ieee80211_is_ftm(struct sk_buff *skb) -{ - TODO(); - return (false); -} - -static __inline bool -ieee80211_is_timing_measurement(struct sk_buff *skb) -{ - TODO(); - return (false); -} - -static __inline bool -ieee80211_has_pm(__le16 fc) -{ - TODO(); - return (false); -} - -static __inline bool -ieee80211_has_a4(__le16 fc) -{ - __le16 v; - - fc &= htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8); - v = htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8); - - return (fc == v); -} - -static __inline bool -ieee80211_has_order(__le16 fc) -{ - - return (fc & htole16(IEEE80211_FC1_ORDER << 8)); -} - -static __inline bool -ieee80211_has_retry(__le16 fc) -{ - - return (fc & htole16(IEEE80211_FC1_RETRY << 8)); -} - - -static __inline bool -ieee80211_has_fromds(__le16 fc) -{ - - return (fc & htole16(IEEE80211_FC1_DIR_FROMDS << 8)); -} - -static __inline bool -ieee80211_has_tods(__le16 fc) -{ - - return (fc & htole16(IEEE80211_FC1_DIR_TODS << 8)); -} - -static __inline uint8_t * -ieee80211_get_SA(struct ieee80211_hdr *hdr) -{ - - if (ieee80211_has_a4(hdr->frame_control)) - return (hdr->addr4); - if (ieee80211_has_fromds(hdr->frame_control)) - return (hdr->addr3); - return (hdr->addr2); -} - -static __inline uint8_t * -ieee80211_get_DA(struct ieee80211_hdr *hdr) -{ - - if (ieee80211_has_tods(hdr->frame_control)) - return (hdr->addr3); - return (hdr->addr1); -} - -static __inline bool -ieee80211_has_morefrags(__le16 fc) -{ - - fc &= htole16(IEEE80211_FC1_MORE_FRAG << 8); - return (fc != 0); -} - -static __inline u8 * -ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) -{ - if (ieee80211_has_a4(hdr->frame_control)) - return (u8 *)hdr + 30; - else - return (u8 *)hdr + 24; -} /* -------------------------------------------------------------------------- */ /* Receive functions (air/driver to mac80211/net80211). */ @@ -1714,6 +1449,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 @@ -1780,25 +1522,22 @@ ieee80211_iterate_interfaces(struct ieee80211_hw *hw, linuxkpi_ieee80211_iterate_interfaces(hw, flags, iterfunc, arg); } -static __inline void +static inline void ieee80211_iter_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct ieee80211_key_conf *, void *), void *arg) { - - linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg); + linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg, false); } -static __inline void +static inline void ieee80211_iter_keys_rcu(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct ieee80211_key_conf *, void *), void *arg) { - - IMPROVE(); /* "rcu" */ - linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg); + linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg, true); } static __inline void @@ -1841,7 +1580,7 @@ ieee80211_beacon_loss(struct ieee80211_vif *vif) } static __inline void -ieee80211_chswitch_done(struct ieee80211_vif *vif, bool t) +ieee80211_chswitch_done(struct ieee80211_vif *vif, bool t, uint32_t link_id) { TODO(); } @@ -1867,12 +1606,12 @@ ieee80211_csa_update_counter(struct ieee80211_vif *vif) } static __inline void -ieee80211_csa_finish(struct ieee80211_vif *vif) +ieee80211_csa_finish(struct ieee80211_vif *vif, uint32_t link_id) { TODO(); } -static __inline enum nl80211_iftype +static inline enum nl80211_iftype ieee80211_vif_type_p2p(struct ieee80211_vif *vif) { @@ -1970,26 +1709,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) @@ -2077,91 +1796,211 @@ ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, TODO(); } -static __inline void -ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *sta, uint8_t tid, - uint32_t ssn, uint64_t bitmap, uint16_t received_mpdu) -{ - TODO(); -} +/* -------------------------------------------------------------------------- */ -static __inline bool +static inline bool ieee80211_sn_less(uint16_t sn1, uint16_t sn2) { - TODO(); - return (false); + return (IEEE80211_SEQ_BA_BEFORE(sn1, sn2)); } -static __inline uint16_t +static inline uint16_t ieee80211_sn_inc(uint16_t sn) { - TODO(); - return (sn + 1); + return (IEEE80211_SEQ_INC(sn)); } -static __inline uint16_t +static inline uint16_t ieee80211_sn_add(uint16_t sn, uint16_t a) { - TODO(); - return (sn + a); + return (IEEE80211_SEQ_ADD(sn, a)); } -static __inline void -ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, uint32_t x, uint8_t *addr) +static inline uint16_t +ieee80211_sn_sub(uint16_t sa, uint16_t sb) { - TODO(); + return (IEEE80211_SEQ_SUB(sa, sb)); } static __inline void -ieee80211_rate_set_vht(struct ieee80211_tx_rate *r, uint32_t f1, uint32_t f2) +ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *sta, uint8_t tid, + uint32_t ssn, uint64_t bitmap, uint16_t received_mpdu) { TODO(); } -static __inline uint8_t -ieee80211_rate_get_vht_nss(struct ieee80211_tx_rate *r) +static __inline void +ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, uint32_t x, uint8_t *addr) { TODO(); - return (0); } -static __inline uint8_t -ieee80211_rate_get_vht_mcs(struct ieee80211_tx_rate *r) +static __inline void +ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif, uint8_t *addr, + uint8_t tid) { TODO(); - return (0); } static __inline void -ieee80211_reserve_tid(struct ieee80211_sta *sta, uint8_t tid) +ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr, + uint8_t tid) { TODO(); } static __inline void -ieee80211_unreserve_tid(struct ieee80211_sta *sta, uint8_t tid) +ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr, + uint8_t tid) { TODO(); } +/* -------------------------------------------------------------------------- */ + +static inline void +ieee80211_rate_set_vht(struct ieee80211_tx_rate *r, uint8_t mcs, uint8_t nss) +{ + + /* XXX-BZ make it KASSERTS? */ + if (((mcs & 0xF0) != 0) || (((nss - 1) & 0xf8) != 0)) { + printf("%s:%d: mcs %#04x nss %#04x invalid\n", + __func__, __LINE__, mcs, nss); + return; + } + + r->idx = mcs; + r->idx |= ((nss - 1) << 4); +} + +static inline uint8_t +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(const struct ieee80211_tx_rate *r) +{ + return (r->idx & 0x0f); +} + +static inline int +ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *vht_cap, + enum ieee80211_vht_chanwidth chanwidth, /* defined in net80211. */ + int mcs /* always 0 */, bool ext_nss_bw_cap /* always true */, int max_nss) +{ + enum ieee80211_vht_mcs_support mcs_s; + uint32_t supp_cw, ext_nss_bw; + + switch (mcs) { + case 0 ... 7: + mcs_s = IEEE80211_VHT_MCS_SUPPORT_0_7; + break; + case 8: + mcs_s = IEEE80211_VHT_MCS_SUPPORT_0_8; + break; + case 9: + mcs_s = IEEE80211_VHT_MCS_SUPPORT_0_9; + break; + default: + printf("%s: unsupported mcs value %d\n", __func__, mcs); + return (0); + } + + if (max_nss == 0) { + uint16_t map; + + map = le16toh(vht_cap->supp_mcs.rx_mcs_map); + for (int i = 7; i >= 0; i--) { + uint8_t val; + + val = (map >> (2 * i)) & 0x03; + if (val == IEEE80211_VHT_MCS_NOT_SUPPORTED) + continue; + if (val >= mcs_s) { + max_nss = i + 1; + break; + } + } + } + + if (max_nss == 0) + return (0); + + if ((le16toh(vht_cap->supp_mcs.tx_mcs_map) & + IEEE80211_VHT_EXT_NSS_BW_CAPABLE) == 0) + return (max_nss); + + supp_cw = le32_get_bits(vht_cap->vht_cap_info, + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK); + ext_nss_bw = le32_get_bits(vht_cap->vht_cap_info, + IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); + + /* If requested as ext nss not supported assume ext_nss_bw 0. */ + if (!ext_nss_bw_cap) + ext_nss_bw = 0; + + /* + * Cover 802.11-2016, Table 9-250. + */ + + /* Unsupported settings. */ + if (supp_cw == 3) + return (0); + if (supp_cw == 2 && (ext_nss_bw == 1 || ext_nss_bw == 2)) + return (0); + + /* Settings with factor != 1 or unsupported. */ + switch (chanwidth) { + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + if (supp_cw == 0 && (ext_nss_bw == 0 || ext_nss_bw == 1)) + return (0); + if (supp_cw == 1 && ext_nss_bw == 0) + return (0); + if ((supp_cw == 0 || supp_cw == 1) && ext_nss_bw == 2) + return (max_nss / 2); + if ((supp_cw == 0 || supp_cw == 1) && ext_nss_bw == 3) + return (3 * max_nss / 4); + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + if (supp_cw == 0 && ext_nss_bw == 0) + return (0); + if (supp_cw == 0 && (ext_nss_bw == 1 || ext_nss_bw == 2)) + return (max_nss / 2); + if (supp_cw == 0 && ext_nss_bw == 3) + return (3 * max_nss / 4); + if (supp_cw == 1 && ext_nss_bw == 3) + return (2 * max_nss); + break; + case IEEE80211_VHT_CHANWIDTH_80MHZ: + case IEEE80211_VHT_CHANWIDTH_USE_HT: + if ((supp_cw == 1 || supp_cw == 2) && ext_nss_bw == 3) + return (2 * max_nss); + break; + } + + /* Everything else has a factor of 1. */ + return (max_nss); +} + + static __inline void -ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif, uint8_t *addr, - uint8_t tid) +ieee80211_reserve_tid(struct ieee80211_sta *sta, uint8_t tid) { TODO(); } static __inline void -ieee80211_send_eosp_nullfunc(struct ieee80211_sta *sta, uint8_t tid) +ieee80211_unreserve_tid(struct ieee80211_sta *sta, uint8_t tid) { TODO(); } -static __inline uint16_t -ieee80211_sn_sub(uint16_t sa, uint16_t sb) +static __inline void +ieee80211_send_eosp_nullfunc(struct ieee80211_sta *sta, uint8_t tid) { - - return ((sa - sb) & - (IEEE80211_SEQ_SEQ_MASK >> IEEE80211_SEQ_SEQ_SHIFT)); + TODO(); } static __inline void @@ -2183,10 +2022,12 @@ ieee80211_sta_pspoll(struct ieee80211_sta *sta) TODO(); } -static __inline void +static inline void ieee80211_sta_recalc_aggregates(struct ieee80211_sta *sta) { - TODO(); + if (sta->valid_links) { + TODO(); + } } static __inline void @@ -2195,13 +2036,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) { @@ -2235,33 +2069,6 @@ ieee80211_sta_set_buffered(struct ieee80211_sta *sta, uint8_t tid, bool t) } static __inline void -ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, uint8_t tid, - struct ieee80211_key_seq *seq) -{ - - KASSERT(keyconf != NULL && seq != NULL, ("%s: keyconf %p seq %p\n", - __func__, keyconf, seq)); - - TODO(); - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_CCMP_256: - memset(seq->ccmp.pn, 0xfa, sizeof(seq->ccmp.pn)); /* XXX TODO */ - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - memset(seq->aes_cmac.pn, 0xfa, sizeof(seq->aes_cmac.pn)); /* XXX TODO */ - break; - case WLAN_CIPHER_SUITE_TKIP: - seq->tkip.iv32 = 0xfa; /* XXX TODO */ - seq->tkip.iv16 = 0xfa; /* XXX TODO */ - break; - default: - pr_debug("%s: unsupported cipher suite %d\n", __func__, keyconf->cipher); - break; - } -} - -static __inline void ieee80211_sched_scan_results(struct ieee80211_hw *hw) { TODO(); @@ -2377,25 +2184,39 @@ ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *w) linuxkpi_ieee80211_queue_work(hw, w); } -static __inline void -ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) +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) { IMPROVE(); - ieee80211_tx_status(hw, skb); + linuxkpi_ieee80211_tx_status(hw, skb); } static __inline void ieee80211_tx_status_ni(struct ieee80211_hw *hw, struct sk_buff *skb) { IMPROVE(); - ieee80211_tx_status(hw, skb); + linuxkpi_ieee80211_tx_status(hw, skb); } static __inline void @@ -2437,15 +2258,6 @@ ieee80211_txq_get_depth(struct ieee80211_txq *txq, unsigned long *frame_cnt, linuxkpi_ieee80211_txq_get_depth(txq, frame_cnt, byte_cnt); } -static __inline int -rate_lowest_index(struct ieee80211_supported_band *band, - struct ieee80211_sta *sta) -{ - IMPROVE(); - return (0); -} - - static __inline void SET_IEEE80211_PERM_ADDR (struct ieee80211_hw *hw, uint8_t *addr) { @@ -2460,20 +2272,6 @@ ieee80211_report_low_ack(struct ieee80211_sta *sta, int x) } static __inline void -ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr, - uint8_t tid) -{ - TODO(); -} - -static __inline void -ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr, - uint8_t tid) -{ - TODO(); -} - -static __inline void ieee80211_tx_rate_update(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct ieee80211_tx_info *info) { @@ -2488,7 +2286,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(); } @@ -2507,22 +2306,14 @@ ieee80211_beacon_set_cntdwn(struct ieee80211_vif *vif, u8 counter) } static __inline int -ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif) -{ - TODO(); - return (-1); -} - -static __inline int -ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *vht_cap, uint32_t chanwidth, - int x, bool t, int nss) +ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif, uint32_t link_id) { TODO(); return (-1); } static __inline bool -ieee80211_beacon_cntdwn_is_complete(struct ieee80211_vif *vif) +ieee80211_beacon_cntdwn_is_complete(struct ieee80211_vif *vif, uint32_t link_id) { TODO(); return (true); @@ -2535,27 +2326,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 const struct ieee80211_sta_he_cap * -ieee80211_get_he_iftype_cap(const struct ieee80211_supported_band *band, - enum nl80211_iftype type) -{ - TODO(); - return (NULL); -} - -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(); } @@ -2576,7 +2347,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(); } @@ -2618,16 +2389,29 @@ 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(); } static __inline struct ieee80211_key_conf * ieee80211_gtk_rekey_add(struct ieee80211_vif *vif, - struct ieee80211_key_conf *key) + struct ieee80211_key_conf *key, int link_id) { TODO(); return (NULL); @@ -2641,18 +2425,103 @@ 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) +{ + const struct ieee80211_key *k; + const uint8_t *p; + + KASSERT(keyconf != NULL && seq != NULL, ("%s: keyconf %p seq %p\n", + __func__, keyconf, seq)); + 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 < -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_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + 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); + break; + } +} + +static __inline void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *key, int tid, struct ieee80211_key_seq *seq) { TODO(); } +/* -------------------------------------------------------------------------- */ + static __inline void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif, struct cfg80211_wowlan_wakeup *wakeup, gfp_t gfp) @@ -2696,23 +2565,122 @@ ieee80211_vif_is_mld(const struct ieee80211_vif *vif) return (vif->valid_links != 0); } -static __inline const struct ieee80211_sta_he_cap * +static inline const struct ieee80211_sta_he_cap * ieee80211_get_he_iftype_cap_vif(const struct ieee80211_supported_band *band, struct ieee80211_vif *vif) { - TODO(); - return (NULL); + enum nl80211_iftype iftype; + + iftype = ieee80211_vif_type_p2p(vif); + return (ieee80211_get_he_iftype_cap(band, iftype)); } -static __inline const struct ieee80211_sta_eht_cap * +static inline const struct ieee80211_sta_eht_cap * ieee80211_get_eht_iftype_cap_vif(const struct ieee80211_supported_band *band, struct ieee80211_vif *vif) { + enum nl80211_iftype iftype; + + iftype = ieee80211_vif_type_p2p(vif); + return (ieee80211_get_eht_iftype_cap(band, iftype)); +} + +static inline uint32_t +ieee80211_vif_usable_links(const struct ieee80211_vif *vif) +{ + IMPROVE("MLO usable links likely are not just valid"); + return (vif->valid_links); +} + +static inline bool +ieee80211_vif_link_active(const struct ieee80211_vif *vif, uint8_t link_id) +{ + if (ieee80211_vif_is_mld(vif)) + return (vif->active_links & BIT(link_id)); + return (link_id == 0); +} + +static inline void +ieee80211_set_active_links_async(struct ieee80211_vif *vif, + uint32_t new_active_links) +{ TODO(); - return (NULL); +} + +static inline int +ieee80211_set_active_links(struct ieee80211_vif *vif, + uint32_t active_links) +{ + TODO(); + return (-ENXIO); +} + +static inline void +ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp __unused) +{ + IMPROVE("we notify user space by a vap state change eventually"); + linuxkpi_ieee80211_beacon_loss(vif); } #define ieee80211_send_bar(_v, _r, _t, _s) \ linuxkpi_ieee80211_send_bar(_v, _r, _t, _s) +/* -------------------------------------------------------------------------- */ + +int lkpi_80211_update_chandef(struct ieee80211_hw *, + struct ieee80211_chanctx_conf *); + +static inline int +ieee80211_emulate_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf) +{ + int error; + + hw->conf.radar_enabled = chanctx_conf->radar_enabled; + error = lkpi_80211_update_chandef(hw, chanctx_conf); + return (error); +} + +static inline void +ieee80211_emulate_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf __unused) +{ + hw->conf.radar_enabled = false; + lkpi_80211_update_chandef(hw, NULL); +} + +static inline void +ieee80211_emulate_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed __unused) +{ + hw->conf.radar_enabled = chanctx_conf->radar_enabled; + lkpi_80211_update_chandef(hw, chanctx_conf); +} + +static inline int +ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, int n_vifs, + enum ieee80211_chanctx_switch_mode mode __unused) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + int error; + + /* Sanity check. */ + if (n_vifs <= 0) + return (-EINVAL); + if (vifs == NULL || vifs[0].new_ctx == NULL) + return (-EINVAL); + + /* + * What to do if n_vifs > 1? + * Does that make sense for drivers not supporting chanctx? + */ + hw->conf.radar_enabled = vifs[0].new_ctx->radar_enabled; + chanctx_conf = vifs[0].new_ctx; + error = lkpi_80211_update_chandef(hw, chanctx_conf); + return (error); +} + +/* -------------------------------------------------------------------------- */ + #endif /* _LINUXKPI_NET_MAC80211_H */ diff --git a/sys/compat/linuxkpi/common/include/net/page_pool.h b/sys/compat/linuxkpi/common/include/net/page_pool.h index 82dbeff82167..2dc8f74b31f3 100644 --- a/sys/compat/linuxkpi/common/include/net/page_pool.h +++ b/sys/compat/linuxkpi/common/include/net/page_pool.h @@ -29,6 +29,7 @@ #include <linux/kernel.h> /* pr_debug */ #include <linux/types.h> #include <linux/dma-mapping.h> +#include <linux/netdevice.h> struct device; @@ -41,6 +42,7 @@ struct page_pool_params { uint32_t offset; int nid; /* NUMA */ enum dma_data_direction dma_dir; + struct napi_struct *napi; }; struct page_pool { 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/include/video/cmdline.h b/sys/compat/linuxkpi/common/include/video/cmdline.h new file mode 100644 index 000000000000..eaa9a998fda2 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/video/cmdline.h @@ -0,0 +1,44 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Serenity Cyber Security, LLC. + * + * 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 _VIDEO_CMDLINE_H_ +#define _VIDEO_CMDLINE_H_ + +#include <linux/types.h> + +#define CONFIG_VIDEO_CMDLINE + +#if defined(CONFIG_VIDEO_CMDLINE) +const char *video_get_options(const char *name); +#else +static inline const char * +video_get_options(const char *name) +{ + return (NULL); +} +#endif +#endif /* _VIDEO_CMDLINE_H_ */ diff --git a/sys/compat/linuxkpi/common/include/video/vga.h b/sys/compat/linuxkpi/common/include/video/vga.h index e327246cc457..a5012d9e2f3f 100644 --- a/sys/compat/linuxkpi/common/include/video/vga.h +++ b/sys/compat/linuxkpi/common/include/video/vga.h @@ -3,6 +3,9 @@ #ifndef _LINUXKPI_VIDEO_VGA_H #define _LINUXKPI_VIDEO_VGA_H +#include <linux/types.h> +#include <linux/io.h> + #define VGA_MIS_W 0x3c2 #define VGA_SEQ_I 0x3c4 #define VGA_SEQ_D 0x3c5 diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c index d598e9af0050..1d00e8da8f9a 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.c +++ b/sys/compat/linuxkpi/common/src/linux_80211.c @@ -1,6 +1,6 @@ /*- - * Copyright (c) 2020-2023 The FreeBSD Foundation - * Copyright (c) 2020-2022 Bjoern A. Zeeb + * Copyright (c) 2020-2025 The FreeBSD Foundation + * Copyright (c) 2020-2025 Bjoern A. Zeeb * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -39,6 +39,12 @@ * We call the internal versions lxxx (e.g., hw -> lhw, sta -> lsta). */ +/* + * TODO: + * - lots :) + * - HW_CRYPTO: we need a "keystore" and an ordered list for suspend/resume. + */ + #include <sys/param.h> #include <sys/types.h> #include <sys/kernel.h> @@ -46,6 +52,7 @@ #include <sys/malloc.h> #include <sys/module.h> #include <sys/mutex.h> +#include <sys/sbuf.h> #include <sys/socket.h> #include <sys/sysctl.h> #include <sys/queue.h> @@ -67,15 +74,20 @@ #include <net/mac80211.h> #include <linux/workqueue.h> +#include <linux/rculist.h> #include "linux_80211.h" #define LKPI_80211_WME -/* #define LKPI_80211_HW_CRYPTO */ -/* #define LKPI_80211_VHT */ -/* #define LKPI_80211_HT */ +#define LKPI_80211_HW_CRYPTO +#define LKPI_80211_HT +#define LKPI_80211_VHT + #if defined(LKPI_80211_VHT) && !defined(LKPI_80211_HT) #define LKPI_80211_HT #endif +#if defined(LKPI_80211_HT) && !defined(LKPI_80211_HW_CRYPTO) +#define LKPI_80211_HW_CRYPTO +#endif static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "LinuxKPI 80211 compat"); @@ -87,14 +99,24 @@ static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "LinuxKPI 80211 compat"); /* -------------------------------------------------------------------------- */ -/* Keep public for as long as header files are using it too. */ -int linuxkpi_debug_80211; - -#ifdef LINUXKPI_DEBUG_80211 SYSCTL_DECL(_compat_linuxkpi); SYSCTL_NODE(_compat_linuxkpi, OID_AUTO, 80211, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "LinuxKPI 802.11 compatibility layer"); +#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. */ +int linuxkpi_debug_80211; + +#ifdef LINUXKPI_DEBUG_80211 SYSCTL_INT(_compat_linuxkpi_80211, OID_AUTO, debug, CTLFLAG_RWTUN, &linuxkpi_debug_80211, 0, "LinuxKPI 802.11 debug level"); @@ -145,28 +167,301 @@ 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_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); 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) +{ + + switch (bw) { + + case RATE_INFO_BW_20: + return ("20"); + break; + case RATE_INFO_BW_5: + return ("5"); + break; + case RATE_INFO_BW_10: + return ("10"); + break; + case RATE_INFO_BW_40: + return ("40"); + break; + case RATE_INFO_BW_80: + return ("80"); + break; + case RATE_INFO_BW_160: + return ("160"); + break; + case RATE_INFO_BW_HE_RU: + IMPROVE("nl80211_he_ru_alloc"); + return ("HE_RU"); + break; + case RATE_INFO_BW_320: + return ("320"); + break; + case RATE_INFO_BW_EHT_RU: + IMPROVE("nl80211_eht_ru_alloc"); + return ("EHT_RU"); + break; + default: + return ("?"); + break; + } +} + +static void +lkpi_nl80211_sta_info_to_str(struct sbuf *s, const char *prefix, + const uint64_t flags) +{ + int bit, i; + + sbuf_printf(s, "%s %#010jx", prefix, flags); + + i = 0; + for (bit = 0; bit < BITS_PER_TYPE(flags); bit++) { + + if ((flags & BIT_ULL(bit)) == 0) + continue; + +#define EXPAND_CASE(_flag) \ + case NL80211_STA_INFO_ ## _flag: \ + sbuf_printf(s, "%c%s", (i == 0) ? '<' : ',', #_flag); \ + i++; \ + break; + + switch (bit) { + EXPAND_CASE(BEACON_RX) + EXPAND_CASE(BEACON_SIGNAL_AVG) + EXPAND_CASE(BSS_PARAM) + EXPAND_CASE(CHAIN_SIGNAL) + EXPAND_CASE(CHAIN_SIGNAL_AVG) + EXPAND_CASE(CONNECTED_TIME) + EXPAND_CASE(INACTIVE_TIME) + EXPAND_CASE(SIGNAL) + EXPAND_CASE(SIGNAL_AVG) + EXPAND_CASE(STA_FLAGS) + EXPAND_CASE(RX_BITRATE) + EXPAND_CASE(RX_PACKETS) + EXPAND_CASE(RX_BYTES) + EXPAND_CASE(RX_DROP_MISC) + EXPAND_CASE(TX_BITRATE) + EXPAND_CASE(TX_PACKETS) + EXPAND_CASE(TX_BYTES) + EXPAND_CASE(TX_BYTES64) + EXPAND_CASE(RX_BYTES64) + EXPAND_CASE(TX_FAILED) + EXPAND_CASE(TX_RETRIES) + EXPAND_CASE(RX_DURATION) + EXPAND_CASE(TX_DURATION) + EXPAND_CASE(ACK_SIGNAL) + EXPAND_CASE(ACK_SIGNAL_AVG) + default: + sbuf_printf(s, "%c?%d", (i == 0) ? '<' : ',', bit); + break; + } + } +#undef EXPAND_CASE + if (i > 0) + sbuf_printf(s, ">"); + sbuf_printf(s, "\n"); +} + +static int +lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS) +{ + 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); + + 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"); + continue; + } + if (error != 0) { + sbuf_printf(&s, " sta_statistics failed: %d\n", error); + continue; + } + + /* 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); + } + + 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", + (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", + (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", + (uintmax_t)sinfo.tx_bytes, sinfo.tx_packets, sinfo.tx_failed); + 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", + 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", + 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", + 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", + 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", + 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", + 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", + sinfo.txrate.he_dcm, sinfo.txrate.he_gi, sinfo.txrate.he_ru_alloc, + sinfo.txrate.eht_gi); + } + wiphy_unlock(hw->wiphy); + + sbuf_finish(&s); + sbuf_delete(&s); + + return (0); +} + +static enum ieee80211_sta_rx_bw +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_bw 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_bw 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, int *ht_rx_nss) +lkpi_sta_sync_ht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_node *ni) { struct ieee80211vap *vap; uint8_t *ie; struct ieee80211_ht_cap *htcap; int i, rx_nss; - if ((ni->ni_flags & IEEE80211_NODE_HT) == 0) + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0) { + sta->deflink.ht_cap.ht_supported = false; return; - - if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && - IEEE80211_IS_CHAN_HT40(ni->ni_chan)) - sta->deflink.bandwidth = IEEE80211_STA_RX_BW_40; + } sta->deflink.ht_cap.ht_supported = true; @@ -188,43 +483,192 @@ lkpi_sta_sync_ht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni, i sta->deflink.ht_cap.cap = htcap->cap_info; sta->deflink.ht_cap.mcs = htcap->mcs; + /* + * 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 + * MCS sets from the Rx MCS Bitmask; then there is MCS 32 and + * MCS33.. is UEQM. + */ rx_nss = 0; - for (i = 0; i < nitems(htcap->mcs.rx_mask); i++) { - if (htcap->mcs.rx_mask[i]) + for (i = 0; i < 4; i++) { + if (htcap->mcs.rx_mask[i] != 0) rx_nss++; } - if (ht_rx_nss != NULL) - *ht_rx_nss = rx_nss; + if (rx_nss > 0) { + sta->deflink.rx_nss = rx_nss; + } else { + sta->deflink.ht_cap.ht_supported = false; + return; + } - IMPROVE("sta->wme, sta->deflink.agg.max*"); + 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"); + + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU) + sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935; + else + sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839; + sta->deflink.agg.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA; +#ifdef __handled_by_driver__ /* iwlwifi only? actually unused? */ + for (i = 0; i < nitems(sta.deflink.agg.max_tid_amsdu_len); i++) { + sta->deflink.agg.max_tid_amsdu_len[j] = ; + } +#endif } #endif #if defined(LKPI_80211_VHT) static void -lkpi_sta_sync_vht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni, int *vht_rx_nss) -{ - - if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0) +lkpi_sta_sync_vht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_node *ni) +{ + enum ieee80211_sta_rx_bw bw; + uint32_t width; + int rx_nss; + uint16_t rx_mcs_map; + uint8_t mcs; + + if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0 || + !IEEE80211_IS_CHAN_VHT_5GHZ(ni->ni_chan)) { + sta->deflink.vht_cap.vht_supported = false; return; + } - if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) { -#ifdef __notyet__ - if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) { - sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160; /* XXX? */ - } else -#endif - if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan)) - sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160; - else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan)) - sta->deflink.bandwidth = IEEE80211_STA_RX_BW_80; + sta->deflink.vht_cap.vht_supported = true; + + sta->deflink.vht_cap.cap = ni->ni_vhtcap; + sta->deflink.vht_cap.vht_mcs = ni->ni_vht_mcsinfo; + + /* + * If VHT20/40 are selected do not update the bandwidth + * from HT but stya on VHT. + */ + 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) { + /* Deprecated. */ + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: + bw = IEEE80211_STA_RX_BW_160; + break; + default: + /* Check if we do support 160Mhz somehow after all. */ + if ((sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) != 0) + bw = IEEE80211_STA_RX_BW_160; + else + 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: - IMPROVE("VHT sync ni to sta"); - return; + rx_nss = 0; + rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map; + for (int i = 7; i >= 0; i--) { + mcs = rx_mcs_map >> (2 * i); + mcs &= 0x3; + if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + rx_nss = i + 1; + break; + } + } + if (rx_nss > 0) + sta->deflink.rx_nss = rx_nss; + + switch (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) { + case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: + sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454; + break; + case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: + sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991; + break; + case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895: + default: + sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895; + break; + } +} +#endif + +static void +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(vif, sta, ni); +#endif +#if defined(LKPI_80211_VHT) + 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 +lkpi_get_max_rx_chains(struct ieee80211_node *ni) +{ + uint8_t chains; +#if defined(LKPI_80211_HT) || defined(LKPI_80211_VHT) + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + + lsta = ni->ni_drv_data; + sta = LSTA_TO_STA(lsta); +#endif + + chains = 1; +#if defined(LKPI_80211_HT) + IMPROVE("We should factor counting MCS/NSS out for sync and here"); + if (sta->deflink.ht_cap.ht_supported) + chains = MAX(chains, sta->deflink.rx_nss); #endif +#if defined(LKPI_80211_VHT) + if (sta->deflink.vht_cap.vht_supported) + chains = MAX(chains, sta->deflink.rx_nss); +#endif + + return (chains); +} + static void lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni, const char *_f, int _l) @@ -242,7 +686,7 @@ lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni, ieee80211_dump_node(NULL, ni); printf("\ttxq_task txq len %d mtx\n", mbufq_len(&lsta->txq)); printf("\tkc %p state %d added_to_drv %d in_mgd %d\n", - lsta->kc, lsta->state, lsta->added_to_drv, lsta->in_mgd); + &lsta->kc[0], lsta->state, lsta->added_to_drv, lsta->in_mgd); #endif } @@ -250,13 +694,11 @@ static void lkpi_lsta_remove(struct lkpi_sta *lsta, struct lkpi_vif *lvif) { + lockdep_assert_wiphy(lsta->hw->wiphy); - LKPI_80211_LVIF_LOCK(lvif); - KASSERT(lsta->lsta_entry.tqe_prev != NULL, - ("%s: lsta %p lsta_entry.tqe_prev %p ni %p\n", __func__, - lsta, lsta->lsta_entry.tqe_prev, lsta->ni)); - TAILQ_REMOVE(&lvif->lsta_head, lsta, lsta_entry); - LKPI_80211_LVIF_UNLOCK(lvif); + KASSERT(!list_empty(&lsta->lsta_list), + ("%s: lsta %p ni %p\n", __func__, lsta, lsta->ni)); + list_del_init(&lsta->lsta_list); } static struct lkpi_sta * @@ -268,14 +710,13 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], struct ieee80211_vif *vif; struct ieee80211_sta *sta; int band, i, tid; - int ht_rx_nss; - int vht_rx_nss; lsta = malloc(sizeof(*lsta) + hw->sta_data_size, M_LKPI80211, M_NOWAIT | M_ZERO); if (lsta == NULL) return (NULL); + lsta->hw = hw; lsta->added_to_drv = false; lsta->state = IEEE80211_STA_NOTEXIST; /* @@ -337,29 +778,40 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], continue; for (i = 0; i < supband->n_bitrates; i++) { - - IMPROVE("Further supband->bitrates[i]* checks?"); - /* or should we get them from the ni? */ - sta->deflink.supp_rates[band] |= BIT(i); + switch (band) { + case NL80211_BAND_2GHZ: + switch (supband->bitrates[i].bitrate) { + case 240: /* 11g only */ + case 120: /* 11g only */ + case 110: + case 60: /* 11g only */ + case 55: + case 20: + case 10: + sta->deflink.supp_rates[band] |= BIT(i); + break; + } + break; + case NL80211_BAND_5GHZ: + switch (supband->bitrates[i].bitrate) { + case 240: + case 120: + case 60: + sta->deflink.supp_rates[band] |= BIT(i); + break; + } + break; + } } } sta->deflink.smps_mode = IEEE80211_SMPS_OFF; sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20; - sta->deflink.rx_nss = 0; + sta->deflink.rx_nss = 1; - ht_rx_nss = 0; -#if defined(LKPI_80211_HT) - lkpi_sta_sync_ht_from_ni(sta, ni, &ht_rx_nss); -#endif - vht_rx_nss = 0; -#if defined(LKPI_80211_VHT) - lkpi_sta_sync_vht_from_ni(sta, ni, &vht_rx_nss); -#endif + lkpi_sta_sync_from_ni(hw, vif, sta, ni, false); - sta->deflink.rx_nss = MAX(ht_rx_nss, sta->deflink.rx_nss); - sta->deflink.rx_nss = MAX(vht_rx_nss, sta->deflink.rx_nss); - IMPROVE("he, ... smps_mode, .."); + IMPROVE("he, eht, bw_320, ... smps_mode, .."); /* Link configuration. */ IEEE80211_ADDR_COPY(sta->deflink.addr, sta->addr); @@ -367,11 +819,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); @@ -538,35 +992,71 @@ lkpi_opmode_to_vif_type(enum ieee80211_opmode opmode) } #ifdef LKPI_80211_HW_CRYPTO -static uint32_t -lkpi_l80211_to_net80211_cyphers(uint32_t wlan_cipher_suite) +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_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_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"); + default: + return ("??"); + } +} +static uint32_t +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\n", __func__, - wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff); - break; + return (IEEE80211_CRYPTO_BIP_GMAC_256); default: - printf("%s: unknown WLAN Cipher Suite %#08x | %u\n", __func__, - wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff); + 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 @@ -574,18 +1064,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); @@ -708,31 +1222,242 @@ linuxkpi_ieee80211_get_channel(struct wiphy *wiphy, uint32_t freq) #ifdef LKPI_80211_HW_CRYPTO static int -_lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k, - enum set_key_cmd cmd) +lkpi_sta_del_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct lkpi_sta *lsta) +{ + int error; + + if (!lkpi_hwcrypto) + return (0); + + lockdep_assert_wiphy(hw->wiphy); + ieee80211_ref_node(lsta->ni); + + error = 0; + for (ieee80211_keyix keyix = 0; keyix < nitems(lsta->kc); keyix++) { + struct ieee80211_key_conf *kc; + int err; + + if (lsta->kc[keyix] == NULL) + 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, "%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++; + + /* + * If we free the key here we will never be able to get it + * removed from the driver/fw which will likely make us + * crash (firmware). + */ + continue; + } +#ifdef LINUXKPI_DEBUG_80211 + if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO) + 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; + free(kc, M_LKPI80211); + } + ieee80211_free_node(lsta->ni); + 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) { struct ieee80211com *ic; struct lkpi_hw *lhw; struct ieee80211_hw *hw; struct lkpi_vif *lvif; + struct lkpi_sta *lsta; struct ieee80211_vif *vif; struct ieee80211_sta *sta; struct ieee80211_node *ni; struct ieee80211_key_conf *kc; int error; - /* XXX TODO Check (k->wk_flags & IEEE80211_KEY_SWENCRYPT) and don't upload to driver/hw? */ - ic = vap->iv_ic; lhw = ic->ic_softc; hw = LHW_TO_HW(lhw); lvif = VAP_TO_LVIF(vap); + + /* + * 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", + __func__, vap->iv_bss, vap); + return (0); + } + + ni = ieee80211_ref_node(vap->iv_bss); + lsta = ni->ni_drv_data; + if (lsta == NULL) { + ic_printf(ic, "%s: ni %p (%6D) with lsta NULL\n", + __func__, ni, ni->ni_bssid, ":"); + ieee80211_free_node(ni); + return (0); + } + sta = LSTA_TO_STA(lsta); + + if (lsta->kc[k->wk_keyix] == NULL) { +#ifdef LINUXKPI_DEBUG_80211 + if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO) + ic_printf(ic, "%d %lu %s: sta %6D and no key information, " + "keyidx %u wk_macaddr %6D; returning success\n", + 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]; + +#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: " + "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 + vif = LVIF_TO_VIF(lvif); + error = lkpi_80211_mo_set_key(hw, DISABLE_KEY, vif, sta, kc); + if (error != 0) { + 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, "%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, IEEE80211_KEY_FLAG_BITS); +#endif + lsta->kc[k->wk_keyix] = NULL; + free(kc, M_LKPI80211); + error = 1; +out: + ieee80211_free_node(ni); + return (error); +} + +static int +lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct lkpi_sta *lsta; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + 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); + + /* + * 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", + __func__, vap->iv_bss, vap); + return (0); + } + ni = ieee80211_ref_node(vap->iv_bss); + lsta = ni->ni_drv_data; + if (lsta == NULL) { + ic_printf(ic, "%s: ni %p (%6D) with lsta NULL\n", + __func__, ni, ni->ni_bssid, ":"); + ieee80211_free_node(ni); + return (0); + } + sta = LSTA_TO_STA(lsta); - memset(&kc, 0, sizeof(kc)); - kc = malloc(sizeof(*kc) + k->wk_keylen, M_LKPI80211, M_WAITOK | M_ZERO); - kc->cipher = lkpi_net80211_to_l80211_cipher_suite( + 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; + default: + ic_printf(ic, "%s: CIPHER SUITE %#x (%s) not supported\n", + __func__, lcipher, lkpi_cipher_suite_to_name(lcipher)); + IMPROVE(); + ieee80211_free_node(ni); + return (0); + } + + 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. */ + kc->cipher = lcipher; kc->keyidx = k->wk_keyix; #if 0 kc->hw_key_idx = /* set by hw and needs to be passed for TX */; @@ -741,51 +1466,237 @@ _lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k, kc->keylen = k->wk_keylen; memcpy(kc->key, k->wk_key, k->wk_keylen); + if (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) + kc->flags |= IEEE80211_KEY_FLAG_PAIRWISE; + 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(); - return (0); + break; }; + lsta->kc[k->wk_keyix] = kc; - ni = vap->iv_bss; - sta = ieee80211_find_sta(vif, ni->ni_bssid); - if (sta != NULL) { - struct lkpi_sta *lsta; - - lsta = STA_TO_LSTA(sta); - lsta->kc = 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 - error = lkpi_80211_mo_set_key(hw, cmd, vif, sta, kc); + 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) { - /* XXX-BZ leaking kc currently */ - ic_printf(ic, "%s: set_key failed: %d\n", __func__, 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); return (0); - } else { - ic_printf(ic, "%s: set_key succeeded: keyidx %u hw_key_idx %u " - "flags %#10x\n", __func__, - kc->keyidx, kc->hw_key_idx, kc->flags); - return (1); } + +#ifdef LINUXKPI_DEBUG_80211 + if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO) + 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, IEEE80211_KEY_FLAG_BITS); +#endif + + 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); + +#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 + + ieee80211_free_node(ni); + return (1); } -static int -lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +static void +lkpi_iv_key_update_begin(struct ieee80211vap *vap) { + struct ieee80211_node_table *nt; + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_node *ni; + bool icislocked, ntislocked; + + ic = vap->iv_ic; + lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + nt = &ic->ic_sta; + + 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(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 - /* XXX-BZ one day we should replace this iterating over VIFs, or node list? */ - return (_lkpi_iv_key_set_delete(vap, k, DISABLE_KEY)); + /* + * 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; + + /* + * 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 (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 int -lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) + +static void +lkpi_iv_key_update_end(struct ieee80211vap *vap) { + struct ieee80211_node_table *nt; + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + bool icislocked, ntislocked; + + ic = vap->iv_ic; + lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + nt = &ic->ic_sta; + + /* + * 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); - return (_lkpi_iv_key_set_delete(vap, k, SET_KEY)); +#ifdef LINUXKPI_DEBUG_80211 + if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO) + 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 + + /* + * Check under lock; see comment in lkpi_iv_key_update_begin(). + * In case the refcnt gets out of sync locking in net80211 will + * quickly barf as well (trying to unlock a lock not held). + */ + 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. + * ic before nt to avoid a LOR. + */ + if (icislocked) + IEEE80211_LOCK(ic); + if (ntislocked) + IEEE80211_NODE_LOCK(nt); } #endif @@ -894,14 +1805,14 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni, if (linuxkpi_debug_80211 & D80211_TRACE) printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u " "dtim_period %u sync_dtim_count %u sync_tsf %ju " - "sync_device_ts %u bss_changed %#08x\n", + "sync_device_ts %u bss_changed %#010jx\n", __func__, __LINE__, _f, _l, vif->cfg.assoc, vif->cfg.aid, vif->bss_conf.beacon_int, vif->bss_conf.dtim_period, vif->bss_conf.sync_dtim_count, (uintmax_t)vif->bss_conf.sync_tsf, vif->bss_conf.sync_device_ts, - bss_changed); + (uintmax_t)bss_changed); #endif if (vif->bss_conf.beacon_int != ni->ni_intval) { @@ -925,14 +1836,14 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni, if (linuxkpi_debug_80211 & D80211_TRACE) printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u " "dtim_period %u sync_dtim_count %u sync_tsf %ju " - "sync_device_ts %u bss_changed %#08x\n", + "sync_device_ts %u bss_changed %#010jx\n", __func__, __LINE__, _f, _l, vif->cfg.assoc, vif->cfg.aid, vif->bss_conf.beacon_int, vif->bss_conf.dtim_period, vif->bss_conf.sync_dtim_count, (uintmax_t)vif->bss_conf.sync_tsf, vif->bss_conf.sync_device_ts, - bss_changed); + (uintmax_t)bss_changed); #endif return (bss_changed); @@ -954,10 +1865,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); @@ -993,33 +1904,37 @@ lkpi_hw_conf_idle(struct ieee80211_hw *hw, bool new) } } -static void +static enum ieee80211_bss_changed lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif, struct lkpi_hw *lhw) { + enum ieee80211_bss_changed changed; + + changed = 0; sta->aid = 0; if (vif->cfg.assoc) { - struct ieee80211_hw *hw; - enum ieee80211_bss_changed changed; lhw->update_mc = true; lkpi_update_mcast_filter(lhw->ic, true); - changed = 0; vif->cfg.assoc = false; vif->cfg.aid = 0; changed |= BSS_CHANGED_ASSOC; - /* - * This will remove the sta from firmware for iwlwifi. - * So confusing that they use state and flags and ... ^%$%#%$^. - */ IMPROVE(); - hw = LHW_TO_HW(lhw); - lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, - changed); - lkpi_hw_conf_idle(hw, true); + /* + * Executing the bss_info_changed(BSS_CHANGED_ASSOC) with + * assoc = false right away here will remove the sta from + * firmware for iwlwifi. + * We no longer do this but only return the BSS_CHNAGED value. + * The caller is responsible for removing the sta gong to + * IEEE80211_STA_NOTEXIST and then executing the + * bss_info_changed() update. + * See lkpi_sta_run_to_init() for more detailed comment. + */ } + + return (changed); } static void @@ -1057,6 +1972,83 @@ lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta, } } +/* + * On the way down from RUN -> ASSOC -> AUTH we may send a DISASSOC or DEAUTH + * packet. The problem is that the state machine functions tend to hold the + * LHW lock which will prevent lkpi_80211_txq_tx_one() from sending the packet. + * We call this after dropping the ic lock and before acquiring the LHW lock. + * we make sure no further packets are queued and if they are queued the task + * will finish or be cancelled. At the end if a packet is left we manually + * send it. scan_to_auth() would re-enable sending if the lsta would be + * re-used. + */ +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; + + /* 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); + lsta->txq_ready = false; + LKPI_80211_LSTA_TXQ_UNLOCK(lsta); + + while (taskqueue_cancel(taskqueue_thread, &lsta->txq_task, NULL) != 0) + taskqueue_drain(taskqueue_thread, &lsta->txq_task); + + LKPI_80211_LSTA_TXQ_LOCK(lsta); + len = mbufq_len(&lsta->txq); + if (len <= 0) { + LKPI_80211_LSTA_TXQ_UNLOCK(lsta); + return; + } + + mbufq_init(&mq, IFQ_MAXLEN); + mbufq_concat(&mq, &lsta->txq); + LKPI_80211_LSTA_TXQ_UNLOCK(lsta); + + m = mbufq_dequeue(&mq); + while (m != NULL) { + lkpi_80211_txq_tx_one(lsta, m); + m = mbufq_dequeue(&mq); + } +} + + +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 @@ -1074,7 +2066,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int { struct linuxkpi_ieee80211_channel *chan; struct lkpi_chanctx *lchanctx; - struct ieee80211_chanctx_conf *conf; + struct ieee80211_chanctx_conf *chanctx_conf; struct lkpi_hw *lhw; struct ieee80211_hw *hw; struct lkpi_vif *lvif; @@ -1085,6 +2077,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. @@ -1131,64 +2124,72 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int lvif, vap, vap->iv_bss, lvif->lvif_bss, (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL, lvif->lvif_bss_synched); + LKPI_80211_LVIF_UNLOCK(lvif); + ieee80211_free_node(ni); /* Error handling for the local ni. */ return (EBUSY); } 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) { - conf = vif->chanctx_conf; - lchanctx = CHANCTX_CONF_TO_LCHANCTX(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 { /* Keep separate alloc as in Linux this is rcu managed? */ lchanctx = malloc(sizeof(*lchanctx) + hw->chanctx_data_size, M_LKPI80211, M_WAITOK | M_ZERO); - conf = &lchanctx->conf; + chanctx_conf = &lchanctx->chanctx_conf; } - conf->rx_chains_dynamic = 1; - conf->rx_chains_static = 1; - conf->radar_enabled = + chanctx_conf->rx_chains_static = 1; + chanctx_conf->rx_chains_dynamic = 1; + chanctx_conf->radar_enabled = (chan->flags & IEEE80211_CHAN_RADAR) ? true : false; - conf->def.chan = chan; - conf->def.width = NL80211_CHAN_WIDTH_20_NOHT; - conf->def.center_freq1 = chan->center_freq; - conf->def.center_freq2 = 0; + chanctx_conf->def.chan = chan; + chanctx_conf->def.width = NL80211_CHAN_WIDTH_20_NOHT; + chanctx_conf->def.center_freq1 = ieee80211_get_channel_center_freq1(ni->ni_chan); + chanctx_conf->def.center_freq2 = ieee80211_get_channel_center_freq2(ni->ni_chan); 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)) { - conf->def.width = NL80211_CHAN_WIDTH_40; - } else - conf->def.width = NL80211_CHAN_WIDTH_20; + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) + chanctx_conf->def.width = NL80211_CHAN_WIDTH_40; + else + chanctx_conf->def.width = NL80211_CHAN_WIDTH_20; } #endif #ifdef LKPI_80211_VHT - if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) { -#ifdef __notyet__ - if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) { - conf->def.width = NL80211_CHAN_WIDTH_80P80; - conf->def.center_freq2 = 0; /* XXX */ - } else -#endif - if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan)) - conf->def.width = NL80211_CHAN_WIDTH_160; + 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 if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan)) - conf->def.width = NL80211_CHAN_WIDTH_80; + chanctx_conf->def.width = NL80211_CHAN_WIDTH_80; } #endif + chanctx_conf->rx_chains_dynamic = lkpi_get_max_rx_chains(ni); /* Responder ... */ - conf->min_def.chan = chan; - conf->min_def.width = NL80211_CHAN_WIDTH_20_NOHT; - conf->min_def.center_freq1 = chan->center_freq; - conf->min_def.center_freq2 = 0; - IMPROVE("currently 20_NOHT min_def only"); +#if 0 + chanctx_conf->min_def.chan = chanctx_conf->def.chan; + chanctx_conf->min_def.width = NL80211_CHAN_WIDTH_20_NOHT; +#ifdef LKPI_80211_HT + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) || IEEE80211_IS_CHAN_VHT(ni->ni_chan)) + chanctx_conf->min_def.width = NL80211_CHAN_WIDTH_20; +#endif + chanctx_conf->min_def.center_freq1 = chanctx_conf->def.center_freq1; + chanctx_conf->min_def.center_freq2 = chanctx_conf->def.center_freq2; +#else + chanctx_conf->min_def = chanctx_conf->def; +#endif /* Set bss info (bss_info_changed). */ bss_changed = 0; @@ -1208,49 +2209,43 @@ 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; changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; - lkpi_80211_mo_change_chanctx(hw, conf, changed); + lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed); } else { - error = lkpi_80211_mo_add_chanctx(hw, conf); + error = lkpi_80211_mo_add_chanctx(hw, chanctx_conf); if (error == 0 || error == EOPNOTSUPP) { - vif->bss_conf.chandef.chan = conf->def.chan; - vif->bss_conf.chandef.width = conf->def.width; - vif->bss_conf.chandef.center_freq1 = - conf->def.center_freq1; -#ifdef LKPI_80211_HT - if (vif->bss_conf.chandef.width == NL80211_CHAN_WIDTH_40) { - /* Note: it is 10 not 20. */ - if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) - vif->bss_conf.chandef.center_freq1 += 10; - else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) - vif->bss_conf.chandef.center_freq1 -= 10; - } -#endif - vif->bss_conf.chandef.center_freq2 = - conf->def.center_freq2; + vif->bss_conf.chanreq.oper.chan = chanctx_conf->def.chan; + vif->bss_conf.chanreq.oper.width = chanctx_conf->def.width; + vif->bss_conf.chanreq.oper.center_freq1 = + chanctx_conf->def.center_freq1; + vif->bss_conf.chanreq.oper.center_freq2 = + chanctx_conf->def.center_freq2; } else { ic_printf(vap->iv_ic, "%s:%d: mo_add_chanctx " "failed: %d\n", __func__, __LINE__, error); goto out; } - vif->bss_conf.chanctx_conf = 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) error = lkpi_80211_mo_assign_vif_chanctx(hw, vif, - &vif->bss_conf, conf); + &vif->bss_conf, chanctx_conf); if (error == EOPNOTSUPP) error = 0; if (error != 0) { ic_printf(vap->iv_ic, "%s:%d: mo_assign_vif_chanctx " "failed: %d\n", __func__, __LINE__, error); - lkpi_80211_mo_remove_chanctx(hw, conf); - lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf); + 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; } @@ -1270,29 +2265,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; - LKPI_80211_LVIF_LOCK(lvif); - /* Re-check given (*iv_update_bss) could have happened. */ - /* XXX-BZ KASSERT later? or deal as error? */ - if (lvif->lvif_bss_synched || lvif->lvif_bss != NULL) - ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p " - "lvif_bss->ni %p synched %d, ni %p lsta %p\n", __func__, __LINE__, - lvif, vap, vap->iv_bss, lvif->lvif_bss, - (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL, - lvif->lvif_bss_synched, ni, lsta); - - /* - * Reference the ni for this cache of lsta/ni on lvif->lvif_bss - * essentially out lsta version of the iv_bss. - * Do NOT use iv_bss here anymore as that may have diverged from our - * function local ni already and would lead to inconsistencies. - */ - ieee80211_ref_node(ni); - lvif->lvif_bss = lsta; - lvif->lvif_bss_synched = true; - /* Insert the [l]sta into the list of known stations. */ - TAILQ_INSERT_TAIL(&lvif->lsta_head, lsta, lsta_entry); - LKPI_80211_LVIF_UNLOCK(lvif); + list_add_tail(&lsta->lsta_list, &lvif->lsta_list); /* Add (or adjust) sta and change state (from NOTEXIST) to NONE. */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); @@ -1338,11 +2312,67 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int * (ideally we'd do that on a callback for something else ...) */ + wiphy_unlock(hw->wiphy); + IEEE80211_LOCK(vap->iv_ic); + + LKPI_80211_LVIF_LOCK(lvif); + /* Re-check given (*iv_update_bss) could have happened while we were unlocked. */ + if (lvif->lvif_bss_synched || lvif->lvif_bss != NULL || + lsta->ni != vap->iv_bss) + ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p " + "lvif_bss->ni %p synched %d, ni %p lsta %p\n", __func__, __LINE__, + lvif, vap, vap->iv_bss, lvif->lvif_bss, + (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL, + lvif->lvif_bss_synched, ni, lsta); + + /* + * Reference the "ni" for caching the lsta/ni in lvif->lvif_bss. + * Given we cache lsta we use lsta->ni instead of ni here (even though + * lsta->ni == ni) to be distinct from the rest of the code where we do + * assume that ni == vap->iv_bss which it may or may not be. + * So do NOT use iv_bss here anymore as that may have diverged from our + * function local ni already while ic was unlocked and would lead to + * inconsistencies. Go and see if we lost a race and do not update + * lvif_bss_synched in that case. + */ + ieee80211_ref_node(lsta->ni); + lvif->lvif_bss = lsta; + if (lsta->ni == vap->iv_bss) { + lvif->lvif_bss_synched = synched = true; + } else { + /* Set to un-synched no matter what. */ + 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 + * AUTH lkpi_sta_a_to_a() will error, lkpi_sta_auth_to_{scan,init}() + * will take the lvif->lvif_bss node down eventually. + * What happens with the vap->iv_bss node will entirely be up + * 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: /* - * Release the reference that keop the ni stable locally + * Release the reference that kept the ni stable locally * during the work of this function. */ if (ni != NULL) @@ -1390,13 +2420,13 @@ 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); /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, true, true); + lkpi_wake_tx_queues(hw, sta, false, true); /* flush, no drop */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); @@ -1449,24 +2479,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 *conf; - - 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. */ - - /* Remove chan ctx. */ - lkpi_80211_mo_remove_chanctx(hw, conf); - lchanctx = CHANCTX_CONF_TO_LCHANCTX(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); } @@ -1499,7 +2515,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? */ @@ -1552,7 +2568,7 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in } /* Wake tx queue to get packet out. */ - lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), true, true); + lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), false, true); /* * <twiddle> .. we end up in "assoc_to_run" @@ -1565,7 +2581,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); } @@ -1588,7 +2604,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? */ @@ -1632,7 +2648,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); @@ -1658,7 +2674,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 @@ -1689,14 +2705,15 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i !lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); prep_tx_info.duration = PREP_TX_INFO_DURATION; + prep_tx_info.was_assoc = true; lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); 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. */ + /* Call iv_newstate first so we get potential DEAUTH packet out. */ error = lvif->iv_newstate(vap, nstate, arg); if (error != 0) { ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) " @@ -1705,12 +2722,16 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i } IEEE80211_UNLOCK(vap->iv_ic); - LKPI_80211_LHW_LOCK(lhw); + + /* Ensure the packets get out. */ + lkpi_80211_flush_tx(lhw, lsta); + + wiphy_lock(hw->wiphy); lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, true, true); + lkpi_wake_tx_queues(hw, sta, false, true); /* flush, no drop */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); @@ -1719,6 +2740,7 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i if (lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); prep_tx_info.success = false; + prep_tx_info.was_assoc = true; lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); lsta->in_mgd = false; } @@ -1742,16 +2764,11 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i goto out; } - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); + /* See comment in lkpi_sta_run_to_init(). */ + bss_changed = 0; + bss_changed |= lkpi_disassoc(sta, vif, lhw); - /* Update bss info (bss_info_changed) (assoc, aid, ..). */ - /* - * We need to do this now, before sta changes to IEEE80211_STA_NOTEXIST - * as otherwise drivers (iwlwifi at least) will silently not remove - * the sta from the firmware and when we will add a new one trigger - * a fw assert. - */ - lkpi_disassoc(sta, vif, lhw); + lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* Adjust sta and change state (from NONE) to NOTEXIST. */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); @@ -1768,7 +2785,6 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */ IMPROVE("Any bss_info changes to announce?"); - bss_changed = 0; vif->bss_conf.qos = 0; bss_changed |= BSS_CHANGED_QOS; vif->cfg.ssid_len = 0; @@ -1791,25 +2807,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 *conf; - - 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. */ - - /* Remove chan ctx. */ - lkpi_80211_mo_remove_chanctx(hw, conf); - lchanctx = CHANCTX_CONF_TO_LCHANCTX(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); @@ -1868,7 +2870,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? */ @@ -1944,7 +2946,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) @@ -1977,10 +2978,14 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int IMPROVE("net80211 does not consider node authorized"); } -#if defined(LKPI_80211_HT) IMPROVE("Is this the right spot, has net80211 done all updates already?"); - lkpi_sta_sync_ht_from_ni(sta, ni, NULL); -#endif + 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)); @@ -2007,7 +3012,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); out: - LKPI_80211_LHW_UNLOCK(lhw); + wiphy_unlock(hw->wiphy); IEEE80211_LOCK(vap->iv_ic); return (error); } @@ -2066,7 +3071,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); @@ -2076,11 +3081,12 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int !lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); prep_tx_info.duration = PREP_TX_INFO_DURATION; + prep_tx_info.was_assoc = true; lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); 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. */ @@ -2092,12 +3098,16 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int } IEEE80211_UNLOCK(vap->iv_ic); - LKPI_80211_LHW_LOCK(lhw); + + /* Ensure the packets get out. */ + lkpi_80211_flush_tx(lhw, lsta); + + wiphy_lock(hw->wiphy); lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, true, true); + lkpi_wake_tx_queues(hw, sta, false, true); /* flush, no drop */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); @@ -2106,6 +3116,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int if (lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); prep_tx_info.success = false; + prep_tx_info.was_assoc = true; lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); lsta->in_mgd = false; } @@ -2133,6 +3144,22 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int lkpi_lsta_dump(lsta, ni, __func__, __LINE__); +#ifdef LKPI_80211_HW_CRYPTO + if (lkpi_hwcrypto) { + error = lkpi_sta_del_keys(hw, vif, lsta); + if (error != 0) { + ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys " + "failed: %d\n", __func__, __LINE__, error); + /* + * Either drv/fw will crash or cleanup itself, + * otherwise net80211 will delete the keys (at a + * less appropriate time). + */ + /* goto out; */ + } + } +#endif + /* Update sta_state (ASSOC to AUTH). */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not " @@ -2153,7 +3180,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); @@ -2179,7 +3206,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 @@ -2210,11 +3237,12 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int !lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); prep_tx_info.duration = PREP_TX_INFO_DURATION; + prep_tx_info.was_assoc = true; lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); 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. */ @@ -2226,12 +3254,16 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int } IEEE80211_UNLOCK(vap->iv_ic); - LKPI_80211_LHW_LOCK(lhw); + + /* Ensure the packets get out. */ + lkpi_80211_flush_tx(lhw, lsta); + + wiphy_lock(hw->wiphy); lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, true, true); + lkpi_wake_tx_queues(hw, sta, false, true); /* flush, no drop */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); @@ -2240,6 +3272,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int if (lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); prep_tx_info.success = false; + prep_tx_info.was_assoc = true; lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); lsta->in_mgd = false; } @@ -2265,6 +3298,22 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int lkpi_lsta_dump(lsta, ni, __func__, __LINE__); +#ifdef LKPI_80211_HW_CRYPTO + if (lkpi_hwcrypto) { + error = lkpi_sta_del_keys(hw, vif, lsta); + if (error != 0) { + ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys " + "failed: %d\n", __func__, __LINE__, error); + /* + * Either drv/fw will crash or cleanup itself, + * otherwise net80211 will delete the keys (at a + * less appropriate time). + */ + /* goto out; */ + } + } +#endif + /* Update sta_state (ASSOC to AUTH). */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not " @@ -2289,14 +3338,33 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int goto out; } - lkpi_lsta_dump(lsta, ni, __func__, __LINE__); - - /* Update bss info (bss_info_changed) (assoc, aid, ..). */ + bss_changed = 0; /* + * Start updating bss info (bss_info_changed) (assoc, aid, ..). + * * One would expect this to happen when going off AUTHORIZED. - * See comment there; removes the sta from fw. + * See comment there; removes the sta from fw if not careful + * (bss_info_changed() change is executed right away). + * + * We need to do this now, before sta changes to IEEE80211_STA_NOTEXIST + * as otherwise drivers (iwlwifi at least) will silently not remove + * the sta from the firmware and when we will add a new one trigger + * a fw assert. + * + * The order which works best so far avoiding early removal or silent + * non-removal seems to be (for iwlwifi::mld-mac80211.c cases; + * the iwlwifi:mac80211.c case still to be tested): + * 1) lkpi_disassoc(): set vif->cfg.assoc = false (aid=0 side effect here) + * 2) call the last sta_state update -> IEEE80211_STA_NOTEXIST + * (removes the sta given assoc is false) + * 3) add the remaining BSS_CHANGED changes and call bss_info_changed() + * 4) call unassign_vif_chanctx + * 5) call lkpi_hw_conf_idle + * 6) call remove_chanctx */ - lkpi_disassoc(sta, vif, lhw); + bss_changed |= lkpi_disassoc(sta, vif, lhw); + + lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* Adjust sta and change state (from NONE) to NOTEXIST. */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); @@ -2310,15 +3378,19 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int goto out; } + lkpi_lsta_remove(lsta, lvif); + lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */ IMPROVE("Any bss_info changes to announce?"); - bss_changed = 0; vif->bss_conf.qos = 0; bss_changed |= BSS_CHANGED_QOS; vif->cfg.ssid_len = 0; memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid)); bss_changed |= BSS_CHANGED_BSSID; + vif->bss_conf.use_short_preamble = false; + vif->bss_conf.qos = false; + /* XXX BSS_CHANGED_???? */ lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); LKPI_80211_LVIF_LOCK(lvif); @@ -2326,7 +3398,6 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int lvif->lvif_bss = NULL; lvif->lvif_bss_synched = false; LKPI_80211_LVIF_UNLOCK(lvif); - lkpi_lsta_remove(lsta, lvif); /* * The very last release the reference on the ni for the ni/lsta on * lvif->lvif_bss. Upon return from this both ni and lsta are invalid @@ -2336,25 +3407,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 *conf; - - 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. */ - - /* Remove chan ctx. */ - lkpi_80211_mo_remove_chanctx(hw, conf); - lchanctx = CHANCTX_CONF_TO_LCHANCTX(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); @@ -2558,6 +3615,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)); @@ -2584,12 +3644,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; @@ -2604,7 +3662,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); @@ -2619,6 +3676,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); @@ -2626,12 +3684,46 @@ 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 */ } +/* + * Change link-layer address on the vif (if the vap is not started/"UP"). + * This can happen if a user changes 'ether' using ifconfig. + * The code is based on net80211/ieee80211_freebsd.c::wlan_iflladdr() but + * we do use a per-[l]vif event handler to be sure we exist as we + * cannot assume that from every vap derives a vif and we have a hard + * time checking based on net80211 information. + * Should this ever become a real problem we could add a callback function + * to wlan_iflladdr() to be set optionally but that would be for a + * single-consumer (or needs a list) -- was just too complicated for an + * otherwise perfect mechanism FreeBSD already provides. + */ +static void +lkpi_vif_iflladdr(void *arg, struct ifnet *ifp) +{ + struct epoch_tracker et; + struct ieee80211_vif *vif; + + NET_EPOCH_ENTER(et); + /* NB: identify vap's by if_transmit; left as an extra check. */ + if (if_gettransmitfn(ifp) != ieee80211_vap_transmit || + (if_getflags(ifp) & IFF_UP) != 0) { + NET_EPOCH_EXIT(et); + return; + } + + vif = arg; + IEEE80211_ADDR_COPY(vif->bss_conf.addr, if_getlladdr(ifp)); + NET_EPOCH_EXIT(et); +} + static struct ieee80211vap * lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, @@ -2645,6 +3737,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], struct ieee80211_vif *vif; struct ieee80211_tx_queue_params txqp; enum ieee80211_bss_changed changed; + struct sysctl_oid *node; size_t len; int error, i; uint16_t ac; @@ -2660,8 +3753,9 @@ 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); - TAILQ_INIT(&lvif->lsta_head); + INIT_LIST_HEAD(&lvif->lsta_list); lvif->lvif_bss = NULL; + refcount_init(&lvif->nt_unlocked, 0); lvif->lvif_bss_synched = false; vap = LVIF_TO_VAP(lvif); @@ -2676,12 +3770,14 @@ 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); + lvif->lvif_ifllevent = EVENTHANDLER_REGISTER(iflladdr_event, + lkpi_vif_iflladdr, vif, EVENTHANDLER_PRI_ANY); vif->bss_conf.link_id = 0; /* Non-MLO operation. */ - vif->bss_conf.chandef.width = NL80211_CHAN_WIDTH_20_NOHT; + vif->bss_conf.chanreq.oper.width = NL80211_CHAN_WIDTH_20_NOHT; vif->bss_conf.use_short_preamble = false; /* vap->iv_flags IEEE80211_F_SHPREAMBLE */ vif->bss_conf.use_short_slot = false; /* vap->iv_flags IEEE80211_F_SHSLOT */ vif->bss_conf.qos = false; @@ -2757,7 +3853,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)); @@ -2770,7 +3866,7 @@ 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); @@ -2787,13 +3883,15 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], lvif->iv_update_bss = vap->iv_update_bss; vap->iv_update_bss = lkpi_iv_update_bss; - /* Key management. */ - if (lhw->ops->set_key != NULL) { #ifdef LKPI_80211_HW_CRYPTO + /* Key management. */ + if (lkpi_hwcrypto && lhw->ops->set_key != NULL) { vap->iv_key_set = lkpi_iv_key_set; vap->iv_key_delete = lkpi_iv_key_delete; -#endif + vap->iv_key_update_begin = lkpi_iv_key_update_begin; + vap->iv_key_update_end = lkpi_iv_key_update_end; } +#endif #ifdef LKPI_80211_HT /* Stay with the iv_ampdu_rxmax,limit / iv_ampdu_density defaults until later. */ @@ -2805,6 +3903,19 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); +#ifdef LKPI_80211_HT + /* + * 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)) + 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); hw->conf.listen_interval = hw->max_listen_interval; @@ -2816,6 +3927,20 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], hw->wiphy->rts_threshold = vap->iv_rtsthreshold; lkpi_80211_mo_set_rts_threshold(hw, vap->iv_rtsthreshold); /* any others? */ + + /* Add per-VIF/VAP sysctls. */ + sysctl_ctx_init(&lvif->sysctl_ctx); + + node = SYSCTL_ADD_NODE(&lvif->sysctl_ctx, + SYSCTL_CHILDREN(&sysctl___compat_linuxkpi_80211), + OID_AUTO, if_name(vap->iv_ifp), + CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE, NULL, "VIF Information"); + + SYSCTL_ADD_PROC(&lvif->sysctl_ctx, + SYSCTL_CHILDREN(node), OID_AUTO, "dump_stas", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, lvif, 0, + lkpi_80211_dump_stas, "A", "Dump sta statistics of this vif"); + IMPROVE(); return (vap); @@ -2853,6 +3978,11 @@ lkpi_ic_vap_delete(struct ieee80211vap *vap) lhw = ic->ic_softc; hw = LHW_TO_HW(lhw); + EVENTHANDLER_DEREGISTER(iflladdr_event, lvif->lvif_ifllevent); + + /* Clear up per-VIF/VAP sysctls. */ + sysctl_ctx_free(&lvif->sysctl_ctx); + LKPI_80211_LHW_LVIF_LOCK(lhw); TAILQ_REMOVE(&lhw->lvif_head, lvif, lvif_entry); LKPI_80211_LHW_LVIF_UNLOCK(lhw); @@ -2865,7 +3995,7 @@ lkpi_ic_vap_delete(struct ieee80211vap *vap) lkpi_80211_mo_remove_interface(hw, vif); /* Single VAP, so we can do this here. */ - lkpi_80211_mo_stop(hw); + lkpi_80211_mo_stop(hw, false); /* XXX SUSPEND */ mtx_destroy(&lvif->mtx); free(lvif, M_80211_VAP); @@ -2898,8 +4028,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; @@ -2907,13 +4037,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); @@ -2922,10 +4050,10 @@ lkpi_ic_parent(struct ieee80211com *ic) start_all = true; } else { #ifdef HW_START_STOP - lkpi_80211_mo_stop(hw); + lkpi_80211_mo_stop(hw, false); /* XXX SUSPEND */ #endif } - LKPI_80211_LHW_UNLOCK(lhw); + wiphy_unlock(hw->wiphy); /* IEEE80211_LOCK(ic); */ if (start_all) @@ -2996,12 +4124,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; } @@ -3025,7 +4167,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, @@ -3106,7 +4249,6 @@ sw_scan: /* XXX want to adjust ss end time/ maxdwell? */ } else { - struct ieee80211_channel *c; struct ieee80211_scan_request *hw_req; struct linuxkpi_ieee80211_channel *lc, **cpp; struct cfg80211_ssid *ssids; @@ -3123,14 +4265,31 @@ sw_scan: band_mask = 0; nchan = 0; - for (i = ss->ss_next; i < ss->ss_last; i++) { - nchan++; - band = lkpi_net80211_chan_to_nl80211_band( - ss->ss_chans[ss->ss_next + i]); - band_mask |= (1 << band); - } - - if (!ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) { + if (ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) { +#if 0 /* 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( + ss->ss_chans[ss->ss_next + i]); + band_mask |= (1 << band); + } +#else + /* Instead we scan for all channels all the time. */ + for (band = 0; band < NUM_NL80211_BANDS; band++) { + switch (band) { + case NL80211_BAND_2GHZ: + case NL80211_BAND_5GHZ: + break; + default: + continue; + } + if (hw->wiphy->bands[band] != NULL) { + nchan += hw->wiphy->bands[band]->n_channels; + band_mask |= (1 << band); + } + } +#endif + } else { IMPROVE("individual band scans not yet supported, only scanning first band"); /* In theory net80211 should drive this. */ /* Probably we need to add local logic for now; @@ -3184,9 +4343,11 @@ sw_scan: *(cpp + i) = (struct linuxkpi_ieee80211_channel *)(lc + i); } +#if 0 /* Avoid net80211 scan lists until it has proper scan offload support. */ for (i = 0; i < nchan; i++) { - c = ss->ss_chans[ss->ss_next + 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 */ @@ -3195,6 +4356,27 @@ sw_scan: /* lc-> ... */ lc++; } +#else + for (band = 0; band < NUM_NL80211_BANDS; band++) { + struct ieee80211_supported_band *supband; + struct linuxkpi_ieee80211_channel *channels; + + /* Band disabled for scanning? */ + if ((band_mask & (1 << band)) == 0) + continue; + + /* Nothing to scan in band? */ + supband = hw->wiphy->bands[band]; + if (supband == NULL || supband->n_channels == 0) + continue; + + channels = supband->channels; + for (i = 0; i < supband->n_channels; i++) { + *lc = channels[i]; + lc++; + } + } +#endif hw_req->req.n_ssids = ssid_count; if (hw_req->req.n_ssids > 0) { @@ -3411,7 +4593,7 @@ lkpi_ic_set_channel(struct ieee80211com *ic) hw = LHW_TO_HW(lhw); cfg80211_chandef_create(&hw->conf.chandef, chan, #ifdef LKPI_80211_HT - (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 : + (ic->ic_flags_ht & IEEE80211_FHT_HT) ? NL80211_CHAN_HT20 : #endif NL80211_CHAN_NO_HT); @@ -3535,26 +4717,55 @@ 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); +#if 0 + if (!lsta->added_to_drv || !lsta->txq_ready) { +#else + /* + * Backout this part of 886653492945f which breaks rtw88 or + * in general drivers without (*sta_state)() but only the + * legacy fallback to (*sta_add)(). + */ 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); @@ -3568,13 +4779,193 @@ 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_tkip(struct ieee80211_key *k, + struct ieee80211_key_conf *kc, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + uint32_t hlen, hdrlen; + uint8_t *p; + + /* + * TKIP only happens on data. + */ + hdr = (void *)skb->data; + if (!ieee80211_is_data_present(hdr->frame_control)) + return (0); + + /* + * "enmic" (though we do not do that). + */ + /* any conditions to not apply this? */ + if (skb_tailroom(skb) < k->wk_cipher->ic_miclen) + return (ENOBUFS); + + p = skb_put(skb, k->wk_cipher->ic_miclen); + if ((kc->flags & IEEE80211_KEY_FLAG_PUT_MIC_SPACE) != 0) + goto encrypt; + + /* + * (*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); +} + +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; + + /* + * Check if we have anythig 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 && + /* MFP */ + !((kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) != 0 && + ieee80211_is_mgmt(hdr->frame_control))) + return (0); + + hlen = k->wk_cipher->ic_header; + if (skb_headroom(skb) < hlen) + return (ENOBUFS); + + 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); + + 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 lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) { struct ieee80211_node *ni; -#ifndef LKPI_80211_HW_CRYPTO struct ieee80211_frame *wh; -#endif struct ieee80211_key *k; struct sk_buff *skb; struct ieee80211com *ic; @@ -3589,7 +4980,8 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) struct ieee80211_hdr *hdr; struct lkpi_txq *ltxq; void *buf; - uint8_t ac, tid; + ieee80211_keyix keyix; + uint8_t ac, tid, tailroom; M_ASSERTPKTHDR(m); #ifdef LINUXKPI_DEBUG_80211 @@ -3599,20 +4991,30 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) ni = lsta->ni; k = NULL; -#ifndef LKPI_80211_HW_CRYPTO - /* Encrypt the frame if need be; XXX-BZ info->control.hw_key. */ + keyix = IEEE80211_KEYIX_NONE; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - /* Retrieve key for TX && do software encryption. */ - k = ieee80211_crypto_encap(ni, m); - if (k == NULL) { - ieee80211_free_node(ni); - m_freem(m); - return; + +#ifdef LKPI_80211_HW_CRYPTO + if (lkpi_hwcrypto) { + k = ieee80211_crypto_get_txkey(ni, m); + if (k != NULL && lsta->kc[k->wk_keyix] != NULL) + keyix = k->wk_keyix; } - } #endif + /* Encrypt the frame if need be. */ + if (keyix == IEEE80211_KEYIX_NONE) { + /* Retrieve key for TX && do software encryption. */ + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + ieee80211_free_node(ni); + m_freem(m); + return; + } + } + } + ic = ni->ni_ic; lhw = ic->ic_softc; hw = LHW_TO_HW(lhw); @@ -3637,15 +5039,37 @@ 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) { - ic_printf(ic, "ERROR %s: skb alloc failed\n", __func__); + static uint8_t skb_alloc_failures = 0; + + if (skb_alloc_failures++ == 0) { + int tid; + + sta = LSTA_TO_STA(lsta); + ic_printf(ic, "ERROR %s: skb alloc failed %d + %d, lsta %p sta %p ni %p\n", + __func__, hw->extra_tx_headroom, m->m_pkthdr.len, lsta, sta, ni); + for (tid = 0; tid < nitems(sta->txq); tid++) { + if (sta->txq[tid] == NULL) + continue; + ltxq = TXQ_TO_LTXQ(sta->txq[tid]); + ic_printf(ic, " tid %d ltxq %p seen_dequeue %d stopped %d skb_queue_len %u\n", + tid, ltxq, ltxq->seen_dequeue, ltxq->stopped, skb_queue_len(<xq->skbq)); + } + } ieee80211_free_node(ni); m_freem(m); return; @@ -3708,7 +5132,19 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) sta = LSTA_TO_STA(lsta); #ifdef LKPI_80211_HW_CRYPTO - info->control.hw_key = lsta->kc; + if (lkpi_hwcrypto && keyix != IEEE80211_KEYIX_NONE) { + int error; + + error = lkpi_hw_crypto_prepare(lsta, k, skb); + if (error != 0) { + /* + * We only have to free the skb which will free the + * mbuf and release the reference on the ni. + */ + dev_kfree_skb(skb); + return; + } + } #endif IMPROVE(); @@ -3733,17 +5169,19 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) skb_queue_tail(<xq->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(<xq->skbq), ltxq->txq.ac, ltxq->txq.tid, ac, skb->priority, skb->qmap); #endif LKPI_80211_LTXQ_UNLOCK(ltxq); + wiphy_lock(hw->wiphy); lkpi_80211_mo_wake_tx_queue(hw, <xq->txq); + wiphy_unlock(hw->wiphy); return; ops_tx: @@ -3756,7 +5194,9 @@ ops_tx: #endif memset(&control, 0, sizeof(control)); control.sta = sta; + wiphy_lock(hw->wiphy); lkpi_80211_mo_tx(hw, &control, skb); + wiphy_unlock(hw->wiphy); } static void @@ -3765,6 +5205,7 @@ lkpi_80211_txq_task(void *ctx, int pending) struct lkpi_sta *lsta; struct mbufq mq; struct mbuf *m; + bool shall_tx; lsta = ctx; @@ -3780,9 +5221,28 @@ lkpi_80211_txq_task(void *ctx, int pending) LKPI_80211_LSTA_TXQ_LOCK(lsta); /* * Do not re-check lsta->txq_ready here; we may have a pending - * disassoc frame still. + * disassoc/deauth frame still. On the contrary if txq_ready is + * false we do not have a valid sta anymore in the firmware so no + * point to try to TX. + * We also use txq_ready as a semaphore and will drain the txq manually + * if needed on our way towards SCAN/INIT in the state machine. + */ +#if 0 + shall_tx = lsta->added_to_drv && lsta->txq_ready; +#else + /* + * Backout this part of 886653492945f which breaks rtw88 or + * in general drivers without (*sta_state)() but only the + * legacy fallback to (*sta_add)(). + */ + shall_tx = lsta->txq_ready; +#endif + if (__predict_true(shall_tx)) + mbufq_concat(&mq, &lsta->txq); + /* + * else a state change will push the packets out manually or + * lkpi_lsta_free() will drain the lsta->txq and free the mbufs. */ - mbufq_concat(&mq, &lsta->txq); LKPI_80211_LSTA_TXQ_UNLOCK(lsta); m = mbufq_dequeue(&mq); @@ -3803,7 +5263,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 @@ -3817,7 +5277,7 @@ lkpi_ic_recv_action(struct ieee80211_node *ni, const struct ieee80211_frame *wh, ic = ni->ni_ic; lhw = ic->ic_softc; - IMPROVE_HT(); + IMPROVE_HT("recv_action called; nothing to do in lkpi; make debugging"); return (lhw->ic_recv_action(ni, wh, frm, efrm)); } @@ -3831,7 +5291,7 @@ lkpi_ic_send_action(struct ieee80211_node *ni, int category, int action, void *s ic = ni->ni_ic; lhw = ic->ic_softc; - IMPROVE_HT(); + IMPROVE_HT("send_action called; nothing to do in lkpi; make debugging"); return (lhw->ic_send_action(ni, category, action, sa)); } @@ -3846,52 +5306,207 @@ lkpi_ic_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) ic = ni->ni_ic; lhw = ic->ic_softc; - IMPROVE_HT(); + IMPROVE_HT("ieee80211_ampdu_enable called; nothing to do in lkpi for now; make debugging"); return (lhw->ic_ampdu_enable(ni, tap)); } +/* + * (*ic_addba_request)() is called by ieee80211_ampdu_request() before + * calling send_action(CAT_BA, BA_ADDBA_REQUEST). + * + * NB: returns 0 on ERROR! + */ static int lkpi_ic_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int dialogtoken, int baparamset, int batimeout) { struct ieee80211com *ic; 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 ieee80211_ampdu_params params = { }; + int error; ic = ni->ni_ic; lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + vap = ni->ni_vap; + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + lsta = ni->ni_drv_data; + sta = LSTA_TO_STA(lsta); - IMPROVE_HT(); + if (!lsta->added_to_drv) { + ic_printf(ic, "%s: lsta %p ni %p, sta %p not added to firmware\n", + __func__, lsta, ni, sta); + return (0); + } + + params.sta = sta; + params.action = IEEE80211_AMPDU_TX_START; + /* Keep 0 here! */ + params.buf_size = 0; + params.timeout = 0; + params.ssn = tap->txa_start & (IEEE80211_SEQ_RANGE-1); + params.tid = tap->txa_tid; + params.amsdu = false; + + IEEE80211_UNLOCK(ic); + wiphy_lock(hw->wiphy); + error = lkpi_80211_mo_ampdu_action(hw, vif, ¶ms); + wiphy_unlock(hw->wiphy); + IEEE80211_LOCK(ic); + if (error != 0) { + ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p tap %p\n", + __func__, error, ni, tap); + return (0); + } return (lhw->ic_addba_request(ni, tap, dialogtoken, baparamset, batimeout)); } +/* + * (*ic_addba_response)() is called from ht_recv_action_ba_addba_response() + * and calls the default ieee80211_addba_response() which always returns 1. + * + * NB: No error checking in net80211! + * Staying with 0 is an error. + */ static int lkpi_ic_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status, int baparamset, int batimeout) { struct ieee80211com *ic; 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 ieee80211_ampdu_params params = { }; + int error; ic = ni->ni_ic; lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + vap = ni->ni_vap; + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + lsta = ni->ni_drv_data; + sta = LSTA_TO_STA(lsta); - IMPROVE_HT(); + if (!lsta->added_to_drv) { + ic_printf(ic, "%s: lsta %p ni %p, sta %p not added to firmware\n", + __func__, lsta, ni, sta); + return (0); + } + + if (status == IEEE80211_STATUS_SUCCESS) { + params.sta = sta; + params.action = IEEE80211_AMPDU_TX_OPERATIONAL; + params.buf_size = tap->txa_wnd; + params.timeout = 0; + params.ssn = 0; + params.tid = tap->txa_tid; + if ((tap->txa_flags & IEEE80211_AGGR_AMSDU) != 0) + params.amsdu = true; + else + params.amsdu = false; + } else { + /* We need to free the allocated resources. */ + params.sta = sta; + switch (status) { + /* params.action = FLUSH, FLUSH_CONT */ + default: + params.action = IEEE80211_AMPDU_TX_STOP_CONT; + break; + } + params.buf_size = 0; + params.timeout = 0; + params.ssn = 0; + params.tid = tap->txa_tid; + params.amsdu = false; + } + + IEEE80211_UNLOCK(ic); + wiphy_lock(hw->wiphy); + error = lkpi_80211_mo_ampdu_action(hw, vif, ¶ms); + wiphy_unlock(hw->wiphy); + IEEE80211_LOCK(ic); + if (error != 0) { + ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p tap %p\n", + __func__, error, ni, tap); + return (0); + } + + IMPROVE_HT("who unleashes the TXQ? and when?, do we need to ni->ni_txseqs[tid] = tap->txa_start & 0xfff;"); return (lhw->ic_addba_response(ni, tap, status, baparamset, batimeout)); } +/* + * (*ic_addba_stop)() is called from ampdu_tx_stop(), ht_recv_action_ba_delba(), + * and ieee80211_ampdu_stop() and calls the default ieee80211_addba_stop(). + */ static void lkpi_ic_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { struct ieee80211com *ic; 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 ieee80211_ampdu_params params = { }; + int error; ic = ni->ni_ic; lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + vap = ni->ni_vap; + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + lsta = ni->ni_drv_data; + sta = LSTA_TO_STA(lsta); - IMPROVE_HT(); + if (!lsta->added_to_drv) { + ic_printf(ic, "%s: lsta %p ni %p, sta %p not added to firmware\n", + __func__, lsta, ni, sta); + goto n80211; + } + + /* We need to free the allocated resources. */ + params.sta = sta; + IMPROVE("net80211 does not provide a reason to us"); + params.action = IEEE80211_AMPDU_TX_STOP_CONT; /* params.action = FLUSH, FLUSH_CONT */ + params.buf_size = 0; + params.timeout = 0; + params.ssn = 0; + params.tid = tap->txa_tid; + params.amsdu = false; + + IEEE80211_UNLOCK(ic); + wiphy_lock(hw->wiphy); + error = lkpi_80211_mo_ampdu_action(hw, vif, ¶ms); + wiphy_unlock(hw->wiphy); + IEEE80211_LOCK(ic); + if (error != 0) { + ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p tap %p\n", + __func__, error, ni, tap); + goto n80211; + } + IMPROVE_HT("anyting else?"); + +n80211: lhw->ic_addba_stop(ni, tap); } @@ -3935,8 +5550,8 @@ lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap struct lkpi_vif *lvif; struct ieee80211_vif *vif; struct lkpi_sta *lsta; - struct ieee80211_sta *sta; - struct ieee80211_ampdu_params params; + struct ieee80211_sta *sta; + struct ieee80211_ampdu_params params = { }; int error; ic = ni->ni_ic; @@ -3948,6 +5563,14 @@ lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap lsta = ni->ni_drv_data; sta = LSTA_TO_STA(lsta); + IEEE80211_UNLOCK_ASSERT(ic); + + 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); + return (-ENXIO); + } + params.sta = sta; params.action = IEEE80211_AMPDU_RX_START; params.buf_size = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_BUFSIZ); @@ -3955,22 +5578,33 @@ lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap params.buf_size = IEEE80211_MAX_AMPDU_BUF_HT; else params.buf_size = min(params.buf_size, IEEE80211_MAX_AMPDU_BUF_HT); - if (params.buf_size > hw->max_rx_aggregation_subframes) + if (hw->max_rx_aggregation_subframes > 0 && + params.buf_size > hw->max_rx_aggregation_subframes) params.buf_size = hw->max_rx_aggregation_subframes; params.timeout = le16toh(batimeout); params.ssn = _IEEE80211_MASKSHIFT(le16toh(baseqctl), IEEE80211_BASEQ_START); params.tid = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_TID); - params.amsdu = false; - IMPROVE_HT("Do we need to distinguish based on SUPPORTS_REORDERING_BUFFER?"); + /* Based on net80211::ampdu_rx_start(). */ + if ((vap->iv_htcaps & IEEE80211_HTC_RX_AMSDU_AMPDU) && + (_IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU))) + params.amsdu = true; + else + params.amsdu = false; - /* This may call kalloc. Make sure we can sleep. */ + wiphy_lock(hw->wiphy); error = lkpi_80211_mo_ampdu_action(hw, vif, ¶ms); + 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); return (error); } + + if (!ieee80211_hw_check(hw, SUPPORTS_REORDERING_BUFFER)) { + IMPROVE("%s: TODO: SUPPORTS_REORDERING_BUFFER not set; check net80211\n", __func__); + } + IMPROVE_HT("net80211 is missing the error check on return and assumes success"); error = lhw->ic_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl); @@ -3987,10 +5621,11 @@ lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) struct lkpi_vif *lvif; struct ieee80211_vif *vif; struct lkpi_sta *lsta; - struct ieee80211_sta *sta; - struct ieee80211_ampdu_params params; + struct ieee80211_sta *sta; + struct ieee80211_ampdu_params params = { }; int error; uint8_t tid; + bool ic_locked; ic = ni->ni_ic; lhw = ic->ic_softc; @@ -4028,7 +5663,14 @@ lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) params.tid = tid; params.amsdu = false; + ic_locked = IEEE80211_IS_LOCKED(ic); + if (ic_locked) + IEEE80211_UNLOCK(ic); + wiphy_lock(hw->wiphy); error = lkpi_80211_mo_ampdu_action(hw, vif, ¶ms); + wiphy_unlock(hw->wiphy); + if (ic_locked) + IEEE80211_LOCK(ic); if (error != 0) ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n", __func__, error, ni, rap); @@ -4174,11 +5816,13 @@ 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 = hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.cap; + ic->ic_vht_cap.supp_mcs = + hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_mcs; setbit(bands, IEEE80211_MODE_VHT_5GHZ); chan_flags |= NET80211_CBW_FLAG_VHT80; @@ -4238,8 +5882,6 @@ lkpi_ieee80211_ifalloc(void) struct ieee80211com *ic; ic = malloc(sizeof(*ic), M_LKPI80211, M_WAITOK | M_ZERO); - if (ic == NULL) - return (NULL); /* Setting these happens later when we have device information. */ ic->ic_softc = NULL; @@ -4264,7 +5906,6 @@ 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); sx_init_flags(&lhw->lvif_sx, "lhw-lvif", SX_RECURSE | SX_DUPOK); @@ -4274,10 +5915,13 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops) 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; /* @@ -4291,10 +5935,6 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops) /* BSD Specific. */ lhw->ic = lkpi_ieee80211_ifalloc(); - if (lhw->ic == NULL) { - ieee80211_free_hw(hw); - return (NULL); - } IMPROVE(); @@ -4325,6 +5965,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); @@ -4334,6 +5975,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); } @@ -4341,10 +5990,25 @@ 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); + } + } + /* Cleanup more of lhw here or in wiphy_free()? */ 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); IMPROVE(); } @@ -4453,6 +6117,10 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw) lhw->scan_flags |= LKPI_LHW_SCAN_HW; } + /* Does HW support Fragmentation offload? */ + if (ieee80211_hw_check(hw, SUPPORTS_TX_FRAG)) + ic->ic_flags_ext |= IEEE80211_FEXT_FRAG_OFFLOAD; + /* * The wiphy variables report bitmasks of avail antennas. * (*get_antenna) get the current bitmask sets which can be @@ -4471,10 +6139,46 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw) ic->ic_cryptocaps = 0; #ifdef LKPI_80211_HW_CRYPTO - if (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]); + if (lkpi_hwcrypto && hw->wiphy->n_cipher_suites > 0) { + 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 @@ -4512,30 +6216,36 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw) ic->ic_node_free = lkpi_ic_node_free; #ifdef LKPI_80211_HT - lhw->ic_recv_action = ic->ic_recv_action; - ic->ic_recv_action = lkpi_ic_recv_action; - lhw->ic_send_action = ic->ic_send_action; - ic->ic_send_action = lkpi_ic_send_action; - - lhw->ic_ampdu_enable = ic->ic_ampdu_enable; - ic->ic_ampdu_enable = lkpi_ic_ampdu_enable; - - lhw->ic_addba_request = ic->ic_addba_request; - ic->ic_addba_request = lkpi_ic_addba_request; - lhw->ic_addba_response = ic->ic_addba_response; - ic->ic_addba_response = lkpi_ic_addba_response; - lhw->ic_addba_stop = ic->ic_addba_stop; - ic->ic_addba_stop = lkpi_ic_addba_stop; - lhw->ic_addba_response_timeout = ic->ic_addba_response_timeout; - ic->ic_addba_response_timeout = lkpi_ic_addba_response_timeout; - - lhw->ic_bar_response = ic->ic_bar_response; - ic->ic_bar_response = lkpi_ic_bar_response; - - lhw->ic_ampdu_rx_start = ic->ic_ampdu_rx_start; - ic->ic_ampdu_rx_start = lkpi_ic_ampdu_rx_start; - lhw->ic_ampdu_rx_stop = ic->ic_ampdu_rx_stop; - ic->ic_ampdu_rx_stop = lkpi_ic_ampdu_rx_stop; + /* + * Only attach if the driver/firmware supports (*ampdu_action)(). + * Otherwise it is in the hands of net80211. + */ + if (lhw->ops->ampdu_action != NULL) { + lhw->ic_recv_action = ic->ic_recv_action; + ic->ic_recv_action = lkpi_ic_recv_action; + lhw->ic_send_action = ic->ic_send_action; + ic->ic_send_action = lkpi_ic_send_action; + + lhw->ic_ampdu_enable = ic->ic_ampdu_enable; + ic->ic_ampdu_enable = lkpi_ic_ampdu_enable; + + lhw->ic_addba_request = ic->ic_addba_request; + ic->ic_addba_request = lkpi_ic_addba_request; + lhw->ic_addba_response = ic->ic_addba_response; + ic->ic_addba_response = lkpi_ic_addba_response; + lhw->ic_addba_stop = ic->ic_addba_stop; + ic->ic_addba_stop = lkpi_ic_addba_stop; + lhw->ic_addba_response_timeout = ic->ic_addba_response_timeout; + ic->ic_addba_response_timeout = lkpi_ic_addba_response_timeout; + + lhw->ic_bar_response = ic->ic_bar_response; + ic->ic_bar_response = lkpi_ic_bar_response; + + lhw->ic_ampdu_rx_start = ic->ic_ampdu_rx_start; + ic->ic_ampdu_rx_start = lkpi_ic_ampdu_rx_start; + lhw->ic_ampdu_rx_stop = ic->ic_ampdu_rx_stop; + ic->ic_ampdu_rx_stop = lkpi_ic_ampdu_rx_stop; + } #endif lkpi_radiotap_attach(lhw); @@ -4570,7 +6280,7 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw) cfg80211_chandef_create(&hw->conf.chandef, &channels[i], #ifdef LKPI_80211_HT - (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 : + (ic->ic_flags_ht & IEEE80211_FHT_HT) ? NL80211_CHAN_HT20 : #endif NL80211_CHAN_NO_HT); break; @@ -4611,7 +6321,7 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw) lhw->scan_ie_len += sizeof(struct ieee80211_ie_htcap); #endif #if defined(LKPI_80211_VHT) - if ((ic->ic_flags_ext & IEEE80211_FEXT_VHT) != 0) + if (IEEE80211_CONF_VHT(ic)) lhw->scan_ie_len += 2 + sizeof(struct ieee80211_vht_cap); #endif @@ -4704,15 +6414,63 @@ linuxkpi_ieee80211_iterate_interfaces(struct ieee80211_hw *hw, LKPI_80211_LHW_LVIF_UNLOCK(lhw); } +static void +lkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + ieee80211_keyix keyix, struct lkpi_sta *lsta, + void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_sta *, struct ieee80211_key_conf *, void *), + void *arg) +{ + if (!lsta->added_to_drv) + return; + + if (lsta->kc[keyix] == NULL) + return; + + iterfunc(hw, vif, LSTA_TO_STA(lsta), lsta->kc[keyix], arg); +} + void linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct ieee80211_key_conf *, void *), - void *arg) + void *arg, bool rcu) { + struct lkpi_sta *lsta; + struct lkpi_vif *lvif; - UNIMPLEMENTED; + lvif = VIF_TO_LVIF(vif); + + if (rcu) { + rcu_read_lock_held(); /* XXX-BZ is this correct? */ + + if (vif == NULL) { + TODO(); + } else { + list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) { + for (ieee80211_keyix keyix = 0; keyix < nitems(lsta->kc); + keyix++) + lkpi_ieee80211_iterate_keys(hw, vif, + keyix, lsta, iterfunc, arg); + } + } + } else { + TODO("Used by suspend/resume; order of keys as installed to " + "firmware is important; we'll need to rewrite some code for that"); + lockdep_assert_wiphy(hw->wiphy); + + if (vif == NULL) { + TODO(); + } else { + list_for_each_entry(lsta, &lvif->lsta_list, lsta_list) { + for (ieee80211_keyix keyix = 0; keyix < nitems(lsta->kc); + keyix++) + lkpi_ieee80211_iterate_keys(hw, vif, + keyix, lsta, iterfunc, arg); + } + } + } } void @@ -4722,8 +6480,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, @@ -4731,22 +6487,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->conf, arg); + iterfunc(hw, &lchanctx->chanctx_conf, arg); } - LKPI_80211_LHW_LVIF_UNLOCK(lhw); + rcu_read_unlock(); } void @@ -4766,14 +6513,14 @@ linuxkpi_ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw, LKPI_80211_LHW_LVIF_LOCK(lhw); TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) { - LKPI_80211_LVIF_LOCK(lvif); - TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) { + rcu_read_lock(); + list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) { if (!lsta->added_to_drv) continue; sta = LSTA_TO_STA(lsta); iterfunc(arg, sta); } - LKPI_80211_LVIF_UNLOCK(lvif); + rcu_read_unlock(); } LKPI_80211_LHW_LVIF_UNLOCK(lhw); } @@ -4839,10 +6586,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; @@ -4850,6 +6600,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); @@ -4863,7 +6619,7 @@ lkpi_80211_lhw_rxq_rx_one(struct lkpi_hw *lhw, struct mbuf *m) #ifdef LINUXKPI_DEBUG_80211 if (linuxkpi_debug_80211 & D80211_TRACE_RX) - printf("TRACE %s: handled frame type %#0x\n", __func__, ok); + printf("TRACE-RX: %s: handled frame type %#0x\n", __func__, ok); #endif } @@ -4878,8 +6634,8 @@ lkpi_80211_lhw_rxq_task(void *ctx, int pending) #ifdef LINUXKPI_DEBUG_80211 if (linuxkpi_debug_80211 & D80211_TRACE_RX) - printf("%s:%d lhw %p pending %d mbuf_qlen %d\n", - __func__, __LINE__, lhw, pending, mbufq_len(&lhw->rxq)); + printf("TRACE-RX: %s: lhw %p pending %d mbuf_qlen %d\n", + __func__, lhw, pending, mbufq_len(&lhw->rxq)); #endif mbufq_init(&mq, IFQ_MAXLEN); @@ -4895,6 +6651,193 @@ lkpi_80211_lhw_rxq_task(void *ctx, int pending) } } +static void +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? */ + rx_stats->c_nf = -96; + if (ieee80211_hw_check(hw, SIGNAL_DBM) && + !(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL)) + rssi = rx_status->signal; + else + rssi = rx_stats->c_nf; + /* + * net80211 signal strength data are in .5 dBm units relative to + * the current noise floor (see comment in ieee80211_node.h). + */ + rssi -= rx_stats->c_nf; + if (rssip != NULL) + *rssip = rssi; + rx_stats->c_rssi = rssi * 2; + rx_stats->r_flags |= IEEE80211_R_BAND; + rx_stats->c_band = + lkpi_nl80211_band_to_net80211_band(rx_status->band); + rx_stats->r_flags |= IEEE80211_R_FREQ | IEEE80211_R_IEEE; + rx_stats->c_freq = rx_status->freq; + rx_stats->c_ieee = ieee80211_mhz2ieee(rx_stats->c_freq, rx_stats->c_band); + + rx_stats->c_rx_tsf = rx_status->mactime; + + /* XXX RX_FLAG_MACTIME_IS_RTAP_TS64 ? */ + if ((rx_status->flag & RX_FLAG_MACTIME) == + (RX_FLAG_MACTIME_START|RX_FLAG_MACTIME_END)) { + rx_stats->r_flags |= IEEE80211_R_TSF64; + /* XXX RX_FLAG_MACTIME_PLCP_START ? */ + if ((rx_status->flag & RX_FLAG_MACTIME) == RX_FLAG_MACTIME_START) + rx_stats->r_flags |= IEEE80211_R_TSF_START; + if ((rx_status->flag & RX_FLAG_MACTIME) == RX_FLAG_MACTIME_END) + rx_stats->r_flags |= IEEE80211_R_TSF_END; + /* XXX-BZ if TSF_END will net80211 do the unwind of time? */ + } + + if (rx_status->chains != 0) { + int cc; + int8_t crssi; + + rx_stats->c_chain = rx_status->chains; + rx_stats->r_flags |= IEEE80211_R_C_CHAIN; + + cc = 0; + for (i = 0; i < nitems(rx_status->chain_signal); i++) { + if (!(rx_status->chains & BIT(i))) + continue; + crssi = rx_status->chain_signal[i]; + crssi -= rx_stats->c_nf; + rx_stats->c_rssi_ctl[i] = crssi * 2; + rx_stats->c_rssi_ext[i] = crssi * 2; /* XXX _ext ??? ATH thing? */ + /* We currently only have the global noise floor value. */ + rx_stats->c_nf_ctl[i] = rx_stats->c_nf; + rx_stats->c_nf_ext[i] = rx_stats->c_nf; + cc++; + } + if (cc > 0) + rx_stats->r_flags |= (IEEE80211_R_C_NF | IEEE80211_R_C_RSSI); + } + + /* XXX-NET80211 We are not going to populate c_phytype! */ + + switch (rx_status->encoding) { + case RX_ENC_LEGACY: + { + uint32_t legacy = 0; + + supband = hw->wiphy->bands[rx_status->band]; + if (supband != NULL) + 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; + 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; + 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; + break; + case RATE_INFO_BW_40: + rx_stats->c_width = IEEE80211_RX_FW_40MHZ; + break; + case RATE_INFO_BW_80: + rx_stats->c_width = IEEE80211_RX_FW_80MHZ; + break; + case RATE_INFO_BW_160: + rx_stats->c_width = IEEE80211_RX_FW_160MHZ; + break; + case RATE_INFO_BW_320: + case RATE_INFO_BW_HE_RU: + case RATE_INFO_BW_EHT_RU: + case RATE_INFO_BW_5: + case RATE_INFO_BW_10: + TODO("net80211 has not matching bandwidth for %u", rx_status->bw); + break; + } + + if ((rx_status->enc_flags & RX_ENC_FLAG_LDPC) != 0) + rx_stats->c_pktflags |= IEEE80211_RX_F_LDPC; + if ((rx_status->enc_flags & RX_ENC_FLAG_STBC_MASK) != 0) + rx_stats->c_pktflags |= IEEE80211_RX_F_STBC; + + /* + * We only need these for LKPI_80211_HW_CRYPTO in theory but in + * case the hardware does something we do not expect always leave + * these enabled. Leaving this commant as documentation for the || 1. + */ +#if defined(LKPI_80211_HW_CRYPTO) || 1 + if (rx_status->flag & RX_FLAG_DECRYPTED) { + rx_stats->c_pktflags |= IEEE80211_RX_F_DECRYPTED; + /* Only valid if decrypted is set. */ + 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_FAILED_FCS_CRC) + rx_stats->c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC; +#endif + + if (lsta != NULL) { + memcpy(&lsta->sinfo.rxrate, &rxrate, sizeof(rxrate)); + lsta->sinfo.filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE); + } +} + /* For %list see comment towards the end of the function. */ void linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, @@ -4911,12 +6854,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; - int8_t rssi; + 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; } @@ -4925,9 +6872,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); @@ -4949,9 +6898,9 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, 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" : ""); @@ -4961,13 +6910,13 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, /* Implement a dump_rxcb() !!! */ if (linuxkpi_debug_80211 & D80211_TRACE_RX) - printf("TRACE %s: RXCB: %ju %ju %u, %#0x, %u, %#0x, %#0x, " + 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__, (uintmax_t)rx_status->boottime_ns, (uintmax_t)rx_status->mactime, rx_status->device_timestamp, - rx_status->flag, + rx_status->flag, IEEE80211_RX_STATUS_FLAGS_BITS, rx_status->freq, rx_status->bw, rx_status->encoding, @@ -4989,41 +6938,7 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, no_trace_beacons: #endif - memset(&rx_stats, 0, sizeof(rx_stats)); - rx_stats.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI; - /* XXX-BZ correct hardcoded rssi and noise floor, how? survey? */ - rx_stats.c_nf = -96; - if (ieee80211_hw_check(hw, SIGNAL_DBM) && - !(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL)) - rssi = rx_status->signal; - else - rssi = rx_stats.c_nf; - /* - * net80211 signal strength data are in .5 dBm units relative to - * the current noise floor (see comment in ieee80211_node.h). - */ - rssi -= rx_stats.c_nf; - rx_stats.c_rssi = rssi * 2; - rx_stats.r_flags |= IEEE80211_R_BAND; - rx_stats.c_band = - lkpi_nl80211_band_to_net80211_band(rx_status->band); - rx_stats.r_flags |= IEEE80211_R_FREQ | IEEE80211_R_IEEE; - rx_stats.c_freq = rx_status->freq; - rx_stats.c_ieee = ieee80211_mhz2ieee(rx_stats.c_freq, rx_stats.c_band); - - /* XXX (*sta_statistics)() to get to some of that? */ - /* XXX-BZ dump the FreeBSD version of rx_stats as well! */ - - 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); ni = ieee80211_ref_node(lsta->ni); @@ -5036,6 +6951,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 @@ -5047,7 +6972,7 @@ no_trace_beacons: #ifdef LINUXKPI_DEBUG_80211 if (linuxkpi_debug_80211 & D80211_TRACE_RX) - printf("TRACE %s: sta %p lsta %p state %d ni %p vap %p%s\n", + printf("TRACE-RX: %s: sta %p lsta %p state %d ni %p vap %p%s\n", __func__, sta, lsta, (lsta != NULL) ? lsta->state : -1, ni, vap, is_beacon ? " beacon" : ""); #endif @@ -5118,32 +7043,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); @@ -5173,18 +7113,158 @@ linuxkpi_ieee80211_get_tid(struct ieee80211_hdr *hdr, bool nonqos_ok) return (tid); } +/* -------------------------------------------------------------------------- */ + +static void +lkpi_wiphy_work(struct work_struct *work) +{ + struct lkpi_wiphy *lwiphy; + struct wiphy *wiphy; + struct wiphy_work *wk; + + lwiphy = container_of(work, struct lkpi_wiphy, wwk); + wiphy = LWIPHY_TO_WIPHY(lwiphy); + + wiphy_lock(wiphy); + + LKPI_80211_LWIPHY_WORK_LOCK(lwiphy); + wk = list_first_entry_or_null(&lwiphy->wwk_list, struct wiphy_work, entry); + /* If there is nothing we do nothing. */ + if (wk == NULL) { + LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy); + wiphy_unlock(wiphy); + return; + } + list_del_init(&wk->entry); + + /* More work to do? */ + if (!list_empty(&lwiphy->wwk_list)) + schedule_work(work); + LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy); + + /* Finally call the (*wiphy_work_fn)() function. */ + wk->fn(wiphy, wk); + + wiphy_unlock(wiphy); +} + +void +linuxkpi_wiphy_work_queue(struct wiphy *wiphy, struct wiphy_work *wwk) +{ + struct lkpi_wiphy *lwiphy; + + lwiphy = WIPHY_TO_LWIPHY(wiphy); + + LKPI_80211_LWIPHY_WORK_LOCK(lwiphy); + /* Do not double-queue. */ + if (list_empty(&wwk->entry)) + list_add_tail(&wwk->entry, &lwiphy->wwk_list); + LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy); + + /* + * See how ieee80211_queue_work() work continues in Linux or if things + * migrate here over time? + * Use a system queue from linux/workqueue.h for now. + */ + queue_work(system_wq, &lwiphy->wwk); +} + +void +linuxkpi_wiphy_work_cancel(struct wiphy *wiphy, struct wiphy_work *wwk) +{ + struct lkpi_wiphy *lwiphy; + + lwiphy = WIPHY_TO_LWIPHY(wiphy); + + LKPI_80211_LWIPHY_WORK_LOCK(lwiphy); + /* Only cancel if queued. */ + if (!list_empty(&wwk->entry)) + list_del_init(&wwk->entry); + LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy); +} + +void +linuxkpi_wiphy_work_flush(struct wiphy *wiphy, struct wiphy_work *wwk) +{ + struct lkpi_wiphy *lwiphy; + struct wiphy_work *wk; + + lwiphy = WIPHY_TO_LWIPHY(wiphy); + LKPI_80211_LWIPHY_WORK_LOCK(lwiphy); + /* If wwk is unset, flush everything; called when wiphy is shut down. */ + if (wwk != NULL && list_empty(&wwk->entry)) { + LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy); + return; + } + + while (!list_empty(&lwiphy->wwk_list)) { + + wk = list_first_entry(&lwiphy->wwk_list, struct wiphy_work, + entry); + list_del_init(&wk->entry); + LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy); + wk->fn(wiphy, wk); + LKPI_80211_LWIPHY_WORK_LOCK(lwiphy); + if (wk == wwk) + break; + } + LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy); +} + +void +lkpi_wiphy_delayed_work_timer(struct timer_list *tl) +{ + struct wiphy_delayed_work *wdwk; + + wdwk = from_timer(wdwk, tl, timer); + wiphy_work_queue(wdwk->wiphy, &wdwk->work); +} + +void +linuxkpi_wiphy_delayed_work_queue(struct wiphy *wiphy, + struct wiphy_delayed_work *wdwk, unsigned long delay) +{ + if (delay == 0) { + /* Run right away. */ + del_timer(&wdwk->timer); + wiphy_work_queue(wiphy, &wdwk->work); + } else { + wdwk->wiphy = wiphy; + mod_timer(&wdwk->timer, jiffies + delay); + } +} + +void +linuxkpi_wiphy_delayed_work_cancel(struct wiphy *wiphy, + struct wiphy_delayed_work *wdwk) +{ + del_timer_sync(&wdwk->timer); + wiphy_work_cancel(wiphy, &wdwk->work); +} + +/* -------------------------------------------------------------------------- */ + struct wiphy * linuxkpi_wiphy_new(const struct cfg80211_ops *ops, size_t priv_len) { struct lkpi_wiphy *lwiphy; + struct wiphy *wiphy; lwiphy = kzalloc(sizeof(*lwiphy) + priv_len, GFP_KERNEL); if (lwiphy == NULL) return (NULL); lwiphy->ops = ops; - /* XXX TODO */ - return (LWIPHY_TO_WIPHY(lwiphy)); + LKPI_80211_LWIPHY_WORK_LOCK_INIT(lwiphy); + INIT_LIST_HEAD(&lwiphy->wwk_list); + INIT_WORK(&lwiphy->wwk, lkpi_wiphy_work); + + wiphy = LWIPHY_TO_WIPHY(lwiphy); + + mutex_init(&wiphy->mtx); + TODO(); + + return (wiphy); } void @@ -5195,10 +7275,45 @@ linuxkpi_wiphy_free(struct wiphy *wiphy) if (wiphy == NULL) return; + linuxkpi_wiphy_work_flush(wiphy, NULL); + mutex_destroy(&wiphy->mtx); + lwiphy = WIPHY_TO_LWIPHY(wiphy); + LKPI_80211_LWIPHY_WORK_LOCK_DESTROY(lwiphy); + kfree(lwiphy); } +static uint32_t +lkpi_cfg80211_calculate_bitrate_ht(struct rate_info *rate) +{ + TODO("cfg80211_calculate_bitrate_ht"); + return (rate->legacy); +} + +static uint32_t +lkpi_cfg80211_calculate_bitrate_vht(struct rate_info *rate) +{ + TODO("cfg80211_calculate_bitrate_vht"); + return (rate->legacy); +} + +uint32_t +linuxkpi_cfg80211_calculate_bitrate(struct rate_info *rate) +{ + + /* Beware: order! */ + if (rate->flags & RATE_INFO_FLAGS_MCS) + return (lkpi_cfg80211_calculate_bitrate_ht(rate)); + + if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) + return (lkpi_cfg80211_calculate_bitrate_vht(rate)); + + IMPROVE("HE/EHT/..."); + + return (rate->legacy); +} + uint32_t linuxkpi_ieee80211_channel_to_frequency(uint32_t channel, enum nl80211_band band) @@ -5232,14 +7347,14 @@ lkpi_find_lsta_by_ni(struct lkpi_vif *lvif, struct ieee80211_node *ni) { struct lkpi_sta *lsta, *temp; - LKPI_80211_LVIF_LOCK(lvif); - TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) { + rcu_read_lock(); + list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) { if (lsta->ni == ni) { - LKPI_80211_LVIF_UNLOCK(lvif); + rcu_read_unlock(); return (lsta); } } - LKPI_80211_LVIF_UNLOCK(lvif); + rcu_read_unlock(); return (NULL); } @@ -5249,20 +7364,20 @@ struct ieee80211_sta * linuxkpi_ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *peer) { struct lkpi_vif *lvif; - struct lkpi_sta *lsta, *temp; + struct lkpi_sta *lsta; struct ieee80211_sta *sta; lvif = VIF_TO_LVIF(vif); - LKPI_80211_LVIF_LOCK(lvif); - TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) { + rcu_read_lock(); + list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) { sta = LSTA_TO_STA(lsta); if (IEEE80211_ADDR_EQ(sta->addr, peer)) { - LKPI_80211_LVIF_UNLOCK(lvif); + rcu_read_unlock(); return (sta); } } - LKPI_80211_LVIF_UNLOCK(lvif); + rcu_read_unlock(); return (NULL); } @@ -5311,6 +7426,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; @@ -5425,12 +7541,6 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw, } if (ni != NULL) { - int ridx __unused; -#ifdef LINUXKPI_DEBUG_80211 - int old_rate; - - old_rate = ni->ni_vap->iv_bss->ni_txrate; -#endif txs.pktlen = skb->len; txs.flags |= IEEE80211_RATECTL_STATUS_PKTLEN; if (info->status.rates[0].count > 1) { @@ -5447,16 +7557,14 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw, txs.flags |= IEEE80211_RATECTL_STATUS_RSSI; } - IMPROVE("only update of rate matches but that requires us to get a proper rate"); + IMPROVE("only update rate if needed but that requires us to get a proper rate from mo_sta_statistics"); ieee80211_ratectl_tx_complete(ni, &txs); - ridx = ieee80211_ratectl_rate(ni->ni_vap->iv_bss, NULL, 0); + ieee80211_ratectl_rate(ni->ni_vap->iv_bss, NULL, 0); #ifdef LINUXKPI_DEBUG_80211 if (linuxkpi_debug_80211 & D80211_TRACE_TX) { - printf("TX-RATE: %s: old %d new %d ridx %d, " - "long_retries %d\n", __func__, - old_rate, ni->ni_vap->iv_bss->ni_txrate, - ridx, txs.long_retries); + printf("TX-RATE: %s: long_retries %d\n", __func__, + txs.long_retries); } #endif } @@ -5802,8 +7910,8 @@ lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq) #endif lvif->hw_queue_stopped[ac] = false; - LKPI_80211_LVIF_LOCK(lvif); - TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) { + rcu_read_lock(); + list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) { struct ieee80211_sta *sta; sta = LSTA_TO_STA(lsta); @@ -5822,19 +7930,19 @@ 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(<xq->skbq)) + lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]); } } - LKPI_80211_LVIF_UNLOCK(lvif); + rcu_read_unlock(); } } } 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; @@ -5844,13 +7952,23 @@ linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw) } void +linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw) +{ + wiphy_lock(hw->wiphy); + lkpi_ieee80211_wake_queues_locked(hw); + wiphy_unlock(hw->wiphy); +} + +void linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum) { KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n", __func__, qnum, hw->queues, hw)); + wiphy_lock(hw->wiphy); lkpi_ieee80211_wake_queues(hw, qnum); + wiphy_unlock(hw->wiphy); } /* This is just hardware queues. */ @@ -5916,8 +8034,12 @@ void linuxkpi_ieee80211_schedule_txq(struct ieee80211_hw *hw, if (!withoutpkts && ltxq_empty) goto out; - /* Make sure we do not double-schedule. */ - if (ltxq->txq_entry.tqe_next != NULL) + /* + * Make sure we do not double-schedule. We do this by checking tqe_prev, + * the previous entry in our tailq. tqe_prev is always valid if this entry + * is queued, tqe_next may be NULL if this is the only element in the list. + */ + if (ltxq->txq_entry.tqe_prev != NULL) goto out; lhw = HW_TO_LHW(hw); @@ -6128,6 +8250,43 @@ linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy) /* -------------------------------------------------------------------------- */ +/* + * hw->conf get initialized/set in various places for us: + * - linuxkpi_ieee80211_alloc_hw(): flags + * - linuxkpi_ieee80211_ifattach(): chandef + * - lkpi_ic_vap_create(): listen_interval + * - lkpi_ic_set_channel(): chandef, flags + */ + +int lkpi_80211_update_chandef(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *new) +{ + struct cfg80211_chan_def *cd; + uint32_t changed; + int error; + + changed = 0; + if (new == NULL || new->def.chan == NULL) + cd = NULL; + else + cd = &new->def; + + if (cd && cd->chan != hw->conf.chandef.chan) { + /* Copy; the chan pointer is fine and will stay valid. */ + hw->conf.chandef = *cd; + changed |= IEEE80211_CONF_CHANGE_CHANNEL; + } + IMPROVE("IEEE80211_CONF_CHANGE_PS, IEEE80211_CONF_CHANGE_POWER"); + + if (changed == 0) + return (0); + + error = lkpi_80211_mo_config(hw, changed); + return (error); +} + +/* -------------------------------------------------------------------------- */ + MODULE_VERSION(linuxkpi_wlan, 1); MODULE_DEPEND(linuxkpi_wlan, linuxkpi, 1, 1, 1); MODULE_DEPEND(linuxkpi_wlan, wlan, 1, 1, 1); diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h index b0156a5ade3f..89afec1235bd 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.h +++ b/sys/compat/linuxkpi/common/src/linux_80211.h @@ -42,6 +42,12 @@ #ifndef _LKPI_SRC_LINUX_80211_H #define _LKPI_SRC_LINUX_80211_H +#include "opt_wlan.h" + +#if defined(IEEE80211_DEBUG) && !defined(LINUXKPI_DEBUG_80211) +#define LINUXKPI_DEBUG_80211 +#endif + /* #define LINUXKPI_DEBUG_80211 */ #ifndef D80211_TODO @@ -61,6 +67,7 @@ #define D80211_TRACEX (D80211_TRACE_TX|D80211_TRACE_RX) #define D80211_TRACEX_DUMP (D80211_TRACE_TX_DUMP|D80211_TRACE_RX_DUMP) #define D80211_TRACE_STA 0x00010000 +#define D80211_TRACE_HW_CRYPTO 0x00020000 #define D80211_TRACE_MO 0x00100000 #define D80211_TRACE_MODE 0x0f000000 #define D80211_TRACE_MODE_HT 0x01000000 @@ -72,12 +79,14 @@ if (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) \ printf("%s:%d: XXX LKPI80211 IMPROVE_TXQ\n", __func__, __LINE__) -#define IMPROVE_HT(...) \ +#define IMPROVE_HT(fmt, ...) \ if (linuxkpi_debug_80211 & D80211_TRACE_MODE_HT) \ - printf("%s:%d: XXX LKPI80211 IMPROVE_HT\n", __func__, __LINE__) + printf("%s:%d: XXX LKPI80211 IMPROVE_HT " fmt "\n", \ + __func__, __LINE__, ##__VA_ARGS__); #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 @@ -87,6 +96,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; @@ -118,6 +128,8 @@ struct lkpi_radiotap_rx_hdr { (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)) +struct lkpi_hw; + struct lkpi_txq { TAILQ_ENTRY(lkpi_txq) txq_entry; @@ -134,8 +146,9 @@ struct lkpi_txq { struct lkpi_sta { - TAILQ_ENTRY(lkpi_sta) lsta_entry; + struct list_head lsta_list; struct ieee80211_node *ni; + struct ieee80211_hw *hw; /* back pointer f. locking. */ /* Deferred TX path. */ /* Eventually we might want to migrate this into net80211 entirely. */ @@ -144,21 +157,27 @@ struct lkpi_sta { struct mbufq txq; struct mtx txq_mtx; - struct ieee80211_key_conf *kc; + struct ieee80211_key_conf *kc[IEEE80211_WEP_NKID]; enum ieee80211_sta_state state; bool txq_ready; /* Can we run the taskq? */ 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); }; #define STA_TO_LSTA(_sta) container_of(_sta, struct lkpi_sta, sta) #define LSTA_TO_STA(_lsta) (&(_lsta)->sta) +/* Either protected by wiphy lock or rcu for the list. */ struct lkpi_vif { TAILQ_ENTRY(lkpi_vif) lvif_entry; struct ieee80211vap iv_vap; + eventhandler_tag lvif_ifllevent; + + struct sysctl_ctx_list sysctl_ctx; struct mtx mtx; struct wireless_dev wdev; @@ -168,8 +187,13 @@ struct lkpi_vif { enum ieee80211_state, int); struct ieee80211_node * (*iv_update_bss)(struct ieee80211vap *, struct ieee80211_node *); - TAILQ_HEAD(, lkpi_sta) lsta_head; + 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) */ bool lvif_bss_synched; bool added_to_drv; /* Driver knows; i.e. we called add_interface(). */ @@ -198,7 +222,7 @@ struct lkpi_hw { /* name it mac80211_sc? */ TAILQ_HEAD(, lkpi_vif) lvif_head; struct sx lvif_sx; - struct sx sx; + struct list_head lchanctx_list; struct mtx txq_mtx; uint32_t txq_generation[IEEE80211_NUM_ACS]; @@ -266,35 +290,42 @@ struct lkpi_hw { /* name it mac80211_sc? */ #define HW_TO_LHW(_hw) container_of(_hw, struct lkpi_hw, hw) struct lkpi_chanctx { + struct list_head entry; + bool added_to_drv; /* Managed by MO */ - struct ieee80211_chanctx_conf conf __aligned(CACHE_LINE_SIZE); + + struct ieee80211_chanctx_conf chanctx_conf __aligned(CACHE_LINE_SIZE); }; #define LCHANCTX_TO_CHANCTX_CONF(_lchanctx) \ - (&(_lchanctx)->conf) + (&(_lchanctx)->chanctx_conf) #define CHANCTX_CONF_TO_LCHANCTX(_conf) \ - container_of(_conf, struct lkpi_chanctx, conf) + container_of(_conf, struct lkpi_chanctx, chanctx_conf) struct lkpi_wiphy { const struct cfg80211_ops *ops; + struct work_struct wwk; + struct list_head wwk_list; + struct mtx wwk_mtx; + /* Must be last! */ struct wiphy wiphy __aligned(CACHE_LINE_SIZE); }; #define WIPHY_TO_LWIPHY(_wiphy) container_of(_wiphy, struct lkpi_wiphy, wiphy) #define LWIPHY_TO_WIPHY(_lwiphy) (&(_lwiphy)->wiphy) -#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_LWIPHY_WORK_LOCK_INIT(_lwiphy) \ + mtx_init(&(_lwiphy)->wwk_mtx, "lwiphy-work", NULL, MTX_DEF); +#define LKPI_80211_LWIPHY_WORK_LOCK_DESTROY(_lwiphy) \ + mtx_destroy(&(_lwiphy)->wwk_mtx) +#define LKPI_80211_LWIPHY_WORK_LOCK(_lwiphy) \ + mtx_lock(&(_lwiphy)->wwk_mtx) +#define LKPI_80211_LWIPHY_WORK_UNLOCK(_lwiphy) \ + mtx_unlock(&(_lwiphy)->wwk_mtx) +#define LKPI_80211_LWIPHY_WORK_LOCK_ASSERT(_lwiphy) \ + mtx_assert(&(_lwiphy)->wwk_mtx, MA_OWNED) +#define LKPI_80211_LWIPHY_WORK_UNLOCK_ASSERT(_lwiphy) \ + mtx_assert(&(_lwiphy)->wwk_mtx, MA_NOTOWNED) #define LKPI_80211_LHW_SCAN_LOCK_INIT(_lhw) \ mtx_init(&(_lhw)->scan_mtx, "lhw-scan", NULL, MTX_DEF | MTX_RECURSE); @@ -368,7 +399,7 @@ struct lkpi_wiphy { mtx_assert(&(_ltxq)->ltxq_mtx, MA_NOTOWNED) int lkpi_80211_mo_start(struct ieee80211_hw *); -void lkpi_80211_mo_stop(struct ieee80211_hw *); +void lkpi_80211_mo_stop(struct ieee80211_hw *, bool); int lkpi_80211_mo_get_antenna(struct ieee80211_hw *, u32 *, u32 *); int lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *, uint32_t); int lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *, uint32_t); @@ -390,7 +421,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); @@ -417,6 +448,7 @@ int lkpi_80211_mo_set_key(struct ieee80211_hw *, enum set_key_cmd, struct ieee80211_key_conf *); int lkpi_80211_mo_ampdu_action(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_ampdu_params *); - +int lkpi_80211_mo_sta_statistics(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_sta *, struct station_info *); #endif /* _LKPI_SRC_LINUX_80211_H */ diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c index 8cc885c037e3..78b2120f2d8c 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 @@ -74,7 +74,7 @@ out: } void -lkpi_80211_mo_stop(struct ieee80211_hw *hw) +lkpi_80211_mo_stop(struct ieee80211_hw *hw, bool suspend) { struct lkpi_hw *lhw; @@ -82,8 +82,8 @@ lkpi_80211_mo_stop(struct ieee80211_hw *hw) if (lhw->ops->stop == NULL) return; - LKPI_80211_TRACE_MO("hw %p", hw); - lhw->ops->stop(hw); + LKPI_80211_TRACE_MO("hw %p suspend %d", hw, suspend); + lhw->ops->stop(hw, suspend); lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED; } @@ -458,7 +458,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 +466,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 +553,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); @@ -683,6 +688,8 @@ lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct lkpi_hw *lhw; int error; + lockdep_assert_wiphy(hw->wiphy); + lhw = HW_TO_LHW(hw); if (lhw->ops->set_key == NULL) { error = EOPNOTSUPP; @@ -717,3 +724,33 @@ lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, out: return (error); } + +int +lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct station_info *sinfo) +{ + struct lkpi_hw *lhw; + struct lkpi_sta *lsta; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->sta_statistics == NULL) { + error = EOPNOTSUPP; + goto out; + } + + lsta = STA_TO_LSTA(sta); + if (!lsta->added_to_drv) { + error = EEXIST; + goto out; + } + + lockdep_assert_wiphy(hw->wiphy); + + LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo); + lhw->ops->sta_statistics(hw, vif, sta, sinfo); + error = 0; + +out: + return (error); +} diff --git a/sys/compat/linuxkpi/common/src/linux_acpi.c b/sys/compat/linuxkpi/common/src/linux_acpi.c index 60ec838e9da7..d18c69d9210d 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> @@ -99,6 +100,17 @@ acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const char *uuid, int rev, argv4, &buf, type)) ? (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; + + return (ACPI_SUCCESS(acpi_EvaluateDSM(ObjHandle, (const uint8_t *)guid, + rev, func, (ACPI_OBJECT *)pkg, &buf)) ? + (union linuxkpi_acpi_object *)buf.Pointer : NULL); +} + static void linux_handle_power_suspend_event(void *arg __unused) { @@ -180,6 +192,7 @@ struct acpi_dev_present_ctx { const char *hid; const char *uid; int64_t hrv; + struct acpi_device *dev; }; static ACPI_STATUS @@ -187,6 +200,7 @@ acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context, void **result) { ACPI_DEVICE_INFO *devinfo; + struct acpi_device *dev; struct acpi_dev_present_ctx *match = context; bool present = false; UINT32 sta, hrv; @@ -230,6 +244,11 @@ acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context, return (AE_OK); } + dev = acpi_get_device(handle); + if (dev == NULL) + return (AE_OK); + match->dev = dev; + return (AE_ERROR); } @@ -249,6 +268,24 @@ lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv) return (rv == AE_ERROR); } +struct acpi_device * +lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid, + int64_t hrv) +{ + struct acpi_dev_present_ctx match; + int rv; + + match.hid = hid; + match.uid = uid; + match.hrv = hrv; + match.dev = NULL; + + rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL); + + return (rv == AE_ERROR ? match.dev : NULL); +} + static void linux_register_acpi_event_handlers(void *arg __unused) { @@ -298,6 +335,13 @@ acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const char *uuid, int rev, 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) { @@ -322,4 +366,11 @@ lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv) return (false); } +struct acpi_device * +lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid, + int64_t hrv) +{ + return (NULL); +} + #endif /* !DEV_ACPI */ diff --git a/sys/compat/linuxkpi/common/src/linux_aperture.c b/sys/compat/linuxkpi/common/src/linux_aperture.c index 15a56839fa9c..21c7041fc851 100644 --- a/sys/compat/linuxkpi/common/src/linux_aperture.c +++ b/sys/compat/linuxkpi/common/src/linux_aperture.c @@ -20,7 +20,7 @@ * driver can be active at any given time. Many systems load a generic * graphics drivers, such as EFI-GOP or VESA, early during the boot process. * During later boot stages, they replace the generic driver with a dedicated, - * hardware-specific driver. To take over the device the dedicated driver + * hardware-specific driver. To take over the device, the dedicated driver * first has to remove the generic driver. Aperture functions manage * ownership of framebuffer memory and hand-over between drivers. * @@ -43,7 +43,7 @@ * base = mem->start; * size = resource_size(mem); * - * ret = aperture_remove_conflicting_devices(base, size, false, "example"); + * ret = aperture_remove_conflicting_devices(base, size, "example"); * if (ret) * return ret; * @@ -76,7 +76,7 @@ * generic EFI or VESA drivers, have to register themselves as owners of their * framebuffer apertures. Ownership of the framebuffer memory is achieved * by calling devm_aperture_acquire_for_platform_device(). If successful, the - * driveris the owner of the framebuffer range. The function fails if the + * driver is the owner of the framebuffer range. The function fails if the * framebuffer is already owned by another driver. See below for an example. * * .. code-block:: c @@ -126,7 +126,7 @@ * et al for the registered framebuffer range, the aperture helpers call * platform_device_unregister() and the generic driver unloads itself. The * generic driver also has to provide a remove function to make this work. - * Once hot unplugged fro mhardware, it may not access the device's + * Once hot unplugged from hardware, it may not access the device's * registers, framebuffer memory, ROM, etc afterwards. */ @@ -203,7 +203,7 @@ static void aperture_detach_platform_device(struct device *dev) /* * Remove the device from the device hierarchy. This is the right thing - * to do for firmware-based DRM drivers, such as EFI, VESA or VGA. After + * to do for firmware-based fb drivers, such as EFI, VESA or VGA. After * the new driver takes over the hardware, the firmware device's state * will be lost. * @@ -274,7 +274,6 @@ static void aperture_detach_devices(resource_size_t base, resource_size_t size) * aperture_remove_conflicting_devices - remove devices in the given range * @base: the aperture's base address in physical memory * @size: aperture size in bytes - * @primary: also kick vga16fb if present; only relevant for VGA devices * @name: a descriptive name of the requesting driver * * This function removes devices that own apertures within @base and @size. @@ -283,7 +282,7 @@ static void aperture_detach_devices(resource_size_t base, resource_size_t size) * 0 on success, or a negative errno code otherwise */ int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size, - bool primary, const char *name) + const char *name) { /* * If a driver asked to unregister a platform device registered by @@ -300,19 +299,46 @@ int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t si aperture_detach_devices(base, size); - /* - * If this is the primary adapter, there could be a VGA device - * that consumes the VGA framebuffer I/O range. Remove this device - * as well. - */ - if (primary) - aperture_detach_devices(VGA_FB_PHYS_BASE, VGA_FB_PHYS_SIZE); - return 0; } EXPORT_SYMBOL(aperture_remove_conflicting_devices); /** + * __aperture_remove_legacy_vga_devices - remove legacy VGA devices of a PCI devices + * @pdev: PCI device + * + * This function removes VGA devices provided by @pdev, such as a VGA + * framebuffer or a console. This is useful if you have a VGA-compatible + * PCI graphics device with framebuffers in non-BAR locations. Drivers + * should acquire ownership of those memory areas and afterwards call + * this helper to release remaining VGA devices. + * + * If your hardware has its framebuffers accessible via PCI BARS, use + * aperture_remove_conflicting_pci_devices() instead. The function will + * release any VGA devices automatically. + * + * WARNING: Apparently we must remove graphics drivers before calling + * this helper. Otherwise the vga fbdev driver falls over if + * we have vgacon configured. + * + * Returns: + * 0 on success, or a negative errno code otherwise + */ +int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev) +{ + /* VGA framebuffer */ + aperture_detach_devices(VGA_FB_PHYS_BASE, VGA_FB_PHYS_SIZE); + + /* VGA textmode console */ +#ifdef __linux__ + return vga_remove_vgacon(pdev); +#elif defined(__FreeBSD__) + return 0; +#endif +} +EXPORT_SYMBOL(__aperture_remove_legacy_vga_devices); + +/** * aperture_remove_conflicting_pci_devices - remove existing framebuffers for PCI devices * @pdev: PCI device * @name: a descriptive name of the requesting driver @@ -328,14 +354,14 @@ int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *na { bool primary = false; resource_size_t base, size; - int bar, ret; + int bar, ret = 0; -#ifdef CONFIG_X86 #ifdef __linux__ - primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; -#elif defined(__FreeBSD__) - primary = NULL; -#endif + if (pdev == vga_default_device()) + primary = true; + + if (primary) + sysfb_disable(); #endif for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) { @@ -344,22 +370,18 @@ int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *na base = pci_resource_start(pdev, bar); size = pci_resource_len(pdev, bar); - ret = aperture_remove_conflicting_devices(base, size, primary, name); - if (ret) - return ret; + aperture_detach_devices(base, size); } /* - * WARNING: Apparently we must kick fbdev drivers before vgacon, - * otherwise the vga fbdev driver falls over. + * If this is the primary adapter, there could be a VGA device + * that consumes the VGA framebuffer I/O range. Remove this + * device as well. */ -#ifdef __linux__ - ret = vga_remove_vgacon(pdev); - if (ret) - return ret; -#endif + if (primary) + ret = __aperture_remove_legacy_vga_devices(pdev); - return 0; + return ret; } EXPORT_SYMBOL(aperture_remove_conflicting_pci_devices); diff --git a/sys/compat/linuxkpi/common/src/linux_cmdline.c b/sys/compat/linuxkpi/common/src/linux_cmdline.c new file mode 100644 index 000000000000..0cfa1d56ee6a --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_cmdline.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/param.h> +#include <sys/systm.h> + +#include <video/cmdline.h> + +const char * +video_get_options(const char *connector_name) +{ + char tunable[64]; + const char *options; + + /* + * A user may use loader tunables to set a specific mode for the + * console. Tunables are read in the following order: + * 1. kern.vt.fb.modes.$connector_name + * 2. kern.vt.fb.default_mode + * + * Example of a mode specific to the LVDS connector: + * kern.vt.fb.modes.LVDS="1024x768" + * + * Example of a mode applied to all connectors not having a + * connector-specific mode: + * kern.vt.fb.default_mode="640x480" + */ + snprintf(tunable, sizeof(tunable), "kern.vt.fb.modes.%s", + connector_name); + if (bootverbose) { + printf("[drm] Connector %s: get mode from tunables:\n", connector_name); + printf("[drm] - %s\n", tunable); + printf("[drm] - kern.vt.fb.default_mode\n"); + } + options = kern_getenv(tunable); + if (options == NULL) + options = kern_getenv("kern.vt.fb.default_mode"); + + return (options); +} diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c index a6eb7bb17e16..dcdec0dfcc78 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> @@ -75,6 +75,7 @@ #include <linux/moduleparam.h> #include <linux/cdev.h> #include <linux/file.h> +#include <linux/fs.h> #include <linux/sysfs.h> #include <linux/mm.h> #include <linux/io.h> @@ -95,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> @@ -117,6 +120,10 @@ int linuxkpi_debug; SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug, CTLFLAG_RWTUN, &linuxkpi_debug, 0, "Set to enable pr_debug() prints. Clear to disable."); +int linuxkpi_rcu_debug; +SYSCTL_INT(_compat_linuxkpi, OID_AUTO, rcu_debug, CTLFLAG_RWTUN, + &linuxkpi_rcu_debug, 0, "Set to enable RCU warning. Clear to disable."); + int linuxkpi_warn_dump_stack = 0; SYSCTL_INT(_compat_linuxkpi, OID_AUTO, warn_dump_stack, CTLFLAG_RWTUN, &linuxkpi_warn_dump_stack, 0, @@ -342,13 +349,12 @@ error: } struct class * -class_create(struct module *owner, const char *name) +lkpi_class_create(const char *name) { struct class *class; int error; class = kzalloc(sizeof(*class), M_WAITOK); - class->owner = owner; class->name = name; class->class_release = linux_class_kfree; error = class_register(class); @@ -382,9 +388,9 @@ linux_kq_assert_lock(void *arg, int what) spinlock_t *s = arg; if (what == LA_LOCKED) - mtx_assert(&s->m, MA_OWNED); + mtx_assert(s, MA_OWNED); else - mtx_assert(&s->m, MA_NOTOWNED); + mtx_assert(s, MA_NOTOWNED); #endif } @@ -641,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; @@ -648,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); @@ -769,7 +775,7 @@ linux_dev_fdopen(struct cdev *dev, int fflags, struct thread *td, } /* hold on to the vnode - used for fstat() */ - vhold(filp->f_vnode); + vref(filp->f_vnode); /* release the file from devfs */ finit(file, filp->f_mode, DTYPE_DEV, filp, &linuxfileops); @@ -1079,6 +1085,58 @@ linux_poll_wakeup(struct linux_file *filp) spin_unlock(&filp->f_kqlock); } +static struct linux_file * +__get_file_rcu(struct linux_file **f) +{ + struct linux_file *file1, *file2; + + file1 = READ_ONCE(*f); + if (file1 == NULL) + return (NULL); + + if (!refcount_acquire_if_not_zero( + file1->_file == NULL ? &file1->f_count : &file1->_file->f_count)) + return (ERR_PTR(-EAGAIN)); + + file2 = READ_ONCE(*f); + if (file2 == file1) + return (file2); + + fput(file1); + return (ERR_PTR(-EAGAIN)); +} + +struct linux_file * +linux_get_file_rcu(struct linux_file **f) +{ + struct linux_file *file1; + + for (;;) { + file1 = __get_file_rcu(f); + if (file1 == NULL) + return (NULL); + + if (IS_ERR(file1)) + continue; + + return (file1); + } +} + +struct linux_file * +get_file_active(struct linux_file **f) +{ + struct linux_file *file1; + + rcu_read_lock(); + file1 = __get_file_rcu(f); + rcu_read_unlock(); + if (IS_ERR(file1)) + file1 = NULL; + + return (file1); +} + static void linux_file_kqfilter_detach(struct knote *kn) { @@ -1094,7 +1152,7 @@ linux_file_kqfilter_read_event(struct knote *kn, long hint) { struct linux_file *filp = kn->kn_hook; - mtx_assert(&filp->f_kqlock.m, MA_OWNED); + mtx_assert(&filp->f_kqlock, MA_OWNED); return ((filp->f_kqflags & LINUX_KQ_FLAG_NEED_READ) ? 1 : 0); } @@ -1104,18 +1162,18 @@ linux_file_kqfilter_write_event(struct knote *kn, long hint) { struct linux_file *filp = kn->kn_hook; - mtx_assert(&filp->f_kqlock.m, MA_OWNED); + mtx_assert(&filp->f_kqlock, MA_OWNED); return ((filp->f_kqflags & LINUX_KQ_FLAG_NEED_WRITE) ? 1 : 0); } -static struct filterops linux_dev_kqfiltops_read = { +static const struct filterops linux_dev_kqfiltops_read = { .f_isfd = 1, .f_detach = linux_file_kqfilter_detach, .f_event = linux_file_kqfilter_read_event, }; -static struct filterops linux_dev_kqfiltops_write = { +static const struct filterops linux_dev_kqfiltops_write = { .f_isfd = 1, .f_detach = linux_file_kqfilter_detach, .f_event = linux_file_kqfilter_write_event, @@ -1501,7 +1559,7 @@ linux_file_close(struct file *file, struct thread *td) error = -OPW(file, td, release(filp->f_vnode, filp)); funsetown(&filp->f_sigio); if (filp->f_vnode != NULL) - vdrop(filp->f_vnode); + vrele(filp->f_vnode); linux_drop_fop(ldev); ldev = filp->f_cdev; if (ldev != NULL) @@ -1734,7 +1792,7 @@ linux_file_kcmp(struct file *fp1, struct file *fp2, struct thread *td) return (kcmp_cmp((uintptr_t)filp1->f_cdev, (uintptr_t)filp2->f_cdev)); } -struct fileops linuxfileops = { +const struct fileops linuxfileops = { .fo_read = linux_file_read, .fo_write = linux_file_write, .fo_truncate = invfo_truncate, @@ -1913,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) { @@ -1934,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; @@ -2069,20 +2221,16 @@ SYSINIT(linux_timer, SI_SUB_DRIVERS, SI_ORDER_FIRST, linux_timer_init, NULL); void linux_complete_common(struct completion *c, int all) { - int wakeup_swapper; - sleepq_lock(c); if (all) { c->done = UINT_MAX; - wakeup_swapper = sleepq_broadcast(c, SLEEPQ_SLEEP, 0, 0); + sleepq_broadcast(c, SLEEPQ_SLEEP, 0, 0); } else { if (c->done != UINT_MAX) c->done++; - wakeup_swapper = sleepq_signal(c, SLEEPQ_SLEEP, 0, 0); + sleepq_signal(c, SLEEPQ_SLEEP, 0, 0); } sleepq_release(c); - if (wakeup_swapper) - kick_proc0(); } /* @@ -2135,12 +2283,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); @@ -2597,6 +2745,54 @@ io_mapping_create_wc(resource_size_t base, unsigned long size) return (io_mapping_init_wc(mapping, base, size)); } +/* We likely want a linuxkpi_device.c at some point. */ +bool +device_can_wakeup(struct device *dev) +{ + + if (dev == NULL) + return (false); + /* + * XXX-BZ iwlwifi queries it as part of enabling WoWLAN. + * Normally this would be based on a bool in dev->power.XXX. + * Check such as PCI PCIM_PCAP_*PME. We have no way to enable this yet. + * We may get away by directly calling into bsddev for as long as + * we can assume PCI only avoiding changing struct device breaking KBI. + */ + pr_debug("%s:%d: not enabled; see comment.\n", __func__, __LINE__); + return (false); +} + +static void +devm_device_group_remove(struct device *dev, void *p) +{ + const struct attribute_group **dr = p; + const struct attribute_group *group = *dr; + + sysfs_remove_group(&dev->kobj, group); +} + +int +lkpi_devm_device_add_group(struct device *dev, + const struct attribute_group *group) +{ + const struct attribute_group **dr; + int ret; + + dr = devres_alloc(devm_device_group_remove, sizeof(*dr), GFP_KERNEL); + if (dr == NULL) + return (-ENOMEM); + + ret = sysfs_create_group(&dev->kobj, group); + if (ret == 0) { + *dr = group; + devres_add(dev, dr); + } else + devres_free(dr); + + return (ret); +} + #if defined(__i386__) || defined(__amd64__) bool linux_cpu_has_clflush; struct cpuinfo_x86 boot_cpu_data; @@ -2667,8 +2863,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; @@ -2710,8 +2906,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 @@ -2730,9 +2926,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_firmware.c b/sys/compat/linuxkpi/common/src/linux_firmware.c index 17da91381280..12658df5ce83 100644 --- a/sys/compat/linuxkpi/common/src/linux_firmware.c +++ b/sys/compat/linuxkpi/common/src/linux_firmware.c @@ -88,17 +88,17 @@ _linuxkpi_request_firmware(const char *fw_name, const struct linuxkpi_firmware * * way rather than adding more name-mangling-hacks here in the future * (though we could if needed). */ - /* (1) Try any name removed of path. */ - fwimg = strrchr(fw_name, '/'); - if (fwimg != NULL) - fwimg++; - if (fwimg == NULL || *fwimg == '\0') - fwimg = fw_name; - fbdfw = firmware_get_flags(fwimg, flags); - /* (2) Try the original name if we have not yet. */ - if (fbdfw == NULL && fwimg != fw_name) { - fwimg = fw_name; - fbdfw = firmware_get_flags(fwimg, flags); + /* (1) Try the original name. */ + fbdfw = firmware_get_flags(fw_name, flags); + /* (2) Try any name removed of path, if we have not yet. */ + if (fbdfw == NULL) { + fwimg = strrchr(fw_name, '/'); + if (fwimg != NULL) + fwimg++; + if (fwimg == NULL || *fwimg == '\0') + fwimg = fw_name; + if (fwimg != fw_name) + fbdfw = firmware_get_flags(fwimg, flags); } /* (3) Flatten '/', '.' and '-' to '_' and try with adjusted name. */ if (fbdfw == NULL && 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_hdmi.c b/sys/compat/linuxkpi/common/src/linux_hdmi.c index 947be761dfa4..fc47693e913c 100644 --- a/sys/compat/linuxkpi/common/src/linux_hdmi.c +++ b/sys/compat/linuxkpi/common/src/linux_hdmi.c @@ -21,6 +21,9 @@ * DEALINGS IN THE SOFTWARE. */ +#ifdef __linux__ +#include <drm/display/drm_dp.h> +#endif #include <linux/bitops.h> #include <linux/bug.h> #include <linux/errno.h> @@ -381,12 +384,34 @@ static int hdmi_audio_infoframe_check_only(const struct hdmi_audio_infoframe *fr * * Returns 0 on success or a negative error code on failure. */ -int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame) +int hdmi_audio_infoframe_check(const struct hdmi_audio_infoframe *frame) { return hdmi_audio_infoframe_check_only(frame); } EXPORT_SYMBOL(hdmi_audio_infoframe_check); +static void +hdmi_audio_infoframe_pack_payload(const struct hdmi_audio_infoframe *frame, + u8 *buffer) +{ + u8 channels; + + if (frame->channels >= 2) + channels = frame->channels - 1; + else + channels = 0; + + buffer[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7); + buffer[1] = ((frame->sample_frequency & 0x7) << 2) | + (frame->sample_size & 0x3); + buffer[2] = frame->coding_type_ext & 0x1f; + buffer[3] = frame->channel_allocation; + buffer[4] = (frame->level_shift_value & 0xf) << 3; + + if (frame->downmix_inhibit) + buffer[4] |= BIT(7); +} + /** * hdmi_audio_infoframe_pack_only() - write HDMI audio infoframe to binary buffer * @frame: HDMI audio infoframe @@ -404,7 +429,6 @@ EXPORT_SYMBOL(hdmi_audio_infoframe_check); ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame, void *buffer, size_t size) { - unsigned char channels; u8 *ptr = buffer; size_t length; int ret; @@ -420,28 +444,13 @@ ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame, memset(buffer, 0, size); - if (frame->channels >= 2) - channels = frame->channels - 1; - else - channels = 0; - ptr[0] = frame->type; ptr[1] = frame->version; ptr[2] = frame->length; ptr[3] = 0; /* checksum */ - /* start infoframe payload */ - ptr += HDMI_INFOFRAME_HEADER_SIZE; - - ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7); - ptr[1] = ((frame->sample_frequency & 0x7) << 2) | - (frame->sample_size & 0x3); - ptr[2] = frame->coding_type_ext & 0x1f; - ptr[3] = frame->channel_allocation; - ptr[4] = (frame->level_shift_value & 0xf) << 3; - - if (frame->downmix_inhibit) - ptr[4] |= BIT(7); + hdmi_audio_infoframe_pack_payload(frame, + ptr + HDMI_INFOFRAME_HEADER_SIZE); hdmi_infoframe_set_checksum(buffer, length); @@ -479,6 +488,45 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, } EXPORT_SYMBOL(hdmi_audio_infoframe_pack); +#ifdef __linux__ +/** + * hdmi_audio_infoframe_pack_for_dp - Pack a HDMI Audio infoframe for DisplayPort + * + * @frame: HDMI Audio infoframe + * @sdp: Secondary data packet for DisplayPort. + * @dp_version: DisplayPort version to be encoded in the header + * + * Packs a HDMI Audio Infoframe to be sent over DisplayPort. This function + * fills the secondary data packet to be used for DisplayPort. + * + * Return: Number of total written bytes or a negative errno on failure. + */ +ssize_t +hdmi_audio_infoframe_pack_for_dp(const struct hdmi_audio_infoframe *frame, + struct dp_sdp *sdp, u8 dp_version) +{ + int ret; + + ret = hdmi_audio_infoframe_check(frame); + if (ret) + return ret; + + memset(sdp->db, 0, sizeof(sdp->db)); + + /* Secondary-data packet header */ + sdp->sdp_header.HB0 = 0; + sdp->sdp_header.HB1 = frame->type; + sdp->sdp_header.HB2 = DP_SDP_AUDIO_INFOFRAME_HB2; + sdp->sdp_header.HB3 = (dp_version & 0x3f) << 2; + + hdmi_audio_infoframe_pack_payload(frame, sdp->db); + + /* Return size = frame length + four HB for sdp_header */ + return frame->length + 4; +} +EXPORT_SYMBOL(hdmi_audio_infoframe_pack_for_dp); +#endif + /** * hdmi_vendor_infoframe_init() - initialize an HDMI vendor infoframe * @frame: HDMI vendor infoframe diff --git a/sys/compat/linuxkpi/common/src/linux_i2c.c b/sys/compat/linuxkpi/common/src/linux_i2c.c index 60f7737cf6ec..f18570202f74 100644 --- a/sys/compat/linuxkpi/common/src/linux_i2c.c +++ b/sys/compat/linuxkpi/common/src/linux_i2c.c @@ -85,12 +85,12 @@ 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); } - bus_generic_attach(dev); + bus_attach_children(dev); return (0); } @@ -316,7 +316,6 @@ int lkpi_i2c_add_adapter(struct i2c_adapter *adapter) { device_t lkpi_iic; - int error; if (adapter->name[0] == '\0') return (-EINVAL); @@ -324,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); @@ -332,14 +332,8 @@ lkpi_i2c_add_adapter(struct i2c_adapter *adapter) } bus_topo_lock(); - error = bus_generic_attach(adapter->dev.parent->bsddev); + bus_attach_children(adapter->dev.parent->bsddev); bus_topo_unlock(); - if (error) { - device_printf(adapter->dev.parent->bsddev, - "failed to attach child: error %d\n", error); - sx_xunlock(&lkpi_sx_i2c); - return (ENXIO); - } LKPI_IIC_ADD_ADAPTER(lkpi_iic, adapter); sx_xunlock(&lkpi_sx_i2c); return (0); diff --git a/sys/compat/linuxkpi/common/src/linux_i2cbb.c b/sys/compat/linuxkpi/common/src/linux_i2cbb.c index 1ebc0b597c4d..48a018ec2533 100644 --- a/sys/compat/linuxkpi/common/src/linux_i2cbb.c +++ b/sys/compat/linuxkpi/common/src/linux_i2cbb.c @@ -90,12 +90,12 @@ 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); } - bus_generic_attach(dev); + bus_attach_children(dev); return (0); } @@ -303,13 +303,13 @@ int lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter) { device_t lkpi_iicbb; - int error; if (bootverbose) 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); @@ -317,14 +317,8 @@ lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter) } bus_topo_lock(); - error = bus_generic_attach(adapter->dev.parent->bsddev); + bus_attach_children(adapter->dev.parent->bsddev); bus_topo_unlock(); - if (error) { - device_printf(adapter->dev.parent->bsddev, - "failed to attach child: error %d\n", error); - sx_xunlock(&lkpi_sx_i2cbb); - return (ENXIO); - } LKPI_IIC_ADD_ADAPTER(lkpi_iicbb, adapter); sx_xunlock(&lkpi_sx_i2cbb); return (0); diff --git a/sys/compat/linuxkpi/common/src/linux_idr.c b/sys/compat/linuxkpi/common/src/linux_idr.c index dc64da0d7cf5..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> @@ -69,7 +68,7 @@ idr_preload_dequeue_locked(struct linux_idr_cache *lic) struct idr_layer *retval; /* check if wrong thread is trying to dequeue */ - if (mtx_owned(&lic->lock.m) == 0) + if (mtx_owned(&lic->lock) == 0) return (NULL); retval = lic->head; @@ -178,6 +177,14 @@ idr_destroy(struct idr *idr) { struct idr_layer *il, *iln; + /* + * This idr can be reused, and this function might be called multiple times + * without a idr_init(). Check if this is the case. If we do not do this + * then the mutex will panic while asserting that it is valid. + */ + if (mtx_initialized(&idr->lock) == 0) + return; + idr_remove_all(idr); mtx_lock(&idr->lock); for (il = idr->free; il != NULL; il = iln) { @@ -802,4 +809,5 @@ ida_destroy(struct ida *ida) { idr_destroy(&ida->idr); free(ida->free_bitmap, M_IDR); + ida->free_bitmap = NULL; } 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 fe00e929c168..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 @@ -409,7 +409,8 @@ linuxkpi_alloc_netdev(size_t len, const char *name, uint32_t flags, /* This needs extending as we support more. */ - setup_func(ndev); + if (setup_func != NULL) + setup_func(ndev); return (ndev); } diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c index 8b78a3739f25..ebb92eacbf9a 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) { @@ -117,7 +117,8 @@ linux_alloc_pages(gfp_t flags, unsigned int order) page = vm_page_alloc_noobj_contig(req, npages, 0, pmax, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT); if (page == NULL) { - if (flags & M_WAITOK) { + if ((flags & (M_WAITOK | __GFP_NORETRY)) == + M_WAITOK) { int err = vm_page_reclaim_contig(req, npages, 0, pmax, PAGE_SIZE, 0); if (err == ENOMEM) @@ -164,8 +165,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; @@ -176,18 +193,27 @@ 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) { size_t size = ((size_t)PAGE_SIZE) << order; void *addr; - if ((flags & GFP_DMA32) == 0) { - addr = kmem_malloc(size, flags & GFP_NATIVE_MASK); - } else { - addr = kmem_alloc_contig(size, flags & GFP_NATIVE_MASK, 0, - BUS_SPACE_MAXADDR_32BIT, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT); - } + addr = kmem_alloc_contig(size, flags & GFP_NATIVE_MASK, 0, + ((flags & GFP_DMA32) == 0) ? -1UL : BUS_SPACE_MAXADDR_32BIT, + PAGE_SIZE, 0, VM_MEMATTR_DEFAULT); + return ((vm_offset_t)addr); } @@ -277,8 +303,8 @@ get_user_pages_remote(struct task_struct *task, struct mm_struct *mm, } long -get_user_pages(unsigned long start, unsigned long nr_pages, - unsigned int gup_flags, struct page **pages, struct vm_area_struct **vmas) +lkpi_get_user_pages(unsigned long start, unsigned long nr_pages, + unsigned int gup_flags, struct page **pages) { vm_map_t map; @@ -297,23 +323,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); @@ -337,10 +367,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); } @@ -418,27 +449,13 @@ lkpi_io_mapping_map_user(struct io_mapping *iomap, */ void lkpi_unmap_mapping_range(void *obj, loff_t const holebegin __unused, - loff_t const holelen, int even_cows __unused) + loff_t const holelen __unused, int even_cows __unused) { vm_object_t devobj; - vm_page_t page; - int i, page_count; devobj = cdev_pager_lookup(obj); if (devobj != NULL) { - page_count = OFF_TO_IDX(holelen); - - VM_OBJECT_WLOCK(devobj); -retry: - for (i = 0; i < page_count; i++) { - page = vm_page_lookup(devobj, i); - if (page == NULL) - continue; - if (!vm_page_busy_acquire(page, VM_ALLOC_WAITFAIL)) - goto retry; - cdev_pager_free_page(devobj, page); - } - VM_OBJECT_WUNLOCK(devobj); + cdev_mgtdev_pager_free_pages(devobj); vm_object_deallocate(devobj); } } diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c index 825ebe319b1a..55202da00440 100644 --- a/sys/compat/linuxkpi/common/src/linux_pci.c +++ b/sys/compat/linuxkpi/common/src/linux_pci.c @@ -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> @@ -287,18 +287,22 @@ 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) { - struct pci_dev *pdev; + struct pci_dev *pdev, *found; KASSERT(odev == NULL, ("%s: odev argument not yet supported\n", __func__)); + found = NULL; spin_lock(&pci_lock); list_for_each_entry(pdev, &pci_devices, links) { - if (pdev->vendor == vendor && pdev->device == device) + if (pdev->vendor == vendor && pdev->device == device) { + found = pdev; break; + } } + pci_dev_get(found); spin_unlock(&pci_lock); - return (pdev); + return (found); } static void @@ -309,9 +313,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 +354,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 +385,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); @@ -401,6 +417,24 @@ lkpi_pci_get_class(unsigned int class, struct pci_dev *from) } struct pci_dev * +lkpi_pci_get_base_class(unsigned int baseclass, struct pci_dev *from) +{ + device_t dev; + device_t devfrom = NULL; + struct pci_dev *pdev; + + if (from != NULL) + devfrom = from->dev.bsddev; + + dev = pci_find_base_class_from(baseclass, devfrom); + if (dev == NULL) + return (NULL); + + pdev = lkpinew_pci_dev(dev); + return (pdev); +} + +struct pci_dev * lkpi_pci_get_domain_bus_and_slot(int domain, unsigned int bus, unsigned int devfn) { @@ -507,7 +541,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 @@ -755,7 +792,8 @@ _lkpi_pci_iomap(struct pci_dev *pdev, int bar, int mmio_size __unused) } void * -linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size) +linuxkpi_pci_iomap_range(struct pci_dev *pdev, int mmio_bar, + unsigned long mmio_off, unsigned long mmio_size) { struct resource *res; @@ -765,17 +803,32 @@ linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size) /* This is a FreeBSD extension so we can use bus_*(). */ if (pdev->want_iomap_res) return (res); - return ((void *)rman_get_bushandle(res)); + MPASS(mmio_off < rman_get_size(res)); + return ((void *)(rman_get_bushandle(res) + mmio_off)); +} + +void * +linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size) +{ + return (linuxkpi_pci_iomap_range(pdev, mmio_bar, 0, mmio_size)); } void linuxkpi_pci_iounmap(struct pci_dev *pdev, void *res) { struct pci_mmio_region *mmio, *p; + bus_space_handle_t bh = (bus_space_handle_t)res; TAILQ_FOREACH_SAFE(mmio, &pdev->mmio, next, p) { - if (res != (void *)rman_get_bushandle(mmio->res)) - continue; + if (pdev->want_iomap_res) { + if (res != mmio->res) + continue; + } else { + if (bh < rman_get_bushandle(mmio->res) || + bh >= rman_get_bushandle(mmio->res) + + rman_get_size(mmio->res)) + continue; + } bus_release_resource(pdev->dev.bsddev, mmio->type, mmio->rid, mmio->res); TAILQ_REMOVE(&pdev->mmio, mmio, next); @@ -976,10 +1029,10 @@ linux_pci_register_driver(struct pci_driver *pdrv) { devclass_t dc; - dc = devclass_find("pci"); + pdrv->isdrm = strcmp(pdrv->name, "drmn") == 0; + dc = pdrv->isdrm ? devclass_create("vgapci") : devclass_find("pci"); if (dc == NULL) return (-ENXIO); - pdrv->isdrm = false; return (_linux_pci_register_driver(pdrv, dc)); } @@ -1166,7 +1219,7 @@ linux_pci_unregister_driver(struct pci_driver *pdrv) { devclass_t bus; - bus = devclass_find("pci"); + bus = devclass_find(pdrv->isdrm ? "vgapci" : "pci"); spin_lock(&pci_lock); list_del(&pdrv->node); @@ -1290,7 +1343,7 @@ out: if (error == 0 && pdev->msi_enabled) return (pdev->dev.irq_end - pdev->dev.irq_start); } - if (flags & PCI_IRQ_LEGACY) { + if (flags & PCI_IRQ_INTX) { if (pdev->irq) return (1); } @@ -1437,14 +1490,19 @@ linux_dma_map_phys_common(struct device *dev, vm_paddr_t phys, size_t len, } nseg = -1; - if (_bus_dmamap_load_phys(obj->dmat, obj->dmamap, phys, len, - BUS_DMA_NOWAIT, &seg, &nseg) != 0) { + error = _bus_dmamap_load_phys(obj->dmat, obj->dmamap, phys, len, + BUS_DMA_NOWAIT, &seg, &nseg); + if (error != 0) { bus_dmamap_destroy(obj->dmat, obj->dmamap); DMA_PRIV_UNLOCK(priv); uma_zfree(linux_dma_obj_zone, obj); counter_u64_add(lkpi_pci_nseg1_fail, 1); - if (linuxkpi_debug) + if (linuxkpi_debug) { + device_printf(dev->bsddev, "%s: _bus_dmamap_load_phys " + "error %d, phys %#018jx len %zu\n", __func__, + error, (uintmax_t)phys, len); dump_stack(); + } return (0); } @@ -1472,17 +1530,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; @@ -1499,6 +1574,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); @@ -1507,11 +1586,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) @@ -1613,7 +1700,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; @@ -1647,6 +1734,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); @@ -1660,6 +1750,7 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents, default: break; } +skip_sync: DMA_PRIV_UNLOCK(priv); @@ -1669,7 +1760,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; @@ -1677,6 +1768,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); @@ -1691,6 +1785,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_rcu.c b/sys/compat/linuxkpi/common/src/linux_rcu.c index 335708b6747f..c0b864d269b3 100644 --- a/sys/compat/linuxkpi/common/src/linux_rcu.c +++ b/sys/compat/linuxkpi/common/src/linux_rcu.c @@ -2,6 +2,10 @@ * Copyright (c) 2016 Matthew Macy (mmacy@mattmacy.io) * Copyright (c) 2017-2021 Hans Petter Selasky (hselasky@freebsd.org) * All rights reserved. + * Copyright (c) 2024 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 @@ -41,6 +45,7 @@ #include <ck_epoch.h> #include <linux/rcupdate.h> +#include <linux/sched.h> #include <linux/srcu.h> #include <linux/slab.h> #include <linux/kernel.h> @@ -244,6 +249,33 @@ linux_rcu_read_unlock(unsigned type) sched_unpin(); } +bool +linux_rcu_read_lock_held(unsigned type) +{ +#ifdef INVARINATS + struct linux_epoch_record *record __diagused; + struct task_struct *ts; + + MPASS(type < RCU_TYPE_MAX); + + if (RCU_SKIP()) + return (false); + + if (__current_unallocated(curthread)) + return (false); + + ts = current; + if (ts->rcu_recurse[type] == 0) + return (false); + + MPASS(curthread->td_pinned != 0); + MPASS((record = &DPCPU_GET(linux_epoch_record[type])) && + record->epoch_record.active != 0); +#endif + + return (true); +} + static void linux_synchronize_rcu_cb(ck_epoch_t *epoch __unused, ck_epoch_record_t *epoch_record, void *arg __unused) { diff --git a/sys/compat/linuxkpi/common/src/linux_schedule.c b/sys/compat/linuxkpi/common/src/linux_schedule.c index 66b339bfbdbd..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 @@ -98,18 +116,16 @@ linux_msleep_interruptible(unsigned int ms) static int wake_up_task(struct task_struct *task, unsigned int state) { - int ret, wakeup_swapper; + int ret; - ret = wakeup_swapper = 0; + ret = 0; sleepq_lock(task); if ((atomic_read(&task->state) & state) != 0) { set_task_state(task, TASK_WAKING); - wakeup_swapper = sleepq_signal(task, SLEEPQ_SLEEP, 0, 0); + sleepq_signal(task, SLEEPQ_SLEEP, 0, 0); ret = 1; } sleepq_release(task); - if (wakeup_swapper) - kick_proc0(); return (ret); } @@ -184,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) { @@ -251,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; @@ -260,19 +335,8 @@ 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; - /* - * Our wait queue entry is on the stack - make sure it doesn't - * get swapped out while we sleep. - */ - PHOLD(task->task_thread->td_proc); sleepq_lock(task); if (atomic_read(&task->state) != TASK_WAKING) { ret = linux_add_to_sleepqueue(task, task, "wevent", timeout, @@ -281,30 +345,22 @@ linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout, sleepq_release(task); ret = 0; } - PRELE(task->task_thread->td_proc); if (lock != NULL) spin_lock_irq(lock); 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); @@ -317,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) @@ -336,13 +392,9 @@ linux_schedule_timeout(int timeout) static void wake_up_sleepers(void *wchan) { - int wakeup_swapper; - sleepq_lock(wchan); - wakeup_swapper = sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0); + sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0); sleepq_release(wchan); - if (wakeup_swapper) - kick_proc0(); } #define bit_to_wchan(word, bit) ((void *)(((uintptr_t)(word) << 6) | (bit))) @@ -356,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_shrinker.c b/sys/compat/linuxkpi/common/src/linux_shrinker.c index 52a0472348d8..e06490b92ed1 100644 --- a/sys/compat/linuxkpi/common/src/linux_shrinker.c +++ b/sys/compat/linuxkpi/common/src/linux_shrinker.c @@ -32,10 +32,26 @@ #include <linux/compat.h> #include <linux/shrinker.h> +#include <linux/slab.h> TAILQ_HEAD(, shrinker) lkpi_shrinkers = TAILQ_HEAD_INITIALIZER(lkpi_shrinkers); static struct sx sx_shrinker; +struct shrinker * +linuxkpi_shrinker_alloc(unsigned int flags, const char *fmt, ...) +{ + struct shrinker *shrinker; + + shrinker = kzalloc(sizeof(*shrinker), GFP_KERNEL); + if (shrinker == NULL) + return (NULL); + + shrinker->flags = flags | SHRINKER_ALLOCATED; + shrinker->seeks = DEFAULT_SEEKS; + + return (shrinker); +} + int linuxkpi_register_shrinker(struct shrinker *s) { @@ -44,6 +60,7 @@ linuxkpi_register_shrinker(struct shrinker *s) KASSERT(s->count_objects != NULL, ("NULL shrinker")); KASSERT(s->scan_objects != NULL, ("NULL shrinker")); sx_xlock(&sx_shrinker); + s->flags |= SHRINKER_REGISTERED; TAILQ_INSERT_TAIL(&lkpi_shrinkers, s, next); sx_xunlock(&sx_shrinker); return (0); @@ -55,10 +72,21 @@ linuxkpi_unregister_shrinker(struct shrinker *s) sx_xlock(&sx_shrinker); TAILQ_REMOVE(&lkpi_shrinkers, s, next); + s->flags &= ~SHRINKER_REGISTERED; sx_xunlock(&sx_shrinker); } void +linuxkpi_shrinker_free(struct shrinker *shrinker) +{ + + if (shrinker->flags & SHRINKER_REGISTERED) + unregister_shrinker(shrinker); + + kfree(shrinker); +} + +void linuxkpi_synchronize_shrinkers(void) { @@ -92,7 +120,7 @@ shrinker_shrink(struct shrinker *s) } static void -linuxkpi_vm_lowmem(void *arg __unused) +linuxkpi_vm_lowmem(void *arg __unused, int flags __unused) { struct shrinker *s; diff --git a/sys/compat/linuxkpi/common/src/linux_simple_attr.c b/sys/compat/linuxkpi/common/src/linux_simple_attr.c index 1bdacd7c1491..8cc9ec7ecbc9 100644 --- a/sys/compat/linuxkpi/common/src/linux_simple_attr.c +++ b/sys/compat/linuxkpi/common/src/linux_simple_attr.c @@ -138,12 +138,13 @@ unlock: } /* - * simple_attr_write: write contents of buffer into simple attribute file + * simple_attr_write_common: write contents of buffer into simple attribute file * * @filp: file pointer * @buf: kernel space buffer * @write_size: number bytes to be transferred * @ppos: starting pointer position for transfer + * @is_signed: signedness of data in @buf * * The simple_attr structure is stored in filp->private_data. * Convert the @buf string to unsigned long long. @@ -153,8 +154,9 @@ unlock: * On success, number of bytes written to simple attr * On failure, negative signed ERRNO */ -ssize_t -simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos) +static ssize_t +simple_attr_write_common(struct file *filp, const char *buf, size_t write_size, + loff_t *ppos, bool is_signed) { struct simple_attr *sattr; unsigned long long data; @@ -172,7 +174,10 @@ simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t mutex_lock(&sattr->mutex); - ret = kstrtoull(buf + *ppos, 0, &data); + if (is_signed) + ret = kstrtoll(buf + *ppos, 0, &data); + else + ret = kstrtoull(buf + *ppos, 0, &data); if (ret) goto unlock; @@ -186,3 +191,17 @@ unlock: mutex_unlock(&sattr->mutex); return (ret); } + +ssize_t +simple_attr_write(struct file *filp, const char *buf, size_t write_size, + loff_t *ppos) +{ + return (simple_attr_write_common(filp, buf, write_size, ppos, false)); +} + +ssize_t +simple_attr_write_signed(struct file *filp, const char *buf, size_t write_size, + loff_t *ppos) +{ + return (simple_attr_write_common(filp, buf, write_size, ppos, true)); +} diff --git a/sys/compat/linuxkpi/common/src/linux_skbuff.c b/sys/compat/linuxkpi/common/src/linux_skbuff.c index 0522d3fdff41..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,14 +178,14 @@ 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); } struct sk_buff * -linuxkpi_skb_copy(struct sk_buff *skb, gfp_t gfp) +linuxkpi_skb_copy(const struct sk_buff *skb, gfp_t gfp) { struct sk_buff *new; struct skb_shared_info *shinfo; @@ -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 68117d1c9fa7..3d75ca480661 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 @@ -207,6 +211,68 @@ linux_kmem_cache_destroy(struct linux_kmem_cache *c) free(c, M_KMALLOC); } +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; + + /* sizeof(struct llist_node) is used for kfree_async(). */ + _s = MAX(size, sizeof(struct llist_node)); + + if (_s <= PAGE_SIZE) + return (malloc(_s, M_KMALLOC, linux_check_m_flags(flags))); + else + return (contigmalloc(_s, M_KMALLOC, linux_check_m_flags(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; @@ -241,7 +307,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) @@ -249,3 +315,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/common/src/linux_xarray.c b/sys/compat/linuxkpi/common/src/linux_xarray.c index 44900666242f..3f07f6d7c59f 100644 --- a/sys/compat/linuxkpi/common/src/linux_xarray.c +++ b/sys/compat/linuxkpi/common/src/linux_xarray.c @@ -52,7 +52,7 @@ __xa_erase(struct xarray *xa, uint32_t index) XA_ASSERT_LOCKED(xa); - retval = radix_tree_delete(&xa->root, index); + retval = radix_tree_delete(&xa->xa_head, index); if (retval == NULL_VALUE) retval = NULL; @@ -81,7 +81,7 @@ xa_load(struct xarray *xa, uint32_t index) void *retval; xa_lock(xa); - retval = radix_tree_lookup(&xa->root, index); + retval = radix_tree_lookup(&xa->xa_head, index); xa_unlock(xa); if (retval == NULL_VALUE) @@ -122,16 +122,16 @@ __xa_alloc(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask, gfp_t XA_ASSERT_LOCKED(xa); /* mask should allow to allocate at least one item */ - MPASS(mask > ((xa->flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0)); + MPASS(mask > ((xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0)); /* mask can be any power of two value minus one */ MPASS((mask & (mask + 1)) == 0); - *pindex = (xa->flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0; + *pindex = (xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0; if (ptr == NULL) ptr = NULL_VALUE; retry: - retval = radix_tree_insert(&xa->root, *pindex, ptr); + retval = radix_tree_insert(&xa->xa_head, *pindex, ptr); switch (retval) { case -EEXIST: @@ -184,16 +184,16 @@ __xa_alloc_cyclic(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask, XA_ASSERT_LOCKED(xa); /* mask should allow to allocate at least one item */ - MPASS(mask > ((xa->flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0)); + MPASS(mask > ((xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0)); /* mask can be any power of two value minus one */ MPASS((mask & (mask + 1)) == 0); - *pnext_index = (xa->flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0; + *pnext_index = (xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0; if (ptr == NULL) ptr = NULL_VALUE; retry: - retval = radix_tree_insert(&xa->root, *pnext_index, ptr); + retval = radix_tree_insert(&xa->xa_head, *pnext_index, ptr); switch (retval) { case -EEXIST: @@ -203,7 +203,7 @@ retry: } (*pnext_index)++; (*pnext_index) &= mask; - if (*pnext_index == 0 && (xa->flags & XA_FLAGS_ALLOC1) != 0) + if (*pnext_index == 0 && (xa->xa_flags & XA_FLAGS_ALLOC1) != 0) (*pnext_index)++; goto retry; case -ENOMEM: @@ -233,6 +233,19 @@ xa_alloc_cyclic(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask, return (retval); } +int +xa_alloc_cyclic_irq(struct xarray *xa, uint32_t *pindex, void *ptr, + uint32_t mask, uint32_t *pnext_index, gfp_t gfp) +{ + int retval; + + xa_lock_irq(xa); + retval = __xa_alloc_cyclic(xa, pindex, ptr, mask, pnext_index, gfp); + xa_unlock_irq(xa); + + return (retval); +} + /* * This function tries to insert an element at the given index. The * "gfp" argument basically decides of this function can sleep or not @@ -249,7 +262,7 @@ __xa_insert(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp) if (ptr == NULL) ptr = NULL_VALUE; retry: - retval = radix_tree_insert(&xa->root, index, ptr); + retval = radix_tree_insert(&xa->xa_head, index, ptr); switch (retval) { case -ENOMEM: @@ -293,7 +306,7 @@ __xa_store(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp) if (ptr == NULL) ptr = NULL_VALUE; retry: - retval = radix_tree_store(&xa->root, index, &ptr); + retval = radix_tree_store(&xa->xa_head, index, &ptr); switch (retval) { case 0: @@ -334,9 +347,9 @@ xa_init_flags(struct xarray *xa, uint32_t flags) { memset(xa, 0, sizeof(*xa)); - mtx_init(&xa->mtx, "lkpi-xarray", NULL, MTX_DEF | MTX_RECURSE); - xa->root.gfp_mask = GFP_NOWAIT; - xa->flags = flags; + mtx_init(&xa->xa_lock, "lkpi-xarray", NULL, MTX_DEF | MTX_RECURSE); + xa->xa_head.gfp_mask = GFP_NOWAIT; + xa->xa_flags = flags; } /* @@ -349,9 +362,19 @@ xa_destroy(struct xarray *xa) struct radix_tree_iter iter; void **ppslot; - radix_tree_for_each_slot(ppslot, &xa->root, &iter, 0) - radix_tree_iter_delete(&xa->root, &iter, ppslot); - mtx_destroy(&xa->mtx); + xa_lock(xa); + radix_tree_for_each_slot(ppslot, &xa->xa_head, &iter, 0) + radix_tree_iter_delete(&xa->xa_head, &iter, ppslot); + xa_unlock(xa); + + /* + * The mutex initialized in `xa_init_flags()` is not destroyed here on + * purpose. The reason is that on Linux, the xarray remains usable + * after a call to `xa_destroy()`. For instance the i915 DRM driver + * relies on that during the initialixation of its GuC. Basically, + * `xa_destroy()` "resets" the structure to zero but doesn't really + * destroy it. + */ } /* @@ -366,7 +389,7 @@ __xa_empty(struct xarray *xa) XA_ASSERT_LOCKED(xa); - return (!radix_tree_iter_find(&xa->root, &iter, &temp)); + return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp)); } bool @@ -403,7 +426,7 @@ __xa_next(struct xarray *xa, unsigned long *pindex, bool not_first) return (NULL); } - found = radix_tree_iter_find(&xa->root, &iter, &ppslot); + found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot); if (likely(found)) { retval = *ppslot; if (retval == NULL_VALUE) |